行业新闻

代码审计入门实战

代码审计入门实战

原创 月亮警察针灸你 合天智汇

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

文章开始前,我们先来了解一下PHP是什么。

0x01 PHP是什么

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

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

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

大家可以通过学习这个实验,掌握PhpStorm环境搭建和Xdebug工具的安装和配置:

v2-759c20db3a20f3b96b8d82e47b61fca8_hd.j

实验地址:

实验:代码审计的前期准备(合天网安实验室)

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

0x02 代码审计又是什么

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

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

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

1、 Seay源代码审计系统

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

v2-7acf192813919ad75e408cdeec929eea_hd.j

实验地址:

实验:代码审计利器-Seay源代码审计系统(合天网安实验室)

2、 RIPS

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

v2-bea7945aa2236dc20d01a4dfaaf1c***_hd.j

实验地址:

实验:代码审计利器-RIPS实践(合天网安实验室)

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

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

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

③ 正向追踪可控变量;

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

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

0x03 PHP函数的代码审计

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

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

一、PHP代码审计之in_array函数

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

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

v2-460c8b4d8bcbaaa85d412f0715ce8eef_hd.j

$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方法,漏洞就存在于这个方法中:

v2-889fb849f5b96e6a3ee0977f17c103a7_hd.j

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

v2-5e782bb3f816fa35777054fe33c10192_hd.j

由于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/1ls;%23;hetianlab.com:80/

v2-acaef1cbb37f5fedb6fc0823d9d1c5fa_hd.j

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

v2-1cfcf7db3b69ca962a5ae901ad03e1ac_hd.j

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

v2-ebaba158056dfc5b4904cb42788da309_hd.j

PHP代码审计之filter_var函数实验的学习地址:

实验:PHP代码审计之filter_var函数(合天网安实验室)

v2-1f170d51fecdda2065cc2d6347dcb13c_hd.j

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

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

v2-4cd169ac575dd17b660c40cc400c6413_hd.j

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

escapeshellcmd函数:

v2-fc423bcbc42142e349f38a07899947e9_hd.j

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

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

v2-7118afc9bbdcbb7980c9594e72effc3c_hd.j

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

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

v2-982091d0a0fbc2634f2c5bfa400426fc_hd.j

学习地址:

实验:PHP代码审计之escapeshellarg与escapeshellcmd函数(合天网安实验室)

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

v2-71cea298af5615dbe2dc6e1020c3d8c8_hd.j

四、PHP代码审计之parse_str函数

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

v2-9d96fd9ba20a3061f96f21e6307160b5_hd.j

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

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

v2-fbc0420c7f9fc7f460475479f0602fe0_hd.j

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

一个简单的例子:

v2-266287e9770e5e4fb1e85885759eed8f_hd.j

直接覆盖了原有的变量$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函数(合天网安实验室)

v2-a52591da746efa51692d5e4311cdef17_hd.j

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

v2-4499bbd9c05e2fcaa8c62459061bbc92_hd.j

五、PHP代码审计之addslashes函数

addslashes函数相关定义:

v2-5bf37a7c3fb8ddbab3386c5bc22e625c_hd.j

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

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

v2-08645b379a95ebf3a2852b9bc13dedac_hd.j

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

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

v2-1a721b3a0ebe2705077c5e47e65b4456_hd.j

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函数一个简单的例子:

v2-9a1ab00d07192aa7aed1c7613440e961_hd.j

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函数(合天网安实验室)

v2-e60ae5f003dd5b0eebb201e7f0eff461_hd.j

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

v2-bf0c1c7c6b38244cb53ad9cddbfc7602_hd.j

0x04 总结

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

实验:PHP常见危险函数(合天网安实验室)

v2-ea03b2772c9484744716cd8512577a32_hd.j

代码审计重在分析、重在坚持。文章看到最后,相信大家对代码审计这个名词不再陌生,学习完上述所有的实验,再上GitHub找几个代码审计的案例源码练练手,差不多就算入门了。安全学习贵在实践、贵在总结,还记得文章一共提到了哪些实验吗,还等什么,点击合天网安实验室-国内大型MOOE在线实验室直接前往合天网安实验室进行学习啊!

声明:笔者初衷用于分享与普及网络知识,若读者因此作出任何危害网络安全行为后果自负,与合天智汇及原作者无关


关闭