PHP代码审计二(通用代码审计思路)

总结一下在PHP代码审计中常用到的代码审计思路

敏感函数逆向回溯参数过程

根据敏感函数来逆向追踪参数的传递过程,是目前使用的最多的一种方式,因为大多数漏洞是由于函数的使用不当造成的。 另外非函数使用不当的漏洞,如sql注入,也有一些特征,比如select、insert等,再结合from和where等关键字,我们就可以判读是否是一条SQL语句,通过对字符串的识别分析,就能判读这个sql语句里面的参数有没有使用单引号过滤,或者根据我们的经验来判断。就像是HTTP头里面的HTTP_CLIENT_IP和HTTP_X_FORWARDFOR等获取到的IP地址经常没有安全过滤就直接拼接到了SQL语句中,并且由于它们是在$_SERVERE变量中不受gpc的影响,那我们就可以去查找HTTP_CLIENT_IP和HTTP_X_FORWARDFOR关键字来快速寻找漏洞。

这种方式的优点是只需要搜索相关敏感关键字,即可以快速得挖掘想要的漏洞,具有可定向挖掘和高效、高质量的优点。缺点是由于没有通读代码对程序的整体框架了解不够深入,在挖掘漏洞时定位利用点会花费一点时间,另外对逻辑漏洞挖掘覆盖不到。

espcms注入挖掘案例

我们来举一个根据关键字回溯的例子,用PHP程序espcms举例,使用Seay源代码审计系统做演示,首先载人程序,然后点击自动审计,得到一部分可能存在漏洞的代码列表,如下图所示。
在这里插入图片描述
我们挑其中的一条代码,如下图所示:
在这里插入图片描述
双击该项直接定位到这行代码,如下图所示。在选中该变量后,在下方可以看到该变量的传递过程,并且点击下方的变量传递过程也可以直接跳转到该项代码处,可以非常直观地帮助我们看清整个变量在该文件的传递过程。另外我们可以看到$parentid变量是在如下代码段获得的:

$parentid = $this->fun->accept ('parentid', 'R');

在这里插入图片描述
右键选中该代码定位该函数主体,如图所示:
在这里插入图片描述
在这里插入图片描述
可以看到跳转到了class function.php 文件的314行,
在这里插入图片描述
代码如下:

function accept($k, $var = 'R', $htmlcode = true, $rehtml = false) {
		switch ($var) {
			case 'G':
				$var = &$_GET;
				break;
			case 'P':
				$var = &$_POST;
				break;
			case 'C':
				$var = &$_COOKIE;
				break;
			case 'R':
				$var = &$_GET;
				if (empty($var[$k])) {
					$var = &$_POST;
				}
				break;
		}

		$putvalue = isset($var[$k]) ? $this->daddslashes($var[$k], 0):NULL;
		return $htmlcode ? ($rehtml ? $this->preg_htmldecode($putvalue):$this->htmldecode($putvalue)):$putvalue;
	}

可以看到这是一个获取GET、POST、COOKIE 参数值的函数,获取到参数值后,会将k变量进行daddslashes函数过滤, daddslashes会把单引号(’)、双引号(")、反斜线(\)与 NUL(NULL 字符)进行转义。我们传人的变量是parentid和R,则代表在POST、GET中都可以获取parentid参数,最后经过了一个daddslashes()函数,实际上是包装的addslashes()函数,对单引号等字符进行过滤,不过注意看前面的SQL语句是这样的:

$sql = "select * from $db_ table where parentid=$parentid";

并不需要单引号来闭合,于是可以直接注入。
那怎么才能找到web页面parentid接口处的url值呢?在citylist.php文件看到oncitylist()函数在important类中,选中该类名右键点击“全局搜索”功能,如下图所示。
在这里插入图片描述
可以看到index.php文件有实例化过该类,代码如下:

$archive = indexget ('archive', 'R') ;    // indexget函数和上面的accept功能一样
$archive = empty ($archive) ? ' adminuser' : $archive;
$action = indexget('action''R') ;
$action = empty($action) ? ' login' : $action;
include admin_ _ROOT。adminfile 。"/control/Sarchive .php";
$control = new important() ;
$action = 'on'$action; .
if (method_ exists ($control, $action)) {
	$control->$action() ;
} else {
	exit('错误:系统方法错误! ');
}

这里可以看到一个include文件的操作,可惜经过了addslashes() 函数无法进行截断使其包含任意文件,只能包含本地的PHP文件,如果你有MySQL的root权限,能导出文件到tmp目录,不能导出到Web目录,这种场景才用得到这个文件包含,再往下走就是实例化类并且调用函数的操作了,根据代码可以构造出利用EXP:

http://127.0.0.1/espcms/adminsoft/index.php?archive=citylist&action=citylist&parentid= -1 union select 1,2,user() ,4,5

archive 可控,调用citylist文件里的oncitilist方法,oncitilist方法中调用parentid参数。
漏洞截图如图3-6所示。
在这里插入图片描述
这个案例非常真实地演示了如何根据关键字回溯变量来进行代码审计。

通读全文代码

前面提到的根据敏感关键字来回溯传入的参数,是一种逆向追踪的思路,我们也提到了这种方式的优点与缺点,实际上在需要快速寻找漏洞的情况下用回溯参数的方式是非常有效的,当这种方式斌不适合运用在企业中做安全运营时的场景,在企业中做自身产品的代码审计时,我们需要了解整个应用的业务逻辑,才能挖掘到更多有价值的漏洞。

通读全文代码也有一定的技巧,并不是随便找个文件逐个读完就可以了,这样你是很难真正读懂这套web程序的,也很难理解代码的业务逻辑。首先我们要看程序的大体代码结构,如主目录有哪些文件,模块目录有哪些文件,插件目录有哪些文件,处理关注有哪些文件,还要注意文件的大小、创建时间。我们根据这些 文件的命名 就可以大致了解这个程序实现了哪些功能,核心文件是哪些。

下图是discus的程序主目录:
在这里插入图片描述
在看程序目录的时候我们要 特别注意以下几个文件:

(1)函数集文件,通常命名中包含functions或者common等关键字,这些文件里面是一些公共的函数,提供给其他文件统一调用,所以大多数文件都会在文件头部包含到这些函数集文件。寻找这些文件一个非常好用的技巧就是打开index.php或者一些功能性文件,在头部一般都会找得到的。

(2)配置文件,通常命名中包括config关键字,配置文件包括web程序运行必须的 功能性配置选项 以及 数据库等配置信息。从这个文件中可以了解程序的小部分功能。
另外看这个文件的时候注意观察配置文件中参数值是 单引号 还是 双引号 括起来,如果是双引号,则很可能会存在代码执行漏洞
例如下面Kuwebs的代码,只要我们在修改配置的时候利用PHP可变变量的特性即可执行代码

<?php
/*网站基本信息配置*/
$kuWebsiteURL  = "http:/ /www. kuwebs . com" ;
$kuWebsiteSupportEn = "1"
$kuWebsiteSupportSimplifiedOrTraditional = "0";
$kuWebsiteDefauleIndexLanguage = "cn";
$kuWebsiteUploadFileMax = "2";
$kuWebsiteAllowUploadFileFormat = "swf|rar|jpg|zip|gif";

/*邮件设置*/
$kuWebsiteMailType = "1";
$kuWebsiteMailSmtpHost = "smtp.qq.com";

PHP中双引号引起的命令执行漏洞:
在PHP语言中,单引号和双引号都可以表示一个字符串,但是对于双引号来说,可能会对引号内的内容进行二次解释,这就可能会出现安全问题。
举个简单例子
在这里插入图片描述
可以看到这两个输出的结果并不相同。在双引号中倘若有${}出现,那么{}内的内容将被当做代码块来执行。
试想一下,倘若在一个cms的后台,可以修改数据库的配置文件,且配置文件中的值用双引号包括,我们虽然也可以直接闭合代码达到getshell的后果,但是如果cms对传递的参数进行了addlashes()处理的话,我们就无法去闭合代码了,但这时我们可以传入** 命 令 ∗ ∗ 就 可 以 达 到 g e t s h e l l 的 目 的 。 如 : ‘ < ? p h p e c h o " {命令}**就可以达到getshell的目的。如: `<?php echo " getshell<?phpecho"{@assert($_POST[a])}";?> //@是用来防止输出错误信息的`
成功连接
在平时的代码书写中能只用单引号一定不要用双引号,毕竟单引号的解释时间也比双引号少得多,代码运行相对更快。

(3)安全过滤文件,安全过滤文件对我们做代码审计至关重要,关系到我们挖掘到的可疑点能不能利用,通常命名中有filtersafecheck等关键字,这类文件主要是对参数进行过滤,比较常见的是针对sql注入和XSS过滤,还有文件路径、执行的系统命令参数、其他的则相对较少见。目前大多数引用单独对在程序的入口循环对所有参数使用addslshes()函数进行过滤。

private static function_do_query_safe($sq1) {
$sql = str_replace (array('\\\\', '\\\'', '\\"''\'\'), '', $sql);
$mark = $clean = '';
if (strpos($sql, '/') === false && strpos($sq1, '#') === false && 
strpos($sq1, '--') === false && strpos ($sql, '@') === false &&
strpos ($sql, '`') === false) {
$clean = preg_ replace("/'(.+?) '/s", '', $sql) ;
} else {

(4)index文件,index是一个程序的入口文件,所以通常我们只要读一遍index文件就可以大致了解整个程序的架构、运行的流程、包含到的文件,其中核心的文件又有哪些而不同目录的index文件也有不同的实现方式,建议最好先将几个核心目录的index文件都简单的读一遍。

上面介绍我们应该注意的部分文件,可以帮助我们更有方向的去读全部的代码,实际上在我们真正做代码审计的时候经常会遇到各种框架,这时候就会被搞得晕头转向,所以在学习代码审计的前期建议不要去读开源框架或者使用开源框架的应用,先去chinaz、admin5之类的 源码下载网站 读一些小程序,并且一定要多找几套程序通读全文代码,这样我们才能总结经验,等总结了一定的经验,对PHP也比较熟悉了之后再去读那些框架(thinkphp、Yii、Zend Frameword)等开源框架,才能快速的挖掘出高质量的漏洞。

通读代码的好处显而易见,可以更好的了解程序的架构以及业务逻辑,能够挖掘到更多、更高质量的逻辑漏洞,一般老手都会比较喜欢这种方式。而缺点就是花费的时间长,如果程序比较大,读起来也比较累。

根据功能点定向审计

在有了一定的代码审计经验之后,一定会知道 哪些功能点通常会有哪些漏洞,在我们想要快速挖掘漏洞的时候就可以这样来做,首先安装好并且运行程序,到处点点,浏览一下,看一下程序有哪些功能,这些功能的程序文件分别是怎样的,是独立的模块还是以插件的形式存在,或者是写一个通用类里面,然后多处调用。在了解了这些功能的存在形式之后,可以先寻找经常会出现问题的功能点,简单黑盒测试一下,如果没有发现很普通、很常见的漏洞,再去读这个功能的代码,这样我们读起来就可以略过一些刚才黑盒过的测试点,提高审计速度。

根据经验,下面简单的介绍几个功能点经常会出现的漏洞,如下:

(1)文件上传功能:这里说的文件上传功能在很多功能点都会出现,比如像文章的编辑、资料编辑、头像上传、附件上传,这个功能最常见的漏洞就是任意文件上传了,后端程序没有严格限制上传文件的格式、导致可以直接上传或者存在绕过的情况。而除了文件上传功能之外,还经常发生SQL注入漏洞。因为一般程序员都不会注意到对文件名进行过滤,但是有时需要把文件名保存到数据库中,所以就会存在sql注入漏洞。

(2)文件管理功能:在文件管理功能中,如果程序将文件名或者文件路径直接在参数中传递,则很有可能会存在任意文件操作的漏洞,比如任意文件读取、目录遍历等等,利用的方式是在路径中使用…/或者…\跳转目录。

(3)登录认证功能:登录认证功能不是指一个登录过程,而是整个操作过程中的认证,目前的认证方式大多是基于CookieSession,不少程序会把当前登录的用户账号等认证信息放到Cookie中,或许是加密方式,是为了保持用户可以长时间登录,不会一退出浏览器或者Session超时就退出账户,进行操作的时候直接从Cookie中读取出当前用户信息,这里就存在一个算法可信的问题,如果这段Cookie信息没有加salt一类的东西,就可以导致任意用户登录漏洞只要知道用户的部分信息,即可生成认证令牌,甚至有的程序会直接把用户明文放到Cookie中,操作的时候直接取这个用户名的数据,这也是常说的越权漏洞

(4)找回密码功能:找回密码虽然看起来不像任意文件上传这种可以危害到服务器安全的漏洞,但是如果可以重置管理员的密码,也是可以间接控制业务权限甚至拿到服务器权限的。而找回密码功能的漏洞利用场景很多,最常见的是对验证码的爆破,目前特别是APP应用中,请求后端验证码的时候大多是4位,并且如果没有限制验证码错误的次数和有效时间,于是就出现了爆破的漏洞除此之外,还有验证码凭证的算法,这需要在代码中才能看到,所以我们做代码审计的时候可以看看这个算法是否可信。

BugFree重装漏洞案例

针对功能点的审计是相对简单的,不过在使用这种方式审计之前建议先了解整个程序的架构设计和运行流程,程序重装漏洞在早期是比较常见的,我们来看看 BugFree 的程序安装功能,该程序之前被papaver爆出存在多个漏洞,其中就有一个重装漏洞。

BugFree安装文件在 install\index.php,代码如下:

<?php
require_once('func.inc.php') ;
set_time_limit(0) ;
error_reporting(E_ERROR) ;
//基本路径
define('BASEPATH', realpath(dirname(dirname(__FILE__) ) ) ) ;
// upload path 
define('UPLOADPATH', realpath(dirname(dirname(dirname(__FILE__))) ).DIRECTORY_SEPARATOR. 'BugFile') ;
//配置样本文件路径.
define('CONFIG_SAMPLE_FILE', BASEPATH .'/protected/config/main.sample.php');
//配置文件路径
define('CONFIG_FILE', BASEPATH, '/protected/config/main.php');

看这段代码首先包含了’func.inc.php’文件,跟进这个文件可以看到一些读取配置文件、检查目录权限以及服务器变量等功能的函数,下方则是定义配置文件的路径,继续往下走,真正进行程序逻辑流程的地方如下代码所示:

$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : CHECK;
if(is_file("install.lock") && $action != UPGRADED && $action != INSTALLED){
	header ("location: ../index.php");
}

这段代码存在一个逻辑漏洞,首先判断install.lock文件是否存在以及 action 参数值是否升级完成和安装完成,如果是,则跳转到程序根目录首页,这里仅仅使用了

header ("location: ../index.php") ;

并没有使用die()或者exit()等函数退出程序流程,这个跳转只是HTTP头的跳转,下方代码依然会继续执行,这时候如果使用浏览器请求 install/index.php 文件则会跳转到首页,我们用burp试试,效果如下图所示:
在这里插入图片描述
可以看到程序又可以再次安装了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值