攻防世界: mfw
本文知识点
- GitHack工具使用: 在暴露
.git的网站可以使用GitHack来进行源代码的下载(GitHack会通过这个文件恢复原始的文件, 其github地址:https://github.com/lijiejie/GitHack)- assert代码执行 逻辑的利用!
- assert函数用于调试,判断表达式是否为 true,为 false 时抛出 AssertionError。
- 字符串参数危险 :PHP 7.2 前
assert('phpinfo()')会执行代码,在这个案例中将会用到这个执行逻辑。- assert中只能执行一个语句,也就是当你输入分号
;后代码会执行结束,后面的语句不再执行!

初步分析
随意点击网页,发现可能存在文件包含漏洞。如下图

- 随意输入一个文件地址,发现被过滤了,测试了半天,发现
../会直接被拦截

- 尝试使用data伪协议测试,发现文件不存在,如下图:

-
使用php伪协议同样是这样的结果,如下图:

源码下载漏洞
点击about,发现提示使用了git,因此考虑到能不能使用GitHack下载源码。

- 要使用GitHack下载源码,首先需要**确保能够访问其
/.git页面。**如下图,发现确实能够访问。

-
使用GitHack工具,下载源代码
使用命令
python .\GitHack.py http://61.147.171.103:63497/.git/进行下载代码,如下图:
-
查看下载的flag页面的代码,发现什么也没有!!!这里其实我也不理解为什么本地代码什么也没有,但是通过利用assert漏洞能够得到flag,**还希望理解的大佬们指点下!**我已经尝试用二进制文件打开,以及放到linux中使用cat查看,依然没看出什么问题,难道是服务端的cat不一样?

代码分析
重点关注下面的代码,可以看到,我们传入的page参数确实和前面我们测试的现象相符合,../会被过滤。

逻辑梳理
- 首先服务端接收一个
page参数,没有传递的化默认为home - 将传递的
page参数与字符串进行拼接,组成一个完整的文件路径 - 使用
assert判断是否含有..,过滤用户非法输入。这里重点关注!因为aseert中的代码是双引号引用起来的字符串,也就是assert类似eval可以通过传递字符串自行代码!!!
在assert中我们可以控制file参数,因此可以考虑通过控制file参数达到我们想要的执行逻辑。
漏洞利用
前面提到assert只能执行一个语句,包含分号以后就停止执行了,因此可以考虑使用或 语句来执行任意代码,只需要保证前面的strops函数返回false即可。
尝试下面的代码: ','123') or system("ls");//, 可以成功执行ls命令,其中栓斜杠(//)的作用是用于注释后面的代码, 结果如下图所示:

为了得到flag,我们可以查看flag.php的内容, 通过GitHack,我们知道flag.php的文件路径是templates/flag.php,因此我们可以构造下面的代码:
','123') or system("cat templates/flag.php");//。成功拿到flag! 如下图所示:

详细解析
其实对于字符串这块的理解,我一直有点没反应过来, 下面我来分享下自己逐行的代码理解
- 当我们传入的page是
','123') or system("cat templates/flag.php");//时, file的值为templates/','123') or system("cat templates/flag.php");//.php。核心的秘诀就是不用带入最外层的双引号。
php
$file = "templates/" . $page . ".php";
// file = templates/','123') or system("cat templates/flag.php");//.php
-
然后将file拼接进assert中执行的字符串中, 结果如下:
strpos('templates/','123') or system("cat templates/flag.php");//.php', '..') === false
php
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");
也就是最后的代码变为下面的样子,这里我为了表示方便,我将system中的双引号改为了单引号。
php
assert("strpos('templates/','123') or system('cat templates/flag.php');//.php', '..') === false") or die("Detected hacking attempt!");
assert("的引号只是语法标记,不在字符串内容中system("的双引号是字符串的一部分,与assert("不会闭合- 注释
//只在 assert 表达式内生效,不影响外部的or die()!!!
一开始我会想着这个注释//会将后面的die函数注释掉,其实不然,再内存中,字符串是没有双引号包裹的,双引号只是为了我们方便编码和编译器的理解才使用的。也就是说, 在内存中assert函数的第一个参数是strpos('templates/','123') or system("cat templates/flag.php");//.php', '..') === false,他无法影响我们外面的程序(将die也注释掉),他只是一个字符串参数!!!
这也是system("中的双引号没有与assert("闭合形成的原因,因为我们传入的assert中的字符串是一个参数,assert("中的引号不会出现在内存中。
不知道这样说能不能让和我有一样困惑的人理解到。