代码审计入门实战

       前段时间在整理一个PHP函数代码审计的项目,所以文章也是围绕PHP的代码审计来写,如果有写的不对的地方,还请大佬们指正。文章开始前,我们先来了解一下PHP是什么。

 

0x01 PHP是什么

 

根据百度百科的描述,PHP是一种在服务器端执行的通用开源脚本语言,主要适用于Web开发。

既然是网站编程语言,自然需要一款工具辅助程序员高效编程。PhpStorm就是一款PHP集成开发工具,可以随时帮助程序员对代码进行调整、运行单元测试、且提供可视化debug功能。

当然也可以使用其他工具对PHP程序进行调试,比如Xdebug,一款开放源码的PHP debug工具,用来跟踪、调试和分析PHP程序的运行状况。

大家可以通过学习这个实验,掌握PhpStorm环境搭建和Xdebug工具的安装和配置——代码审计的前期准备

 

 

网传这么一个段子:如何让一个论坛的人吵起来?答:PHP是世界上最好的语言。这个梗出自PHP的官方文档,最早出现在2001年7月的PHP文档中。虽然有PHP是世界上最好的语言这种说法,但是也有一些因为弱类型语言的安全性问题出现,这就需要安全人员通过代码审计来检查PHP程序的安全性。

 

0x02 代码审计又是什么

代码审计,顾名思义就是检查源代码中的安全缺陷。通过自动化工具或人工审查的方式,对程序源代码逐条进行检查和分析,发现源码缺陷引发的安全漏洞,有时还需要提供代码修订措施和修复建议。

软件的源代码、程序缺陷可能导致严重的安全漏洞,要消除代码中的漏洞、减少不必要的补丁升级,就需要进行源代码的安全审计。源代码审计是对代码库和软件架构的安全性、可靠性进行全面的安全检查。人工审查已经成为软件源码设计、开发和应用的最佳保障,因此做好代码审计就是从安全的角度对整个代码质量进行评估。安全人员需要在黑客发现系统漏洞之前,找出应用的安全隐患,并提供相应的安全报告和修复方法,从而提高应用系统的安全性。

除了人工审查的方式,还可以通过一些自动化工具进行代码审计,下面简单介绍两种代码审计利器:

 

1、  Seay源代码审计系统

这是一款针对PHP代码安全审计的系统,基于C#语言开发,主要运行于Windows系统上。这款软件能够发现SQL注入、代码/命令执行、文件包含、文件上传、拒绝服务、XSS、信息泄露、任意URL跳转等漏洞。关于Seay源代码审计系统工具的使用,可以通过下面的实验进行学习——代码审计利器-Seay源代码审计系统

 2、  RIPS

RIPS通过标记和解析所有源代码文件,自动检测PHP应用程序中的漏洞。RIPS能够将PHP源代码转换为程序模型,检测程序流期间用户输入可能污染的敏感接收器,即潜在易受攻击的函数。RIPS工具的使用参考下面的实验——代码审计利器-RIPS实践

 学习了代码审计的常用工具,相信大家会对代码审计的方法和步骤有一定的了解,那么接下来简单总结一下代码审计的流程:

①  通读全文代码:更好地了解程序的架构及业务逻辑,挖掘更多高质量的漏洞;

②  通读敏感功能点:快速挖掘某种漏洞;

③  正向追踪可控变量;

④  敏感关键字回溯参数传递过程。

下面进入文章的重点部分——PHP函数的代码审计,通过具体实例分析PHP部分函数,包括in_array函数、filter_var函数、escapeshellarg与escapeshellcmd函数、parse_str函数及addslashes函数,学习上述函数缺陷引发的漏洞及利用方式,结合CTF练习掌握PHP函数漏洞审计流程。

 

0x03 PHP函数的代码审计

项目来自https://github.com/hongriSec/PHP-Audit-Labs

这里我们根据《PHP函数漏洞审计》课程顺序进行学习。

 

1、PHP代码审计之in_array函数

既然是对in_array函数进行具体分析,我们先来了解一下in_array函数的相关定义:

注:后面说到的函数定义均来自PHP手册,手册地址:http://php.net/manual/zh/index.php

 $needle变量表示待搜索的值,$haystack表示待搜索的数组。in_array函数用法为:在$haystack数组中搜索$needle变量的值,检查值是否存在。如果第三个变量$strict的值为true,则in_array函数会进行强检查,检查$needle的类型是否和$haystack数组中的相同。

in_array函数为什么会存在漏洞呢,原因是程序员在使用in_array函数进行数据处理时,未使用第三个参数进行严格匹配,比如Piwigo软件2.7.1版本就是因为in_array函数的不安全使用,导致SQL注入漏洞发生。

查看源码/picture.php文件可以发现,当$_GET[‘action’]为rate时,会调用文件/include/function_rate.inc.php文件中的rate_picture方法,漏洞就存在于这个方法中:

         而在rate_picture方法中,使用了in_array函数对$rate变量进行检测,判断$rate是否在$conf['rate_items']中:

由于functions_rate.inc.php文件中没有将in_array函数的第三个参数设置为true,所以会进行弱比较,可以将$rate的值设置成1,1 and if(ascii(substr((select database()),1,1))=112,1,sleep(3)));#进行绕过。

那么SQL语句就变成:

INSERT INTO piwigo_rate(user_id,anonymous_id,element_id,rate,date) VALUES (2,'192.168.2',1,1,1 and if(ascii(substr((select database()),1,1))=112,1,sleep(3)));#,NOW())

这样就可以进行盲注了。

直接利用sqlmap进行漏洞利用:

python sqlmap.py -u "http://10.1.1.79/piwigo/picture.php?/1/category/1&action=rate" --data "rate=1" --dbs

 还可以用sqlmap获取目标数据库的表及敏感数据。

总结一下in_array函数的审计流程:

      大家可以学习in_array函数缺陷引发的相关漏洞及其利用方式——PHP代码审计之in_array函数

2、PHP代码审计之filter_var函数

       filter_var函数定义:

$variable变量表示待过滤的变量(变量的值在过滤前,会被转换成字符串),$filter变量表示要应用的过滤器的ID,$options变量代表一个选项的关联数组或按位区分的标识。filter_var函数返回过滤后的数据,过滤失败则返回false。

这里以一个CTF练习介绍filter_var函数缺陷引发的漏洞可以怎么利用,题目考察filter_var函数的绕过与远程命令执行。题目源码为:

程序使用exec函数来执行curl命令,很容易让人联系到命令执行。可以看到用于拼接命令的$site_info变量是从用户传来的url参数,经过filter_var和parse_url两个函数过滤而来。之后又规定当url参数的值以hetianlab.com结尾时,才会执行exec函数。

这就需要绕过filter_var和parse_url函数,并且需要满足$site_info['host']的值以hetianlab.com结尾,然后通过执行系统命令获取flag。

Payload为/ctf/index.php?url=demo://%22;ls;%23;hetianlab.com:80/

      直接用cat f1agi3hEre.php命令过不了filter_var函数检测,因为包含空格,使用如下payload获取flag:/ctf/index.php?url=demo://%22;cat<f1agi3hEre.php;%23;hetianlab.com:80/

       总结一下filter_var函数的审计流程如下入,也可以学习实验——PHP代码审计之filter_var函数

 

3、PHP代码审计之escapeshellarg与escapeshellcmd函数

先看一下两个函数的相关定义,escapeshellarg函数:

$arg变量表示需要被转码的参数,函数返回值为转码之后的字符串。

escapeshellcmd函数:

$command变量代表要转义的命令,函数返回值为转义后的字符串。

以一个由PHP内置函数mail,结合escapeshellarg与escapeshellcmd函数所引发的命令执行漏洞为例,代码如下:

第4行代码的作用是确保只使用有效的电子邮件地址$email,filter_var函数用于使用特定是过滤器过滤一个变量。PHP的mail函数在底层实现中,调用了escapeshellcmd函数,对用户输入的邮箱地址进行检测。即使存在特殊符号,也会被escapeshellcmd函数处理转义,无法达到命令执行的目的。第6行代码的作用是处理$email传入的数据,而escapeshellarg和escapeshellcmd两个函数一起使用,会造成特殊字符逃逸,导致远程代码执行。

       PHPMailer命令执行漏洞(CVE-2016-10033)也是利用escapeshellarg和escapeshellcmd两个函数结合使用,导致了单引号逃逸。具体的漏洞分析和利用过程可以通过这个实验进行学习——PHP代码审计之escapeshellarg与escapeshellcmd函数

 

     总结一下escapeshellarg与escapeshellcmd函数的审计流程:

4、PHP代码审计之parse_str函数

同样先了解parse_str函数的相关定义:

$encoded_string变量代表输入的字符串,如果设置了第二个变量result,变量将会以数组元素的形式存入数组,作为替代。

下面简述parse_str函数缺陷引发的变量覆盖漏洞,代码如下:

        由于第22行parse_str()调用,其行为非常类似于注册全局变量,通过提交类似config[dbhost]=127.0.0.1这样的数据,因此可以控制getUser()中第6到第9行的全局变量$config。如果目标存在登录验证的过程,就可以通过变量覆盖的方法,远程连接我们自己的MySQL服务器,从而绕过登录验证进行下一步攻击。一个简单的例子

直接覆盖了原有的变量$b。

parse_str函数还有一个有意思的CTF练习,首先利用PHP哈希比较缺陷,构造请求参数,使其经过哈希之后,值是以’OE’开头。缺陷就是如果两个不同的密码经过哈希之后,其哈希值都是以'OE'开头,PHP将会认为它们结果都为0。请求后页面会出现‘flag is here’,点击跳转至flag.php。题目真正的考察点在于flag.php存在一个refer判断,判断refer是否存在,如果存在则展示上传页面,否则返回‘you can not see this page’。接下来的部分存在时间竞争问题,需要在写入too slow之前访问之前写入的文件,才能获取flag。

此题的解法是开burp的200线程不断发包,在start attack之前写一个脚本不断请求写入文件的路径,是变量覆盖与竞争条件漏洞的结合利用。

CTF练习地址——PHP代码审计之parse_str函数

      总结一下parse_str函数的审计流程:

 

 

5、PHP代码审计之addslashes函数

addslashes函数相关定义:

$str表示要转义的字符,当我们要往数据库中输入数据时,例如将名字O’reilly插入到数据库中,就需要对其进行转义。

以一个用户登录程序为例,考察通过SQL注入绕过登录验证,代码如下:

第29行通过POST方式传入user和passwd两个参数,通过isValid函数判断是否合法。isValid函数主要功能代码在第10~20行,调用sanitizeInput方法对user和passwd进行相关处理。sanitizeInput方法主要功能代码在第22~25行,针对输入的数据调用addslashes函数进行处理,然后对处理后的内容进行长度判断,长度大于20则只截取前20个字符。

      这道题已经过滤了单引号,正常情况是没有注入了,为什么还能导致注入呢?原因实际上出在第24行substr函数这里,下面简单看一下substr函数的定义:

substr函数的参数说明:string表示输入的字符串(至少有一个字符),如果start为非负数,返回的字符串从string的start位置开始,从0开始计算;如果start为负数,返回的字符串从string结尾处向前数,从第start个字符开始。

注:如果string的长度小于start,将返回false。

length:如果length为正数,返回的字符串将从start处开始,最多包括length个字符(取决于string的长度);如果length为负数,string末尾处的length个字符将会被省略(若start是负数则从字符串尾部算起);如果length为0、false或null,则返回一个空字符串;如果没有提供length,返回的字符串将从start位置开始,直到字符串结尾。

关于substr函数一个简单的例子:

 28.png

substr中的参数0代表从位置为0的字符开始计算,2代表返回的字符串将从0(start)处开始最多包括2(length)个字符。

再回到题目中,我们知道反斜杠可以取消特殊字符的用法,而注入想要通过单引号闭合,必然会引入反斜杠。将官方提供的payload带入题目中,拼接第15~17行代码中的SQL语句:select count(p) from user u where user = ’1234567890123456789\’ AND password = ‘$pass’

这里的SQL语句由于反斜杠的原因,user=’1234567890123456789\’最后这个单引号会失去它的作用,我们让password=or 1=1#,那么最后的SQL语句为:select count(p) from user u where user = ’1234567890123456789\’ AND password = ‘or 1=1#’

此时user值为1234567890123456789\’ AND password =,可以保证带入数据库执行的结果为true,就能顺利地通过验证。因此使用形如user=1234567890123456789’&passwd= or 1=1#的payload即可逃逸出\(反斜杠)注入。

       苹果CMS视频分享程序8.0版本也曾爆出过SQL注入漏洞,程序调用addslashes函数对反斜杠进行转义处理,但是对用户请求的参数又会进行url解码,因此可以使用双url编码绕过addslashes函数,触发漏洞。

具体的漏洞分析过程可以学习——PHP代码审计之addslashes函数

29.png

总结一下addslashes函数的审计流程:

30.png

 

0x04 总结

除了上面提到的,PHP还有很多常见危险函数,也有相关的实验方便大家了解PHP常见的危险函数,以及使用这些函数可能导致的漏洞——PHP常见危险函数

31.png

代码审计重在分析、重在坚持。文章看到最后,相信大家对代码审计这个名词不再陌生,学习完上述所有的实验,再上GitHub找几个代码审计的案例源码练练手,差不多就算入门了。安全学习贵在实践、贵在总结

 

posted @ 2020-03-31 13:29  蚁景网安实验室  阅读(1216)  评论(0编辑  收藏  举报