PHP代码审计:代码执行漏洞

原文链接: https://blog.csdn.net/God_XiangYu/article/details/97822204

当你的才华

还撑不起你的野心时

那你就应该静下心来学习


      代码审计学习线上实验,都是CE一边实操,一边整理的笔记,方便以后翻看时,可快速查阅。

       PHP代码审计阶段,基本要理解常见Web漏洞,如Sql注入、XSS、CSRF、文件上传等,近十种安全漏洞的产生原因和初步的防御方法。

目录

内容如下

代码审计简介

一、代码执行漏洞

漏洞简介

代码执行漏洞相关函数

eval() 和 assert() 函数

1. eval()函数

2. assert() 函数

二、preg_replace() 函数

三、call_user_func() 函数

四、动态函数

五、代码执行漏洞防范


       进入正题之前先扫一下盲区,可能有朋友已经会了,可以跳过

内容如下

1、webshell

      WebShell就是以asp、php、jsp或者cgi等网页文件形式存在的一种命令执行环境,也可以将其称做为一种网页后门。一般来说,黑客在入侵了 一个网站后,常常在将这些asp或php木马后门文件放置在网站服务器的web目录中,与正常的网页文件混在一起。然后黑客就可以用web的方式,通过 asp或php木马后门控制网站服务器,包括上传下载文件、查看数据库、执行任意程序命令等。 再通过dos命令或者植入后门木马通过服务器漏洞等达到提权的目的 从而旁注同服务器其他的网站。

      webshell可以穿越服务器防火墙,由于与被控制的服务器或远程过80端口传递的,因此不会被防火墙拦截。并且使用webshell一般不会在系统日志中留下记录,只会在网站的web日志中留下一些数据提交记录,没有经验的管理员是很难看出入侵痕迹的。

2、base64编码

      Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,常用于在HTTP环境下传递较长的标识信息。

      标准的Base64并不适合直接放在URL里传输,因为URL编码器会把标准Base64中的“/”和“+”字符变为形如“%XX”的形式,而这些“%”号在存入数据库时还需要再进行转换,因为ANSI SQL中已将“%”号用作通配符。

      为解决此问题,可采用一种用于URL的改进Base64编码,它不在末尾填充'='号,并将标准Base64中的“+”和“/”分别改成了“_”和 “-”,这样就免去了在URL编解码和数据库存储时所要作的转换,避免了编码信息长度在此过程中的增加,并统一了数据库、表单等处对象标识符的格式。

3、Php中base64函数

base64_encode()函数

     作用:将字符串以 BASE64 编码。

     语法: string base64_encode(string data)。

     返回值: 字符串。

     函数种类: 编码处理。

     内容说明:本函数将字符串以 MIME BASE64 编码。此编码方式可以让中文字或者图片也能在网络上顺利传输。在 BASE64 编码后的字符串只包含英文字母大小写、阿拉伯数字、加号与反斜线,共 64 个基本字符,不包含其它特殊的字符,因而才取名 BASE64。编码后的字符串比原来的字符串长度再加 1/3 左右。更多的 BASE64 编码信息可以参考 RFC2045 文件之 6.8 节。

例如:


  
  
  1. <?php
  2. $str = 'This is an encoded string';
  3. echo base64_encode($str);
  4. ?>

base64_decode()函数

      作用:对使用 MIME base64 编码的数据进行解码。

      语法:string base64_decode ( string data)。

      返回值: 字符串。

      函数种类: 编码处理。

      说明:对base64_encode函数编码过的字符串进行解码。

例如:


  
  
  1. <?php
  2. $str = 'd2VsbGNvbWUlMjB0byUyMGVyYW5nZSUyMQ== ';
  3. echo base64_decode($str);
  4. ?>

4、php中deflate相关函数

      DEFLATE是同时使用了LZ77算法与哈夫曼编码(Huffman Coding)的一个无损数据压缩算法。

gzdeflate()函数

      作用:对数据做DEFLAT编码。

      语法:string gzdeflate ( string data)。

      返回值: 字符串。

     函数种类: 编码处理。

例如:


  
  
  1. <?php
  2. $compressed = gzdeflate( 'Compress me', 9);
  3. echo $compressed;
  4. ?>

Gzinflate()函数

      作用:对数据做DEFLAT编码。

      语法:string gzinflate ( string data)。

      返回值: 字符串。

      函数种类: 编码处理。

例如:


  
  
  1. <?php
  2. $compressed   = gzdeflate( 'Compress me', 9);
  3. $uncompressed = gzinflate($compressed);
  4. echo $uncompressed;
  5. ?>

代码审计简介

       代码审计,是对应用程序源代码进行系统性检查的工作。它的目的是为了找到并且修复应 用程序在开发阶段存在的一些漏洞或者程序逻辑错误,避免程序漏洞被非法利用给企业带来不必要的风险。审计代码的原因是确保代码能安全的做到对信息和资源进行足够的保护,所以熟悉整个应用程序的业务流程对于控制潜在的风险是非常重要的。 代码审计工作者并不是简单的检查代码,更还要明白如下几点:

  • 可以使用类似下面的问题对开发者进行访谈,来收集应用程序信息。
  • 应用程序中包含什么类型的敏感信息,应用程序怎么保护这些信息的?
  • 应用程序是对内提供服务,还是对外?哪些人会使用,他们都是可信用户么? 应用程序部署在哪里?
  • 应用程序的数据安全对于企业的重要性?

       简单来说,代码审计就是检查源代码中的缺点和错误信息,分析并找到这些问题引发的安全漏洞,并提供代码修订措施和建议。而我们则可以通过代码审计,深入理解常见Web漏洞的产生原因和防御方法。

一、代码执行漏洞

       代码执行漏洞就是通过PHP漏洞去执行我们构造的PHP代码,该漏洞是PHP危害最为严重的漏洞之一

漏洞简介

       字符串转化成代码的函数时,没有考虑到用户是否能控制这个字符串(说白了就是未对用户输入进行过滤或过滤不严),用户可以将代码注入到应用中执行才导致代码执行漏洞的产生。

PHP代码执行漏洞和 Sql注入、PHP命令注入漏洞的区别:

  • Sql注入漏洞是将Sql语句注入到后台数据库中进行解析并执行

  • 命令注入漏洞是指注入可以执行的系统命令(如windows中的CMD命令、linux中的Bash命令)并执行

  • PHP代码执行漏洞是将PHP代码注入到Web应用中通过Web容器执行

代码执行漏洞相关函数:

  • PHP:eval() assert() call_user_func_array() preg_replace() call_user_func()等常规函数和动态函数$a($b) (比如$_GET($_POST["xxx"])
  • Python:exec
  • Java:没有类似函数,但采用的反射机制和各种基于反射机制的表达式引擎(OGNL、SpEL、MVEL等)有类似功能

参考简书上代码执行漏洞简介描述

eval() 和 assert() 函数

eval() 函数把字符串按照 PHP 代码来计算

    该字符串必须是合法的 PHP 代码,且必须以分号结尾。

    如果没有在代码字符串中调用 return 语句,则返回 NULL。如果代码中存在解析错误,则 eval() 函数返回 false。

1. eval()函数:


  
  
  1. #菜鸟教程
  2. <?php
  3. $string = "beautiful";
  4. $time = "winter";
  5. $str = "This is a $string $time morning!";
  6. echo $str. "<br />";
  7. eval( "\$str = \"$str\";"); #等同于:$str = "$str";而双引号中的代码会优先执行并替换
  8. echo $str;
  9. ?>

值得注意的是:


  
  
  1. 1. eval函数的参数的字符串末尾一定要有分号,在最后还要另加一个分号(这个分号是php限制)。
  2. 2.注意单引号,双引号和反斜杠的运用。如果参数中带有变量时,并且变量有赋值操作的话,变量前的$符号前一定要有\来转义,如果没有赋值操作可以不需要。

代码如下:


  
  
  1. <?php
  2. # 变量 $id 获取 get 方式传递的变量名为 id 的变量值(值为一个字符串),
  3. # 然后通过eval()函数对$shiyanlou赋值,最后再使用echo输出$shiyanlou 的值
  4. $id = $_GET[ 'id'];
  5. eval( "\$get = $id;");
  6. echo $get;

页面没展示任何内容,我们尝试加上?id=1 试试,发现页面成功输出我们带上的值

再尝试输入localhost/codeaudit/CodeExec/CodeExec1.php?id=xxxxx ,发现xxxx又成功再页面展示,凸艹还犹豫啥,直接想到肯定存在代码执行漏洞,输入phpinfo() 命令,看看能不能显示PHP 当前的配置信息,例如数据库版本等等.....

后台成功解析了phpinfo()函数,并返回了 phpinfo 页面,显然这不是我们希望用户看到的,暴露了很多Web容器相关配置,这将带来许多未知的危险

为什么会返回phpinfo页面呢?


  
  
  1. # <解释>
  2. phpinfo() # 被赋值给$id,之后执行代码: 
  3. eval( "\$get = phpinfo();");
  4. # 实际上相当于执行:
  5. $get = phpinfo();
  6. # 而php代码是从右往左执行,所以phpinfo()函数直接就先执行返回了phpinfo页面,这时后台php代码等同于:
  7. <?php
  8.      $id = $_GET[ 'id'];
  9.       eval( "$id;");
  10. ?>

该漏洞产生的原因,就是未对用户的输入进行有效过滤

2. assert() 函数

说明:

    assert这个函数在php语言中是用来判断一个表达式是否成立,返回true or false。

    如果参数是字符串,它将会被 assert() 当做 PHP 代码来执行。和eval()函数区别在于参数不需要`;`结尾


  
  
  1. eval( " phpinfo()"); <错误>
  2. assert( " phpinfo()"); <正确>

此函数安全隐患和 eval()函数极为类似,代码如下(和eval()函数类似):


  
  
  1. <?php
  2. $id = $_GET[ 'id'];
  3. assert( "\$get = $id"); # assert()函数内部没有`;`
  4. echo $get;
  5. ?>

跟上述evale 访问的标签页一样,进去后加上?id= phpinfo(),后台成功解析命令,并返回PHP 相关配置信息

二、preg_replace() 函数

preg_replace() 函数用于正则表达式的搜索和替换。

    语法:

          mixed preg_replace( mixed pattern, mixed replacement, mixed subject [, int limit ] ) 

         搜索subject中匹配pattern(正则表达式)的部分, 以replacement进行替换。

注: 当pattern 中存在参数e修饰符时,pattern中存在参数e修饰符时,replacement 的值会被当成php代码来执行

代码如下,变量 $id 获取 get 方式传递的变量名为 id 的变量值(值为一个字符串),第一个参数从$id中正则匹配<j><j>之间的字符串并保存,第二个参数取出之前保存的字符串并作为PHP代码执行:


  
  
  1. <?php
  2. $id = $_GET[ 'id'];
  3. preg_replace( "/<j>(.*)<j>/e", '\1',$id);
  4. ?>

通过代码分析,我们就可以构造我们的payload为:?id=<j>phpinfo()<j>时,页面就能成功返回phpinfo页面

三、call_user_func() 函数

函数说明:

      mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] ) 第一个参数 callback 是被调用的函数,其余参数是被调用函数的参数。

      call_user_func() 函数不是指一个函数,而是一类函数: 调用函数。 这一类调用函数经常用在框架中动态调用函数,一般都是较大的程序才会使用到这类函数。 这里我们就拿call_user_func()函数作为例子讲解安全隐患出在哪里

代码如下,变量 $_GET['id'] 获取 get 方式传递的变量名为 id 的变量值作为被调用函数的函数名,而变量 $_GET['data'] 则获取 get 方式传递的变量名为 data 的变量值作为该调用函数的参数:


  
  
  1. <?php
  2. call_user_func($_GET[ 'id'],$_GET[ 'data']);
  3. ?>

经过代码分析,同理,我们构造我们的payload为:id=assert&data=phpinfo()时,页面就能成功返回phpinfo页面

四、动态函数

函数说明:

     变量 $_GET['id'] 获取 get 方式传递的变量名为 id 的变量值作为函数名,而变量 $_GET['data'] 则获取 get 方式传递的变量名为 data 的变量值作为函数的参数

$_GET['id']($_GET['data']);
  
  

     这种方式使用起来确实很方面,但也同时引来了很多安全问题,代码如下:


  
  
  1. <?php
  2. $_GET[ 'id']($_GET[ 'data']);
  3. ?>

        由于PHP语言自身的设计原因,为了方便动态调用函数,引入了动态函数的写法,但也存在安全隐患。

      同理,我们构造我们的payload为:id=assert&data=phpinfo()时,页面就能成功返回phpinfo页面

五、代码执行漏洞防范

通常我们可以使用白名单+正则表达式的方式对用户输入进行过滤。

除此之外这里有几项具体的建议:

  • 使用json保存数组,当读取时就不需要使用eval了

  • 对于必须使用eval的地方,一定严格处理用户数据

  • 字符串使用单引号包括可控代码,插入前使用addslashes转义

  • 放弃使用preg_replace的e修饰符,使用preg_replace_callback()替换

  • 若必须使用preg_replace的e修饰符,则必用单引号包裹正则匹配出的对象

https://www.shiyanlou.com/courses/895


我不需要自由,只想背着她的梦

一步步向前走,她给的永远不重


猜你喜欢

转载自blog.csdn.net/bylfsj/article/details/102731749
今日推荐