漏洞原理

命令执行漏洞原理

命令执行漏洞是指应用有时需要调用一些执行系统命令的函数,如:system()、exec()、shell_exec()、eval()、passthru(),代码未对用户可控参数做过滤,当用户能控制这些函数中的参数时,就可以将恶意系统命令拼接到正常命令中,从而造成命令执行攻击。

在操作系统中,“& 、&& 、| 、 ||”都可以作为命令连接符使用,用户通过浏览器提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令 .

1
2
3
4
5
command1;command2 : 先执行command1再执行command2
command1 & command2 :不管command1执行成功与否,都会执行command2(将上一个命令的输出作为下一个命令的输入)
command1 && command2 :先执行command1执行成功后才会执行command2
command1 | command2 :只执行command2
command1 || command2 :command1执行失败,再执行command2(若command1执行成功,就不再执行command2)

基于dvwa进行学习

low

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

<?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>";
}

?>

由源码可知,并没有对我们输入的东西进行过滤,仅仅是判断了一下是不是Windows NT
提交 “127.0.0.1 | ipconfig” 可以得到目标的所有IP信息.
提交 “127.0.0.1 | whoami” 可以得到目标的用户信息.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
有关函数如下👇
stristr(string,search,before_search)
搜索字符串在另一字符串中的第一次出现,返回字符串的剩余部分(从匹配点),如果未找到所搜索的字符串,则返回 FALSE

该函数是不区分大小写的,如需进行区分大小写的搜索,请使用 strstr() 函数。

php_uname ($mode)
返回运行 PHP 的系统的有关信息,也就是返回运行 PHP 的操作系统的描述
$mode 是单个字符,用于定义要返回什么信息:

'a':此为默认。包含序列 "s n r v m" 里的所有模式。
's':操作系统名称。例如: FreeBSD。
'n':主机名。例如: localhost.example.com。
'r':版本名称,例如: 5.1.2-RELEASE。
'v':版本信息。操作系统之间有很大的不同。
'm':机器类型。例如:i386。

medium

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?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>";
}

?>

不难发现只过滤了&&和;
那我们不用这两个就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//相关函数

str_replace(find,replace,string,count)
以其他字符替换字符串中的一些字符(区分大小写)
find 必需。规定要查找的值
replace 必需。规定替换 find 中的值的值
string 必需。规定被搜索的字符串
count 可选。对替换数进行计数的变量
该函数必须遵循下列规则:
如果替换和替换目标都是数组,且需要替换的元素少于查找到的元素,那么多余元素将用空字符串进行替换
如果替换的目标是数组,而替换的是字符串,那么替代字符串将对所有查找到的值起作用
注释:该函数区分大小写。请使用 str_ireplace() 函数执行不区分大小写的搜索
注释:该函数是二进制安全的

array_keys(array,value,strict)
返回包含数组中所有键名的一个新数组
array 必需。规定数组
value 可选。您可以指定键值,然后只有该键值对应的键名会被返回
strict 可选。与 value 参数一起使用。可能的值:
true - 返回带有指定键值的键名。依赖类型,数字 5 与字符串 “5” 是不同的
false - 默认值。不依赖类型,数字 5 与字符串 “5” 是相同的
如果提供了第二个参数,则只返回键值为该值的键名
如果 strict 参数指定为 true,则 PHP 会使用全等比较 (===) 来严格检查键值的数据类型

high

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?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.0.0.1|| ipconfig
在||后多个空格,会先匹配到’| ‘,这样之后就等价于127.0.0.1|ipconfig

high难度的过滤较为全面但是仍有漏洞

impossible

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php

if( isset( $_POST[ 'Submit' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

// Get input
$target = $_REQUEST[ 'ip' ];
$target = stripslashes( $target );

// Split the IP into 4 octects
$octet = explode( ".", $target );

// Check IF each octet is an integer
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
// If all 4 octets are int's put the IP back together.
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];

// 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>";
}
else {
// Ops. Let the user name theres a mistake
echo '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}

// Generate Anti-CSRF token
generateSessionToken();

?>

相关函数介绍
stripslashes(string)
stripslashes函数会删除字符串string中的反斜杠,返回已剥离反斜杠的字符串。

explode(separator,string,limit)
把字符串打散为数组,返回字符串的数组。参数separator规定在哪里分割字符串,参数string是要分割的字符串,可选参数limit规定所返回的数组元素的数目。

is_numeric(string)
检测string是否为数字或数字字符串,如果是返回TRUE,否则返回FALSE。

Impossible级别的代码对参数ip进行了严格的限制,只有像“数字.数字.数字.数字”的输入才会被接收执行,因此不存在命令注入漏洞。


总结

简单的通过dvwa来学习一下基本的命令执行漏洞,有关绕过的方法以后再学。