总字符数: 10.96K

代码: 4.48K, 文本: 4.01K

预计阅读时间: 37 分钟

命令执行

漏洞信息

漏洞原理

应用有时需要调用一些执行系统命令的函数,如PHP中的systemexecshell_execpassthrupopenproc_popen等,当用户能控制这些函数中的参数时,就可以将恶意系统命令. 拼接到正常命令中,从而造成命令注入攻击,这就是命令注入漏洞.

利用条件

  1. 应用调用执行系统命令的函数
  2. 将用户输入作为系统命令的参数拼接到了命令行中
  3. 没有对用户输入进行过滤或过滤不严

漏洞分类

  1. 命令直接注入执行漏洞

    应用程序直接使用了危险的可执行系统命令的函数,比如php的system、exec函数等,并且这些函数的运行参数是用户可控的,若过滤不严格,就会增大命令执行漏洞的概率.命令本地包含执行漏洞.(注:(CGI)系统命令注入执行漏洞示例,就比如Bash漏洞,就属于这类漏洞,用户可以直接更改HTTP头user-agent的值,就可引发命令注入.)

  2. 命令包含执行漏洞

    命令本地/远程包含漏洞:应用程序直接包含或执行了用户可控的上传脚本文件或远程文件(URL引用文件),就会触发此漏洞.

  3. 命令反序列执行漏洞

    有些动态脚本语言,如php支持实例对象的序列化传输,然后服务端将实例对象反序列化出来并执行解析后实例的构造函数、析构函数或_wakeup()函数,若这些函数利用了用户可控的参数,则会触发命令/代码注入执行漏洞,原理和之前直接注入一样.

  4. 命令动态变量执行漏洞

    有些动态脚本语言,如php,支持变量或函数的动态定义,即运行时可通过参数名来动态组装变量、变量值或函数.若代码中包含有类似代码,就会存在动态变量/函数的执行漏洞.

漏洞危害

继承Web服务程序的权限去执行系统命令或读写文件 反弹shell 控制整个网站甚至控制服务器 进一步内网渗透

  1. 控制受害主机:攻击者可以通过RCE攻击远程控制受害主机,执行任意操作.
  2. 窃取敏感信息:攻击者可以利用RCE攻击窃取受害主机上的敏感信息,如密码、银行账户等.
  3. 破坏数据:攻击者可以利用RCE攻击破坏受害主机上的数据,如删除文件、格式化硬盘等.
  4. 传播恶意代码:攻击者可以利用RCE攻击在受害主机上安装恶意软件,从而进行更多的攻击.
  5. 滥用系统权限:攻击者可以利用RCE攻击获取受害主机上的系统权限,从而进行更多的攻击和滥用.

防范措施

防止远程命令执行漏洞是一项关键任务,可以通过以下方法来实现:

(1)输入数据过滤和验证:应用程序应该对所有输入数据进行充分的过滤和验证,包括但不限于用户输 入、文件上传、cookie等.

(2)使用安全的编程语言和框架:使用安全的编程语言和框架可以帮助开发人员避免常见的安全漏洞.

(3)最小化应用程序权限:应用程序应该以最低的权限运行,这可以减少攻击者获取服务器或系统的控制 权的可能性.

(4)定期更新和修补程序:定期更新和修补程序可以帮助消除安全漏洞,包括远程命令执行漏洞. (5)使用Web应用程序防火墙:Web应用程序防火墙可以帮助检测和阻止恶意流量,包括远程命令执行漏洞攻击

(6)限制外部访问:限制外部访问可以帮助保护Web应用程序免受未经授权的访问和攻击.

(7)日志和监控:记录和监控应用程序的所有操作可以帮助发现安全漏洞和恶意行为,以及对应应急响 应.

(8)加密和认证:使用加密和认证机制可以帮助保护应用程序的数据免受未经授权的访问和窃取.

(9)安全开发实践:开发人员应该遵循安全开发实践,包括但不限于安全代码审查、安全测试、安全培训 等.

常见连接符(管道符)

windows系统支持的连接符
连接符 含义
| 直接执行后面的语句,例如 ping 127.0.0.1:whoami
|| 如果前面执行的语句出错,才执行后面的语句,所以前面的语句必须为假.例如 ping 127.0.0.1 & whoami
& 前面的语句不管是真还是假都执行后面的语句.例如 ping 127.0.0.1 & whoami
&& 如果前面的语句为假则直接出错,也不执行后面的语句,只有前面的语句为真才行. 例如 ping 127.0.0.1 && whoami
Linux系统支持的连接符
连接符 含义
; 执行前面的语句才会执行后面的语句.例如:ping 127.0.0.1;whoami
| 直接执行后面的语句.例如:ping 127.0.0.1|whoami
|| 如果前面执行的语句出错,才执行后面的语句,所以前面的语句必须为假.例如 ping 127.0.0.1 || whoami
& 前面的语句不管是真还是假都执行后面的语句.例如: ping 127.0.0.1 & whoami
&& 如果前面的语句为假则直接出错,也不执行后面的语句,只有前面的语句为真才行.例如: ping 127.0.0.1 && whoami

常见命令执行的函数

php执行系统命令有 6 个函数:

命令 说明
exec() 允许执行一个外部程序(如UNIX Shell/Linux ShellCMD命令等)
system() 允许执行一个外部程序并回显输出,类似于passthru()
passthru() 允许执行一个外部程序并回显输出,类似于exec()
popen() 可通过popen()的参数传递一条命令,并对popen()所打开的文件进行执行
proc_open() 执行一个命令并打开文件指针用于读取以及写入
shell_exec() 通过Shell执行命令,并将执行结果作为字符串返回
system()
1
2
3
4
5
[root@centos7 html]# vim system.php
<?php
$test = "id";
$last = system($test);
?>

执行系统命令,有回显

passthru()
1
2
3
4
<?php
$test = "id";
passthru($test);
?>

执行系统命令并且显示原始输出

shell_exec()
1
shell_exec(string $cmd): string)

通过shell环境执行命令,并且将完整的输出以字符串形式返回(无回显)

exec()
1
2
3
4
5
<?php
$test = $_POST['c']; //id 是 linux 下的用于显示用户的 ID,以及所属群组的 ID
exec($test,$array); //执行命令
print_r($array);
?>

执行一个外部程序, 同时无回显,且输出的时候仅返回命令的最后一行

反引号
1
echo `ls`;

只要在反引号里的字符串都会被当作代码执行,注意如果反引号在单、双引号内则不起作用

漏洞复现

低级

我们对命令注入漏洞进行一个初步利用 让我们先执行一段命令,在输入框输入127.0.0.1,点击submit提交

可以看到成功回显执行结果,说明ping 127.0.0.1命令成功.然后尝试构造payload127.0.0.1|whoami

我们再输入框输入127.0.0.1&whoami,点击submit提交,可以看到不仅ping命令成功执行,并且后面的whoami命令也成功执行.

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
if( isset( $_POST[ 'Submit' ]  ) ) {
// 获取输入的内容
$target = $_REQUEST[ 'ip' ];

// 判断操作系统,执行ping命令
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows 下直接拼接用户输入的内容
// 比如上方我们输入的127.0.0.1|whoami
// 下方的代码就是
// $cmd = shell_exec( 'ping ' . 127.0.0.1|whoami );
// $cmd = shell_exec( 'ping 127.0.0.1|whoami' );
// shell_exec (PHP 4, PHP 5, PHP 7)
// shell_exec - 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回.
// 说明: shell_exec ( string $cmd ) : string
// 参数 cmd:要执行的命令
// 返回值:命令执行的输出. 如果执行过程中发生错误或者进程不产生输出,则返回 NULL.
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}

// 返回命令执行的结果
echo "
<pre>{$cmd}</pre>";
}

中级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 设置危险字符的数组
$substitutions = array(
'&&' => '',
';' => '',
);

/* str_replace ( $search, $replace, $subject ) : 该函数返回一个字符串或者数组.该字符串或数组是将 subject 中全部的 search 都被 replace 替换之后的结果
search 查找的目标值,也就是 needle.一个数组可以指定多个目标.
replace search 的替换值.一个数组可以被用来指定多重替换.
subject 执行替换的数组或者字符串.也就是 haystack.
如果 subject 是一个数组,替换操作将遍历整个 subject,返回值也将是一个数组.
*/
// 将危险字符替换为空
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );

通过源代码分析可以知道并没有过滤|,所以还是可以通过|来绕过

高级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义危险数组
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);

// 将危险字符替换为空
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );

高级就特别有意思了,它类似于CTF故意给你留下了一个bug看你能不能发现,其实我们可以看到|旁边有个空格,所以只要我们的Payload |和两边的命令没有空格就不会被替换

不可能

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
43
44
if( isset( $_POST[ 'Submit' ]  ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

// Get input
$target = $_REQUEST[ 'ip' ];
/*
stripslashes ( string $str ) 反引用一个引用字符串.
return: 返回一个去除转义反斜线后的字符串(\' 转换为 ' 等等).双反斜线(\\)被转换为单个反斜线(\)
*/
$target = stripslashes( $target );

// explode - 使用一个字符串分割另一个字符串
// 以.为分割字符将IP分为四部分
$octet = explode( ".", $target );

// 检查每个八进制是否为整数
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
// 如果4个八进制都是整数,把IP放回去.
$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 {
// 返回IP错误
echo '
<pre>ERROR: You have entered an invalid IP.</pre>';
}
}

// Generate Anti-CSRF token
generateSessionToken();

以上也属于一种措施,但是真正的业务中会产生无法ping域名的情况,可能会对正常的业务造成一定的损失

代码执行

漏洞信息

漏洞原理

远程命令执行漏洞的本质是输入数据未经过充分的验证和过滤,攻击者可以通过输入特定的恶意数据来执行任意代码.这通常是由于应用程序或服务器的代码实现中存在漏洞,使得攻击者可以利用该漏洞来执行任意代码.

攻击者可以利用多种技术来执行远程命令执行漏洞攻击,包括但不限于:

  1. 操作系统命令注入:攻击者通过输入操作系统命令来执行恶意代码,例如Linux系统中的”shell”命令.

  2. SQL注入:攻击者通过输入SQL语句来执行恶意代码,例如向Web应用程序中的数据库查询语句注入代码.

  3. 文件包含:攻击者通过在Web应用程序中包含恶意文件来执行恶意代码,例如通过包含PHP文件来执 行PHP代码.

  4. 反序列化漏洞:攻击者通过利用应用程序中的反序列化功能来执行恶意代码.

  5. 代码注入:攻击者通过向Web应用程序中注入恶意代码来执行代码.

漏洞危害

或者获取敏感数据,控制服务器,甚至发起更加危险的攻击.

  1. 数据泄露:攻击者可以利用RCE攻击来窃取服务器上的敏感数据,如用户账号密码、数据库信息等.
  2. 破坏服务器:攻击者可以利用RCE攻击来破坏服务器的配置,如删除系统文件、更改系统设置等.
  3. 发起DDoS攻击:攻击者可以利用RCE攻击在目标服务器上安装僵尸程序,并将其作为攻击工具发起 DDoS攻击,使服务器无法正常运行.

漏洞防御

为了防止RCE攻击,应该采取以下措施:

  1. 及时更新系统和应用程序,修复已知漏洞.
  2. 使用防火墙和入侵检测系统(IDS)等安全工具,及时检测和防范攻击.
  3. 对输入的数据进行严格过滤和验证,避免输入参数被解析为可执行代码.
  4. 限制应用程序的执行权限,将应用程序运行在最小权限的用户下.
  5. 禁止使用明文密码和弱口令,采用强密码策略.
  6. 启用日志记录,及时发现和排查异常行为.

代码注入攻击与命令注入攻击不同.因为需求设计,后台有时候需要把用户的输入作为代码的一部分进行执行,也就造成了远程代码执行漏洞.不管是使用了代码执行的函数,还是使用了不安全的反序列化等等.

通过代码注入或远程代码执行(RCE),攻击者可以通过注入攻击执行恶意代码、向网站写webshell、控制整个网站甚至服务器.其实际危害性取决于服务器端解释器的限制(例如,PHP,Python等).在某些情况下,攻击者可能能够从代码注入升级为命令注入.

通常,代码注入容易发生在应用程序执行却不经过验证代码的情况下.以下是带有代码注入错误的示例PHP应用程序的源代码.

1
2
3
4
5
6
7
8
9
10
/**
* 从get方法得到代码
*/
$code = $_GET['code'];

/**
* 不安全地执行代码
* 例子 - phpinfo();
*/
eval($code);

根据上面的示例,攻击者可以使用以下结构来执行任意PHP代码.结果是显示出PHP信息页面.

1
http://example.com/?code=phpinfo();

Payload:system('whoami');

命令执行漏洞与代码执行漏洞的区别

命令执行漏洞:直接调用操作系统命令

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

代码执行漏洞:靠执行脚本代码调用操作系统命令

应用有时需要调用一些执行系统命令的函数,如PHP中的systemexecassertshell_execpassthrupopenproc_openescapeshellcmdpcntl_exec等,当用户能控制这些函数中的参数时,就可以将恶意系统命令拼接到正常命令中,从而造成命令执行漏洞,这就是命令执行漏洞.以上函数主要也在webshell中用的多,实际上在正常应用中差别不太大,用得最多的还是前三个.

常用危险函数

PHP

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
# 函数调用注入和命令执行漏洞

# 函数调用注入
eval() // 把字符串作为PHP代码执行
assert() // 检查一个断言是否为 FALSE,可用来执行代码
preg_replace() // 执行一个正则表达式的搜索和替换
call_user_func() // 把第一个参数作为回调函数调用
call_user_func_array() // 调用回调函数,并把一个数组参数作为回调函数的参数
array_map() // 为数组的每个元素应用回调函数

# 动态函数$a($b)
# 由于PHP的特性,PHP的函数支持直接由拼接的方式调用,这直接导致了PHP在安全上的控制有加大了难度.不少知名程序中也用到了动态函数的写法,这种写法跟使用`call_user_func()`的初衷一样,用来更加方便地调用函数,但是一旦过于不严格就会造成代码执行漏洞.

# 举例:不调用`eval()`
<?php
if(isset($_GET['a'])){
$a = $_GET['a'];
$b = $_GET['b'];
$a($b);
} else {
echo "?a=assert&amp;b=phpinfo()";
}

# 命令执行漏洞
system() // 执行外部程序,并且显示输出
exec() // 执行一个外部程序
shell_exec() // 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回
passthru() // 执行外部程序并且显示原始输出
pcntl_exec() // 在当前进程空间执行指定程序
popen() // 打开进程文件指针
proc_open() // 执行一个命令,并且打开用来输入/输出的文件指针

java

1
java.lang.Runtime.getRuntime.exec(command)

在Java中,虽然没有像PHP的eval函数一样直接执行字符串的函数,但是反射机制和表达式引擎等功能依然会带来安全隐患.下面简要介绍一下这些概念:

  1. 反射机制(Reflection): Java的反射机制允许在运行时检查、获取和操作类、接口、字段、方法等类的信息.通过反射,可以动态创建对象、调用方法、获取字段值等,这给了攻击者一定的可操作性.在使用反射时,应该确保对输入进行充分验证和过滤,避免不当操作.
  2. OGNL(Object-Graph Navigation Language): OGNL是一种表达式语言,用于导航和操作对象图.它常被用于在Struts2等框架中进行数据绑定和表达式求值.攻击者可能通过构造恶意的OGNL表达式来执行任意代码,因此在使用OGNL时需要做好输入验证.
  3. SpEL(Spring Expression Language): SpEL是Spring框架中的表达式语言,用于在运行时进行查询和操作对象图.和OGNL类似,攻击者可能通过构造恶意的SpEL表达式来执行任意代码.在使用SpEL时应当谨慎处理用户输入.
  4. MVEL: MVEL是一种类似OGNL和SpEL的表达式语言,也用于在运行时进行动态表达式求值.同样,需要注意防范恶意输入,避免代码执行风险.

python

1
2
3
4
5
exec(string) # Python代码的动态执行
eval(string) # 返回表达式或代码对象的值
execfile(string) # 从一个文件中读取和执行Python脚本
input(string) #Python2.x 中 input() 相等于 eval(raw_input(prompt)) ,用来获取控制台的输入
compile(string) # 将源字符串编译为可执行对象
1
2
3
4
system() #执行系统指令
popen() #popen()方法用于从一个命令打开一个管道
subprocess.call #执行由参数提供的命令
spawn #执行命令