前言
代码审计工具的实现都是基于代码审计经验研发出来用于优化工作效率的工具,咱们要学好代码审计就必须要熟练代码审计的思路。况且代码审计是基于PHP语言基本上学习的,学习代码审计最基本的需求便是能读懂代码。平常的代码审计思路有以下四种:
按照敏锐关键字回溯参数传递过程;
查询可控变量,正向跟踪变量传递过程;
寻找敏锐功能点,通读功能点代码;
直接通读全文代码。
敏锐函数回溯参数过程 按照敏锐函数来逆向跟踪参数的传递过程,是日前运用的最多的一种方式,由于大都数漏洞是因为函数的运用欠妥导致的。另一非函数运用欠妥的漏洞,如SQL注入,等以后学习再仔细介绍。这种方式的优缺点如下:
优点:只需搜索相应敏锐关键字,就可快速挖掘想要的漏洞,可定向挖掘,有效、高质量;
缺点:因为无通读代码,对程序整体架构认识不足深入,在挖掘漏洞时定位利用会花点时间,另一对规律漏洞挖掘覆盖不到。 espcms注入挖掘案例:
下载网址:http://down.chinaz.com/soft/27695.htm
下载espcms源程序
(ps:有些程序源代码能够在chinaz.com上面下载) 打开seay源代码审计系统,点击左上角新建项目,选取下载的espcms文件夹,点击自动审计,起始审计,得到可能存在漏洞,漏洞文件的路径,和漏洞代码列表。
咱们挑选其中的一条代码
双击直接定位到这行代码,选中该变量后,能够看到变量的传递过程,在左侧点击parentid函数,在下面仔细信息的地区能够看到parentid函数,在下面仔细信息的地区能够看到parentid变量得到。
右键选中这行代码,定位函数主体accept,点击右键,选取定位函数
能够看到转到了class_function.php文件,代码如下:
能够看到这是一个获取GET、POST、COOKIE参数值得函数,咱们传入的变量是parentid和R,则表率在POST、GET中都能够获取parentid参数,最后经过一个daddslashes()函数,实质上是包装的addslashes()函数,对单引号等字符进行过滤。看前面的SQL语句是这般的: $sql = “select * from db_table where parentid=dbtablewhereparentid
=parentid”; 并不需要单引号来闭合,能够直接注入。
在citylist.php文件看到oncitylist()函数在important类中,选中该类名右键点击,选取全局搜索 能够看到index.php文件有实例化该类,代码如下:$archive = indexget(‘archive’, ‘R’);
$archive = empty($archive) ? ‘adminuser’ : $archive;
$action = indexget(‘action’, ‘R’);
$action =empty($action) ? ‘login’ : $action;
$soft_MOD = array(‘admin’, ‘public’, ‘product’, ‘forum’, ‘filemanage’, ‘basebook’, ‘member’, ‘order’, ‘other’, ‘news’, ‘inc’, ‘cache’, ‘bann’, ‘logs’, ‘template’);if (in_array($point, $soft_MOD)) {
includeadmin_ROOT . adminfile . “/control/$archive.php”;
$control =new important();
$action = ‘on’ . $action;
if (method_exists($control, $action)) {
$control->$action();
} else {
exit(‘错误:系统办法错误!’);
}这儿能够看到一个include文件的操作,可惜经过了addslashes()函数没法进行周期使其包括任意文件,只能包括本地的PHP文件,往下是实例化类并且调用函数的操作,按照代码能够构造出利用EXP: http://127.0.0.1/espcms/upload/adminsoft/index.php?archive=citylist&action=citylist&parentid=-1 union select 1,2,user(),4,5
通读全文代码 通读全文代码亦有必定的技巧,否则很难读懂Web程序的,亦很难理解代码的业务规律。首要咱们要看程序的大体结构,如主目录有那些文件,模块目录有那些文件,插件目录有那些文件,另一还要重视文件的体积,创建时间,就能够大概晓得这个程序实现了哪些功能,核心文件有那些。
如discuz的主目录如下图所示:
在看目录结构的时候,尤其重视以下几个文件:
函数集文件 函数集文件一般命名中包括functions或common等关键字,这些文件里面是有些公共的函数,供给给其他文件统一调用,因此大都数文件都会在文件头部包括到其他文件。寻找这些文件的一个技巧便是打开index.php或有些功能性文件。
配置文件 配置文件一般命名中包括config关键字,配置文件包含Web程序运行必须的功能性配置选项以及数据库等配置信息。从这个文件能够认识程序的小部分功能,另一看这个文件的时候重视观察配置文件中参数是用单引号还是双引号,倘若是双引号,则很可能会存在代码执行漏洞。
安全过滤文件 安全过滤文件对咱们做代码审计至关重要,一般命名中有filter、safe、check等关键字,这类文件重点是对参数进行过滤,比较平常的是针对SQL注入和XSS过滤,还有文件路径、执行的系统命令的参数。
index文件 index是一个程序的入口文件,因此咱们只要读一遍index文件就能够大致认识全部程序的架构、运行的流程、包括到的文件。
骑士cms通读审计案例 (1)查看应用文件结构
首要瞧瞧有那些文件和文件夹,寻找名叫作里有无带api、admin、manage、include一类关键字的文件和文件夹。能够看到有一个include文件夹,通常比较核心的文件都会放在这个文件夹中。
(2)查看关键文件代码 在这个文件夹里能够看多多个数十K的文件,弱common.fun.php便是本程序的核心文件,基本函数基本就在这个文件中实现。一打开文件,马上看多一大堆过滤函数,首要是一个SQL注入过滤函数:
function addslashes_deep($value)
{
if (empty($value))
{
return $value;
}
else
{
if(!get_magic_quotes_gpc())
{
$value=is_array($value) ? array_map(‘addslashes_deep’, $value) : mystrip_tags(addslashes($value));
}else{
$value=is_array($value) ? array_map(‘addslashes_deep’, $value) : mystrip_tags($value);
}return $value;
}
}
该函数将传入的变量运用addslashes()函数进行过滤,过滤掉了单引号、双引号、NULL字符以及斜杠,要记住,在挖掘SQL注入漏洞时,只要参数在拼接到SQL语句前,除非有宽字节注入或其他特殊状况,否则使用了这个函数就不可注入了。
再往下是一个XSS过滤的函数mystrip_tags(),代码如下: function mystrip_tags($string)
{
$string = new_html_special_chars($string);
$string = remove_xss($string);return $string;
}
这个函数调用了new_html_special_chars()和remove_xss()函数来过滤XSS,代码如下: function new_html_special_chars($string) {
$string= str_replace(array(‘&’, ‘"’, ‘<’, ‘>’), array(‘&’, ‘“‘, ‘<’, ‘>’), $string);
$string = strip_tags($string);
return $string;
}
function remove_xss($string) {
$string = preg_replace(‘/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S’, ‘’, $string);
$parm1= Array(javascript, union,vbscript, expression, applet, xml, blink, link, script, embed, object, iframe, frame, frameset, ilayer, layer, bgsound, title, base);$parm2= Array(onabort, onactivate, onafterprint, onafterupdate, onbeforeactivate, onbeforecopy, onbeforecut, onbeforedeactivate, onbeforeeditfocus, onbeforepaste, onbeforeprint, onbeforeunload, onbeforeupdate, onblur, onbounce, oncellchange, onchange, onclick, oncontextmenu, oncontrolselect, oncopy, oncut, ondataavailable, ondatasetchanged, ondatasetcomplete, ondblclick, ondeactivate, ondrag, ondragend, ondragenter, ondragleave, ondragover, ondragstart, ondrop, onerror, onerrorupdate, onfilterchange, onfinish, onfocus, onfocusin, onfocusout, onhelp, onkeydown, onkeypress, onkeyup, onlayoutcomplete, onload, onlosecapture, onmousedown, onmouseenter, onmouseleave, onmousemove, onmouseout, onmouseover, onmouseup, onmousewheel, onmove, onmoveend, onmovestart, onpaste, onpropertychange, onreadystatechange, onreset, onresize, onresizeend, onresizestart, onrowenter, onrowexit, onrowsdelete, onrowsinserted, onscroll, onselect, onselectionchange, onselectstart, onstart, onstop, onsubmit, onunload,style,href,action,location,bac公斤round,src,poster);$parm3= Array(alert,sleep,load_file,confirm,prompt,benchmark,select,update,insert,delete,alter,drop,truncate,script,eval,outfile,dumpfile);$parm = array_merge($parm1, $parm2, $parm3);
for ($i = 0; $i < sizeof($parm); $i++) {
$pattern = /;
for ($j = 0; $j < strlen($parm[$i]); $j++) {
if ($j > 0) {
$pattern .= (;
$pattern.= (&#[x|X]0([9][a][b]);?)?;$pattern .= |(�([9][10][13]);?)?;
$pattern .= )?;
}
$pattern .= $parm[$i][$j];
}
$pattern .= /i;
$string = preg_replace($pattern, ****,$string);
}
return $string;
}
在new_html_special_chars()函数中能够看到,这个函数对&符号、双引号以及尖括号进行了html实体编码,并且运用strip_tags()函数进行了二次过滤。而remove_xss()函数则是对有些标签关键字、事件关键字以及敏锐函数关键字进行了替换。
再往下有一个获取IP位置的函数getip(),是能够伪造IP位置的: function getip()
{
if (getenv(‘HTTP_CLIENT_IP’) andstrcasecmp(getenv(‘HTTP_CLIENT_IP’),’unknown’)) {
$onlineip=getenv(‘HTTP_CLIENT_IP’);
}elseif (getenv(‘HTTP_X_FORWARDED_FOR’) andstrcasecmp(getenv(‘HTTP_X_FORWARDED_FOR’),’unknown’)) {
$onlineip=getenv(‘HTTP_X_FORWARDED_FOR’);
}elseif (getenv(‘REMOTE_ADDR’) andstrcasecmp(getenv(‘REMOTE_ADDR’),’unknown’)) {
$onlineip=getenv(‘REMOTE_ADDR’);
}elseif (isset($_SERVER[‘REMOTE_ADDR’]) and $_SERVER[‘REMOTE_ADDR’] andstrcasecmp($_SERVER[‘REMOTE_ADDR’],’unknown’)) {
$onlineip=$_SERVER[‘REMOTE_ADDR’];
}
preg_match(“/\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}/“,$onlineip,$match);
return $onlineip = $match[0] ? $match[0] : ‘unknown’;
}非常多应用都会因为在获取IP时无验证IP格式,而存在注入漏洞,不外这儿只是能够伪造IP。
再往下能够看到一个值得关注的地区,SQL查找统一操作函数inserttable()以及updatetable()函数,大都数SQL语句执行都会经过这儿,因此咱们要关注这个地区是不是还有过滤等问题。 function inserttable($tablename, $insertsqlarr, $returnid=0, $replace = false, $silent=0) {
global$db;
$insertkeysql = $insertvaluesql = $comma = ‘’;foreach ($insertsqlarr as $insert_key => $insert_value) {
$insertkeysql .= $comma.’.$insert_key.‘;
$insertvaluesql .= $comma.’\’’.$insert_value.’\’’;
$comma = ‘, ‘;
}
$method = $replace?’REPLACE’:’INSERT’;// echo $method.” INTO $tablename ($insertkeysql) VALUES ($insertvaluesql)”, $silent?’SILENT’:’’;die;
$state = $db->query($method.” INTO $tablename ($insertkeysql) VALUES ($insertvaluesql)”, $silent?’SILENT’:’’);
if($returnid && !$replace) {return $db->insert_id();
}else {
return $state;
}
}
再往下则是wheresql()函数,是SQL语句查找的Where要求拼接的地区,咱们能够看到参数都运用了单引号进行包裹,代码如下: function wheresql($wherearr=’’)
{
$wheresql=””;
if (is_array($wherearr))
{
$where_set=’ WHERE ‘;
foreach ($wherearr as$key => $value)
{
$wheresql .=$where_set. $comma.$key.’=”‘.$value.’”‘;
$comma = ‘AND ‘;
$where_set=’ ‘;
}
}
return $wheresql;
}
还有一个拜访令牌生成函数asyn_userkey(),拼接用户名、秘码salt以及秘码进行一次md5,拜访的时候只要在GET参数key的值里面加上生成的这个key就可验证是不是有权限,被用在注册、找回秘码等验证过程中,代码如下: function asyn_userkey($uid)
{
global$db;
$sql = “select * from “.table(‘members’).” where uid = ‘“.intval($uid).”‘ LIMIT1”;
$user=$db->getone($sql);
returnmd5($user[‘username’].$user[‘pwd_hash’].$user[‘password’]);
}(3)查看配置文件 上面咱们介绍到配置文件一般带有“config”这般的关键字,咱们只要搜索带有这个关键字的文件名就可:
在搜索结果中咱们能够看到搜索到多个文件,结合经验能够判断config.php以及cache_config.php才是真正的配置文件,打开config.php查看代码: <?php
$dbhost = “localhost”;
$dbname =”74cms”;
$dbuser =”root”;
$dbpass =”123456”;
$pre =”qs_”;
$QS_cookiedomain = ‘’;
$QS_cookiepath = “/74cms/“;
$QS_pwdhash = “K0ciF:RkE4xNhu@S”;
define(‘QISHI_CHARSET’,’gb2312’);
define(‘QISHI_DBCHARSET’,’GBK’);
?>
很显著看到,特别有可能存在咱们之前说过的双引号解析代码执行的问题,一般这个配置是在安装系统的时候设置的,或后台亦有设置的地区。
瞧瞧数据库连接时设置的编码,找到骑士cms连接MySQL的代码在include\mysql.class.php文件的connect()函数,代码如下: function connect($dbhost, $dbuser, $dbpw, $dbname = ‘’, $dbcharset = ‘gbk’, $connect=1){
$func = empty($connect) ? ‘mysql_pconnect’ : ‘mysql_connect’;
if(!$this->linkid = @$func($dbhost, $dbuser, $dbpw,true)){
$this->dbshow(‘Can not connect to Mysql!’);
} else {
if($this->dbversion() > ‘4.1’){
mysql_query( “SET NAMES gbk”);if($this->dbversion() > ‘5.0.1’){
mysql_query(“SET sql_mode = ‘’”,$this->linkid);
mysql_query(“SET character_set_connection=”.$dbcharset.”, character_set_results=”.$dbcharset.”, character_set_client=binary”,$this->linkid);
}
}
}
if($dbname){
if(mysql_select_db($dbname, $this->linkid)===false){
$this->dbshow(“Can’t select MySQL database($dbname)!”);
}
}
}
这段代码有个关键的地区,有安全隐患。
代码首要判断MySQL版本是不是大于4.1,倘若是则执行下面代码:
mysql_query( “SET NAMES gbk”);
执行这个语句之后在判断,倘若版本大于5则执行下面代码: mysql_query(“SET character_set_connection=”.$dbcharset.”, character_set_results=”.$dbcharset.”, character_set_client=binary”, $this->linkid);
亦便是说在MySQL版本少于5的状况下是不会执行这行代码的,
然则执行了”set names gbk”,咱们在之前介绍过”set names gbk”其实干了三件事,等同于: SET character_set_connection=’gbk’, character_set_results=’gbk’, character_set_client=’gbk’因此呢在MySQL版本大于4.1少于5的状况下,基本所有跟数据库相关的操作都存在宽字节注入。
(4)跟读首页文件 经过对系统文件大概的认识,咱们队这套程序的整体架构已然有了必定的认识,然则还不足,需要跟读一下index.php文件,瞧瞧程序运行的时候回调用那些文件和函数。
打开首页文件index.php能够看到如下代码: if(!file_exists(dirname(FILE).’/data/install.lock’))
header(“Location:install/index.php”);
define(‘IN_QISHI’,true);
$alias=”QS_index”;
require_once(dirname(FILE).’/include/common.inc.php’);
首要判断安装锁文件是不是存在,倘若不存在则转到install\index.php
接下来是包括\include\common.inc.php文件,跟进文件查看 require_once(QISHI_ROOT_PATH.’data/config.php’);
header(“Content-Type:text/html;charset=”.QISHI_CHARSET);
require_once(QISHI_ROOT_PATH.’include/common.fun.php’);
require_once(QISHI_ROOT_PATH.’include/74cms_version.php’);
\include\common.inc.php文件在开头包括了三个文件,data\config.php为数据库配置文件,include\common.fun.php文件为基本函数库文件,include\74cms_version.php为应用版本文件。
再看下面的代码: f (!empty($_GET))
{
$_GET = addslashes_deep($_GET);
}if (!empty($_POST))
{
$_POST = addslashes_deep($_POST);
}
$_COOKIE = addslashes_deep($_COOKIE);
$_REQUEST = addslashes_deep($_REQUEST);这段代码调用了include\common.fun.php文件里面的addslashes_deep()函数对GET、POST、COOKIE参数进行了过滤。
再往下能够看到有一个包括文件的操作:
require_once(QISHI_ROOT_PATH.’include/tpl.inc.php’);
包括了include\tpl.inc.php文件,跟进这个文件瞧瞧: include_once(QISHI_ROOT_PATH.’include/template_lite/class.template.php’);$smarty = new Template_Lite;
$smarty -> cache_dir = QISHI_ROOT_PATH.’temp/caches/‘.$_CFG[‘template_dir’];
$smarty -> compile_dir = QISHI_ROOT_PATH.’temp/templates_c/‘.$_CFG[‘template_dir’];
$smarty -> template_dir = QISHI_ROOT_PATH.’templates/‘.$_CFG[‘template_dir’];
$smarty -> reserved_template_varname = “smarty”;
$smarty -> left_delimiter = “”;
$smarty -> force_compile =false;
$smarty -> assign(‘_PLUG’, $_PLUG);
$smarty -> assign(‘QISHI’, $_CFG);
$smarty -> assign(‘page_select’,$page_select);首要看到包括了include\template_lite\class.template.php文件,这是一个映射程序模板的类,继续往下看,能够看到这段代码实例化了这个类对象赋值给¥smarty变量。
继续跟进则回到index.php文件代码: if(!$smarty->is_cached($mypage[‘tpl’],$cached_id))
{
require_once(QISHI_ROOT_PATH.’include/mysql.class.php’);$db = new mysql($dbhost,$dbuser,$dbpass,$dbname);
unset($dbhost,$dbuser,$dbpass,$dbname);
$smarty->display($mypage[‘tpl’],$cached_id);
}else
{
$smarty->display($mypage[‘tpl’],$cached_id);
}
判断是不是已然缓存,而后调用display()函数输出页面。接下来像审计index.php文件同样跟进其他功能入口文件就可完成代码通读。
按照功能点定向审计 按照经验咱们简单介绍几个功能点会显现的漏洞:
文件上传功能 这儿说的文件上传在非常多功能点都会显现,例如像文案编辑、资料编辑、头像上传、附件上传,这个功能最平常的漏洞便是任意文件上传了,后端程序无严格地限制上传的格式,引起能够上传或存在绕过的状况,而除了文件上传功能外,还经常出现SQL注入漏洞。
文件管理功能
在文件管理功能中,如果程序将文件名或文件路径直接在参数中传递,则特别有可能会存在任意文件的操作漏洞,例如任意文件读取等,利用的办法是在路径中运用…/或…\转目录。 除了任意文件操作漏洞外,还可能会存在XSS漏洞,程序会在页面中输出文件名,而一般会疏忽对文件名进行过滤,引起能够在数据库中存入带有尖括号等特殊符号的文件名,最后在页面表示的时候就会被执行。
登录认证功能 登录认证功能不指的是一个过程,而是全部操作过程中的认证,日前的认证方式大大都是基于Cookie和Session,不少程序会把当前登陆的用户账号等认证信息放到Cookie中,或许是加密方式。进行操作的时候直接从Cookie中读取当前用户信息,这儿就存在一个算法可信的问题,倘若这段Cookie信息无加salt一类的东西,就能够引起任意用户登录漏洞,只要晓得用户的不扥信息,就可生成认证令牌,乃至有的程序会直接把用户名放到Cookie中,操作的时候直接读取这个用户名的数据,这亦是常说的越权漏洞。
找回秘码功能 找回秘码虽然看起来不像任意文件上传这种能够害处到服务器安全的漏洞,然则倘若能够重置管理员的秘码,亦是能够间接掌控业务权限乃至拿到服务权限的。找回秘码功能的漏洞有非常多利用场景,最平常的是验证码爆破。日前尤其是APP应用,请求后端验证码的时候大大都是4位,并且无限制验证码的错误次数和有效时间,于是就显现了爆破的漏洞。
|