代码审计需要掌握的点

  • PHP编程语言的特性和基础
  • Web前端编程基础
  • 漏洞形成原理
  • 代码审计思路
  • 不同系统、中间件之间的特性差异

代码审计思路

  • 方法一 ---- 检查敏感函数的参数,然后回溯变量,判断变量是否可控,并且有没有经过严格的过滤,这是一个逆向追踪的过程
  • 方法二 ---- 找出哪些文件在接收外部传入的参数,然后跟踪变量的传递过程,观察是否有变量传入到高危函数里面,或者传递的过程是否有逻辑漏洞,这是一种正向追踪的方式
  • 方法三 ---- 直接挖掘功能点漏洞,根据自身经验判断该类应用通常在哪些功能中会出现漏洞,直接全篇阅读该功能代码
  • 方法四 ---- 通读全文代码

(1) 方法一

  • 检查敏感函数的参数,然后回溯变量,判断变量是否可控,并且有没有经过严格的过滤,这是一个逆向追踪的过程
  • 说明:由于大多数漏洞都是由于函数的使用不当造成的。而非函数使用不当的漏洞,比如sql注入,也有一些关键字特征:如 select、insert等。再结合fromwhere等关键字,即可以判断其是否是一条sql语句。再有比如说:http头中的HTTP_CLIENT_IPHTTP_X_FORWARDED_FOR字段,有时在没有过滤的情况下就拼接到了sql语句中,并且由于他们是在$_SERVER变量中的,不受GPC的控制。so,我们可以专门去查找http_client_ip 或者 http_x_forwarded_for字段,快速的判断是否有漏洞存在。
  • 优点:只需要搜索关键字,可以快速地挖掘出想要的漏洞,可以定向挖掘漏洞。
  • 缺点:没有通读代码对程序的整体框架不了解,无法挖掘出逻辑漏洞。在挖掘漏洞时定位漏洞利用点会花费一些时间。

下面介绍一个使用方法一挖掘出来的sql注入漏洞(参考了seay大佬的《代码审计》一书)
espcms注入挖掘案例,使用的版本是:ESPCMS V6.6.15.12.09。通过Seay源代码审计系统导入cms的源代码

进行自动审计

选择第26条内容进行审计($parentid变量可能存在sql注入漏洞,且通过列表中的漏洞详细信息来看这是一个数字型注入点),双击这一行可以直接定位到代码所在地。选中$parentid变量后在下方可以看到变量的传递信息

可以看到$parentid通过:$parentid = $this->fun->accept('parentid', 'R'); 这条代码获得。定位accept函数的位置

跳转到了class_function.php函数中

其中的代码如下所示

accept函数的功能是获取通过GETPOST或者COOKIE方法,传递的变量的值。然后通过daddslashes函数(实际上是包装的addslashes函数,功能差不多)对传进来的单引号等值进行转义(我们前面说过这是一个数字型注入点,并不需要闭合单引号,因此这个过滤是没有用的)。再通过htmldecode函数解编码返回传入的数据。这里很明显存在数字型sql注入点。变量的整个传递过程大概是这样的

确定了存在漏洞之后就要确定漏洞的利用点了。在citylist.php文件中可以看到该语句$parentid = $this->fun->accept('parentid', 'R');位于oncitylist函数中,该函数又位于important类中。

选中important类,右键点击全局搜索

可以看到/adminsoft/index.php文件中有实例化该类

打开查看文件的内容

主要的代码如下所示

$archive = indexget('archive', 'R'); //indexget函数 用来获取通过GET、POST和COOKIE传递的名为archive的参数
$archive = empty($archive) ? 'adminuser' : $archive;
$action = indexget('action', 'R'); //indexget函数 用来获取通过GET、POST和COOKIE传递的名为action的参数
$action = empty($action) ? 'login' : $action;include admin_ROOT . adminfile . "/control/$archive.php"; // 由于在获取 $archive变量 时经过了indexdaddslashes的过滤因此无法产生文件包含漏洞。因此我们让其包含citylist.php文件
$control = new important(); // 实例化 important类
$action = 'on' . $action; // 给$action 拼接一个 on ,刚好对应了citylist.php中的important类中的oncitylist函数
if (method_exists($control, $action)) { // 检查实例化后的 $control中是否存在oncitylist函数,肯定是存在的$control->$action();
} else {exit('错误:系统方法错误!');
}

关键点:

  • 需要通过GET或者POST方式传进来三个参数:archive(用于包含citylist.php文件)action(用于检测实例化后的对象是否存在oncitylist函数)parentid(写注入语句),让archiveaction等于citylist。
  • 构造EXPhttp://localhost/espcms/adminsoft/index.php?archive=citylist&action=citylist&parentid=-1 and 1=1 这样去注入即可

(2) 方法二

  • 找出哪些文件在接收外部传入的参数,然后跟踪变量的传递过程,观察是否有变量传入到高危函数里面,或者传递的过程是否有逻辑漏洞,这是一种正向追踪的方式

(3) 方法三

  • 直接挖掘功能点漏洞,根据自身经验判断该类应用通常在哪些功能中会出现漏洞,直接全篇阅读该功能代码
  • 审计方法 ---- 安装好并运行程序,看程序有哪些功能,寻找到实现这些功能的程序文件分别是怎样的。是独立的模块,还是插件,还是写在了一个通用类中。了解了这些功能的存在形式之后,可先找到经常会出现问题的功能点,黑盒测试一下,如果没有发现常见的漏洞,就去读该功能的代码,进行审计。
  • 相关功能点经常会出现的漏洞
    • 文件上传功能:文件上传在很多功能点都会有,如:文件编辑、资料编辑、头像上传、附件上传等。该功能点最常见的漏洞就是任意文件上传漏洞。除了任意文件上传之外还经常发生sql注入漏洞,因为程序员一般都不会注意到对文件名进行过滤,但是文件名有时又会保存到数据库中,所以可以通过文件名实现sql注入攻击。
    • 文件管理功能:在文件管理功能中,如果程序将文件名或者文件路径直接在参数中传递,那就有可能存在任意文件操作漏洞,如任意文件读取,利用../或者..\来跳转路径。还可能存在xss漏洞,程序会在页面中输出文件名,此时如果没有对存入数据库的文件名进行过滤的话就有可能存在xss漏洞。
    • 登录认证功能:程序将当前登录的用户账号的认证信息放到cookie中,当操作需要认证信息时直接从cookie中读取用户信息。如果这段cookie没有加salt一类的东西,就可以导致任意用户登录漏洞,只要知道用户的部分信息,即可生成认证令牌。有的程序甚至会直接把用户名明文放到Cookie中,从而导致越权漏洞
    • 找回密码功能:找回密码功能的漏洞有很多利用场景,常见的是验证码爆破。还有验证凭证的算法,这只有在代码中才能看到,在代码审计时可以看这个算法是否可信。

(4) 方法四

  • 直接通读全文代码
  • 方法:通读全文代码也有一定的技巧,而非抓住一个文件逐句读完就行了。首先我们要看程序的大体代码结构,如:主目录的文件模块目录的文件插件目录的文件。除了看有哪些目录还要看文件的大小创建时间。根据文件名就可以推断出文件实现了哪些功能。
  • 在看程序目录时需要特别注意的几个文件
    • 函数集文件,命名中包含 functions 或者 common等关键字。其中写的是一些公用函数,是给其他文件统一调用的,大部分的文件中都会在开头包含这些文件。寻找这些文件的方法:打开index.php或者一些功能文件,在其头部会包含这些文件。
    • 配置文件,命名中通常包含 config 关键字。从其中可以了解到程序的小部分功能。在看配置文件时要看清楚其中参数的值是使用单引号还是双引号括起来的,如是双引号则可能存在代码执行漏洞
    • 安全过滤文件,命名中有filtersafecheck等关键字。文件的作用是对参数进行过滤。常见的是对:sql注入XSS文件路径执行的系统命令参数的过滤。很多的程序会在参数传入时使用addslashes()进行一次过滤。
    • index文件,他是程序的入口文件,读一遍index文件即可大致了解:程序的架构运行的流程包含的文件核心文件等信息。不同目录的index文件有不同的实现方式,应当将几个核心目录下的index文件都读一遍。

案例:骑士CMS代码审计,版本:3.5.1
第一步:查看应用文件结构
文件目录结构如下所示

首先要看有那些文件和文件夹,寻找名称中带:api,admin,manage,include关键字的文件,这里有admininclude文件夹

查看admin文件夹中的内容,其中有一个api文件夹

再查看include文件夹中的内容,include文件夹很重要,其中一般会保存比较核心的文件,供其他文件包含。
第二步:查看关键文件代码
查看include文件夹下的文件

其中有很多php文件,可以看到有一个名为common.fun.php的文件。前面说过common关键字多用来命名函数集文件,该文件就是一个定义了许多基础函数的文件。


打开文件第一个函数是addslashes_deep()

function addslashes_deep($value)
{if (empty($value)){return $value;}else{if (!get_magic_quotes_gpc()) // 获取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;}
}

相关函数:

  • get_magic_quotes_gpc() ---- 获取当前magic_qoutes_gpc的配置选项设置。本函数自 PHP 7.4.0 起废弃。强烈建议不要使用本函数。返回0表示magic_quotes_gpc关闭,返回1代表开启。
  • array_map() ---- 为数组的每个元素应用回调函数

该函数首先会判断magic_qoutes_gpc是否开启,没有开启的话会使用addslashes注意:在挖掘sql注入漏洞时除非有宽字节注入或者二次注入等特殊情况,只要参数在拼接到sql语句之前调用了addslashes进行了过滤,就不能进行注入了】进行一次过滤。如果传进的值是数组的话会将数组中的每一个元素作用于addslashes_deep函数,实际会调用mystrip_tags函数。


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的。


首先看看new_html_special_chars函数中的内容

function new_html_special_chars($string) {$string = str_replace(array('&amp;', '&quot;', '&lt;', '&gt;'), array('&', '"', '<', '>'), $string);$string = strip_tags($string);return $string;

相关函数:

  • strip_tags() ---- 从字符串中去除 HTML 和 PHP 标记

该函数的作用是对输入字符串中的& " > <进行html实体编码,然后还使用了strip_tags函数进行了二次过滤。


remove_xss函数中的内容如下

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','background','src','poster');$parm3 = Array('alert','sleep','load_file','confirm','prompt','benchmark','select','update','insert','delete','alter','drop','truncate','script','eval');$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 .= '|(&#0([9][10][13]);?)?'; $pattern .= ')?'; }$pattern .= $parm[$i][$j]; }$pattern .= '/i';$string = preg_replace($pattern, '****', $string); }return $string;
}

相关函数:

  • array_merge() ---- 合并一个或多个数组

该函数对标签关键字($parm1)事件关键字($parm2)敏感函数关键字($parm3)进行了过滤


总结:看到这里出现了:addslashes_deep(用addslashes过滤输入)、mystrip_tags(调用new_html_special_chars和remove_xss过滤xss)、new_html_special_chars(过滤xss)、remove_xss(过滤xss)这些函数


在往下审计会看到一个名为getip的函数,该函数是用来获取客户端的真实IP的,函数内容如下

function getip()
{if (getenv('HTTP_CLIENT_IP') and strcasecmp(getenv('HTTP_CLIENT_IP'),'unknown')) {$onlineip=getenv('HTTP_CLIENT_IP');}elseif (getenv('HTTP_X_FORWARDED_FOR') and strcasecmp(getenv('HTTP_X_FORWARDED_FOR'),'unknown')) {$onlineip=getenv('HTTP_X_FORWARDED_FOR');}elseif (getenv('REMOTE_ADDR') and strcasecmp(getenv('REMOTE_ADDR'),'unknown')) {$onlineip=getenv('REMOTE_ADDR');}elseif (isset($_SERVER['REMOTE_ADDR']) and $_SERVER['REMOTE_ADDR'] and strcasecmp($_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';
}

相关函数:

  • getenv() ---- 取得系统的环境变量
  • strcasecmp(string $str1 , string $str2 ) ---- 二进制安全比较字符串(不区分大小写)。如果str1 < str2 返回 < 0,如果str1 > str2 返回 > 0,如果str1 = str2 返回 0

该函数通过请求头中的HTTP_CLIENT_IPHTTP_X_FORWARDED_FORREMOTE_ADDR这些字段和$_SERVER["REMOTE_ADDR"]来获取客户端的IP地址, 并通过strcasecmp函数来判断IP地址是否二进制安全,最后使用正则表达式判断IP地址的格式是否正确。 这里只是可以伪造IP地址, 不能利用IP地址来注入。但是许多应用都在获取IP地址时没有验证IP地址的格式而导致存在注入漏洞, 这一点我们要格外注意。


下面有两个sql查询统一操作函数:inserttable()updateable(),大多数sql语句的执行都会经过这两个函数,inserttable函数的内容如下:

// $tablename --- 表名;$insertsqlarr --- 查询数据
function inserttable($tablename, $insertsqlarr, $returnid=0, $replace = false, $silent=0) {global $db; // 定义一个全局变量$insertkeysql = $insertvaluesql = $comma = ''; // 定义三个变量// 取出$insertsqlarr数组中的元素,键由反引号包裹,值由单引号包裹foreach ($insertsqlarr as $insert_key => $insert_value) {$insertkeysql .= $comma.'`'.$insert_key.'`';$insertvaluesql .= $comma.'\''.$insert_value.'\'';$comma = ', ';}// 判断是执行REPLACE还是INSERT$method = $replace?'REPLACE':'INSERT';// 执行查询语句$state = $db->query($method." INTO $tablename ($insertkeysql) VALUES ($insertvaluesql)", $silent?'SILENT':'');// 如果执行的是insert操作且$returnid为真,就返回插入数据的主键值。否则直接返回$stateif($returnid && !$replace) {return $db->insert_id();}else {return $state;}
}

相关函数:

  • $db->insert_id() ---- 执行数据库插入时的插入ID号。应该是一个自定义函数。

该函数会执行两种sql操作,REPLACE或者INSERT


updateable()函数内容如下

// $tablename --- 表名;$setsqlarr --- 数组
function updatetable($tablename, $setsqlarr, $wheresqlarr, $silent=0) {global $db; // 一个全局变量$setsql = $comma = '';// 得到$setsqlarr中的元素,键用反引号包裹,值用单引号包裹foreach ($setsqlarr as $set_key => $set_value) {if(is_array($set_value)) {$setsql .= $comma.'`'.$set_key.'`'.'=\''.$set_value[0].'\'';} else {$setsql .= $comma.'`'.$set_key.'`'.'=\''.$set_value.'\'';}$comma = ', ';}// 取得$wheresqlarr中的元素,作为sql查询中where关键字后面的内容$where = $comma = '';if(empty($wheresqlarr)) {$where = '1';} elseif(is_array($wheresqlarr)) {foreach ($wheresqlarr as $key => $value) {$where .= $comma.'`'.$key.'`'.'=\''.$value.'\'';$comma = ' AND ';}} else {$where = $wheresqlarr;}// 执行sql语句return $db->query("UPDATE ".($tablename)." SET ".$setsql." WHERE ".$where, $silent?"SILENT":"");
}

该函数会执行UPDATE操作


再往下走会看到一个wheresql函数

function wheresql($wherearr='')
{$wheresql="";if (is_array($wherearr)){$where_set=' WHERE ';// 拼接 $wherearr中的值foreach ($wherearr as $key => $value){$wheresql .=$where_set. $comma.$key.'="'.$value.'"';$comma = ' AND ';$where_set=' ';}}return $wheresql; // 得到 :WHERE a="1" AND b="2"....
}

该函数用于拼接sql查询语句的where条件,其中的参数都用单引号包裹。


下面还有一个生成访问令牌的函数:asyn_userkey函数

function asyn_userkey($uid)
{global $db; // 全局变量$sql = "select * from ".table('members')." where uid = '".intval($uid)."' LIMIT 1";$user=$db->getone($sql); // 执行自定义函数取出一个结果// 返回md5摘要后的结果return md5($user['username'].$user['pwd_hash'].$user['password']);
}

该函数通过将:用户名密码salt密码拼接在一起,取一次md5,作为访问的令牌。访问时只要在GET参数的key值中加上这个令牌即可验证用户的身份和权限。通常该令牌用在注册、找回密码等需要验证用户身份的地方。
我们对该函数进行全局搜索可以看到在很多与用户相关的文件都调用了该函数。

这些函数先不进行审计,先了解程序的其他结构。


第三步:查看配置文件
前面说过配置文件的文件名一般都是以config命名的,在主目录下搜索config

可以搜索出多个文件,根据经验可以判断data目录下的config.phpcache_config.php才是真正的配置文件(PS:这里需要安装好cms之后data目录下才有这两个文件,否则没有这两个文件),查看/data/config.php中的内容,该文件是数据库配置文件

这里的配置文件的参数是使用双引号包裹的,所以很有可能存在前面提到的双引号解析代码执行问题。一般这些配置是在安装系统时设置的,或者后台也有设置的地方。另外还要关注的一点是QISHI_DBCHARSET常量,其值是GBK,说明数据库编码是GBK。因此可能存在宽字节注入,不过需要找到数据库连接时设置的编码。可以找到连接mysql数据库的代码在/include/mysql.class.php文件的connect()函数

函数内容如下

    function connect($dbhost, $dbuser, $dbpw, $dbname = '', $dbcharset = 'gbk', $connect=1){$func = empty($connect) ? 'mysql_pconnect' : 'mysql_connect';// 执行mysql_connect函数连接数据库if(!$this->linkid = @$func($dbhost, $dbuser, $dbpw, true)){$this->dbshow('Can not connect to Mysql!');} else {// 数据库的版本 > 4.1 会执行SET NAMES gbkif($this->dbversion() > '4.1'){mysql_query( "SET NAMES gbk");// 数据库的版本 > 5.0.1if($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到5.0.1之间的话会执行SET NAMES gbk,其就相当于:SET character_set_connection='gbk', character_set_results='gbk', character_set_client='gbk';这就会导致character_set_client没有设置成binary,存在宽字节注入。(PS:关于宽字节注入的防御可以参见 — 宽字节)


第四步:跟读首页文件
通过读取系统文件,已经对程序有了大概的了解。现在需要跟读index.php文件,看看程序运行时会调用那些文件和函数。

// 判断程序是否已经安装,没有安装的话就跳转到install/index.php目录下
if(!file_exists(dirname(__FILE__).'/data/install.lock')) header("Location:install/index.php");
// 定义一个常量
define('IN_QISHI', true);
$alias="QS_index";
// 包含/include/common.inc.php文件,这里我们跟进该文件
require_once(dirname(__FILE__).'/include/common.inc.php');// 判断终端类型
if(browser()=="mobile" && $_GET['iswap']==""){header("location:".$_CFG['wap_domain']);
}if($mypage['caching']>0){$smarty->cache =true;$smarty->cache_lifetime=$mypage['caching'];}else{$smarty->cache = false;}
$cached_id=$_CFG['subsite_id']."|".$alias.(isset($_GET['id'])?"|".(intval($_GET['id'])%100).'|'.intval($_GET['id']):'').(isset($_GET['page'])?"|p".intval($_GET['page'])%100:'');// 判断是否已经缓存,使用display函数输出页面
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);
}
unset($smarty);
?>

/include/common.inc.php中的代码如下所示

if(!defined('IN_QISHI')) exit('Access Denied!');
define('QISHI_ROOT_PATH',dirname(dirname(__FILE__)).'/'); // 根目录
error_reporting(E_ERROR);
ini_set('session.save_handler', 'files');
session_save_path(QISHI_ROOT_PATH.'data/sessions/');
session_start();
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');  // 应用版本文件$QSstarttime=exectime();  // 返回一个时间// 过滤GET、POST、COOKIE
if (!empty($_GET))
{
$_GET  = addslashes_deep($_GET);
}
if (!empty($_POST))
{
$_POST = addslashes_deep($_POST);
}
$_COOKIE   = addslashes_deep($_COOKIE);
$_REQUEST  = addslashes_deep($_REQUEST);date_default_timezone_set("PRC"); // 设置时区
$timestamp = time(); // 返回Unix的时间戳
$online_ip=getip();  // 获取IP
$ip_address=convertip($online_ip); // 对IP进行处理// 包含/data/cache_xxx.php文件
$_NAV=get_cache('nav');
$_PAGE=get_cache('page');
$_CFG=get_cache('config');
$_PLUG=get_cache('plug');// 从cache_config.php文件中读取相关配置文件进行设置
$_CFG['main_domain'] = $_CFG['site_domain'].$_CFG['site_dir'];
$_CFG['wap_domain'] = $_CFG['wap_domain']==""?$_CFG['main_domain']."wap":$_CFG['wap_domain'];
$_CFG['version']=QISHI_VERSION;
$_CFG['web_logo']=$_CFG['web_logo']?$_CFG['web_logo']:'logo.gif';
$_CFG['upfiles_dir']=$_CFG['main_domain']."data/".$_CFG['updir_images']."/";
$_CFG['thumb_dir']=$_CFG['main_domain']."data/".$_CFG['updir_thumb']."/";
$_CFG['resume_photo_dir']=$_CFG['main_domain']."data/".$_CFG['resume_photo_dir']."/";
$_CFG['resume_photo_dir_thumb']=$_CFG['main_domain']."data/".$_CFG['resume_photo_dir_thumb']."/";
$_CFG['teacher_photo_dir']=$_CFG['main_domain']."data/train_teachers/";
$_CFG['teacher_photo_dir_thumb']=$_CFG['main_domain']."data/train_teachers/thumb/";
$_CFG['hunter_photo_dir']=$_CFG['main_domain']."data/hunter/";
$_CFG['hunter_photo_dir_thumb']=$_CFG['main_domain']."data/hunter/thumb/";
$_CFG['subsite_id']=0;
subsiteinfo($_CFG);
$_CFG['site_template']=$_CFG['main_domain'].'templates/'.$_CFG['template_dir'];
$mypage=$_PAGE[$alias];
$mypage['tag']?$page_select=$mypage['tag']:'';// 包含include/tpl.inc.php文件
require_once(QISHI_ROOT_PATH.'include/tpl.inc.php');// 调用实例化后Template_Lite类的相关方法if ($_CFG['isclose']){$smarty->assign('info',$_CFG['close_reason']=$_CFG['close_reason']?$_CFG['close_reason']:'站点暂时关闭...');$smarty->display('warning.htm');exit();}if ($_CFG['filter_ip'] && check_word($_CFG['filter_ip'],$online_ip)){$smarty->assign('info',$_CFG['filter_ip_tips']);$smarty->display('warning.htm');exit();}
?>

相关函数:

  • error_reporting(E_ERROR) ---- 设置应该报告何种 PHP 错误
  • ini_set() ---- 为一个配置选项设置值
  • session_save_path() ---- 读取/设置当前会话的保存路径
  • session_start() ---- 启动新会话或者重用现有会话
  • microtime() ---- 返回当前 Unix 时间戳和微秒数
  • explode() ---- 使用一个字符串分割另一个字符串
  • date_default_timezone_set() ---- 设定用于一个脚本中所有日期时间函数的默认时区
  • time() ---- 返回当前的 Unix 时间戳

该文件主要用来读取参数和配置文件。其中还包含了其他的文件,跟进审计即可。接下来跟进审计其他功能入口文件,即可完成代码通读

【代码审计】--- php代码审计方法相关推荐

  1. P003-PHP代码审计基础-审计方法与步骤-框架与结构

    本节课主要讲解了php审计的方法与大体框架, 首先我们审计代码的时候首先要做的是先看网站的根目录下有什么文件 在进行下一步的判断: 解释: 分析网站:

  2. java代码审计_Java代码审计入门篇

    本期斗哥带来Java代码审计的一些环境和工具准备. Java这个语言相对于PHP来说还是比较复杂的,所以一开始接触到Java源码审计的时候会遇到一些关于环境和配置上一些困难,本文记录斗哥在开始去审计J ...

  3. java代码审计_Java代码审计| Spring框架思路篇

    Java的WEB框架是Java进阶课程,当要进行Spring的漏洞分析,要有一定的Java代码知识储备. Java后端标准的学习路线:JavaSE->JavaEE->Java Web框架 ...

  4. 代码审计--12--源代码审计思路(上)

    一.引子 一个Java应用的代码审计工作应该从哪里入手,对于新手来说也许会不知所措,如果在不了解整个应用系统情况下(包括业务功能.框架使用等)盲目的直接去阅读项目中源代码,往往会迷失在代码中导致漏洞误 ...

  5. 代码审计--13--源代码审计思路(下)

    三.按照业务类型正向审计 前面提到逆向回溯的审计方式针对特征明显的安全漏洞挖掘是非常有效的,但是同样会有很多弊端,通过逆向回溯的方式只能对通用漏洞进行快速审计,不能全面挖掘更有价值的漏洞,如果在时间允 ...

  6. [ 代码审计篇 ] 代码审计案例详解(二) XXE代码审计案例

  7. 代码审计--49--PHP代码审计中常见的漏洞(一)

    经过漫长时间的迭代更新,内容从一开始的碎片化网罗搜集,到排版与整合划分,再到学习与实践验证,最后摒弃初始,用自己的语言与实践结论通俗的将它展现出来.强化知识技能的同时也为读者带来更好的阅读体验. 本篇 ...

  8. Java代码审计审技巧及方法

    java代码审计系统课程-java代码审计入门篇pdf,java代码审计课程-代码审计视频教程-信息安全-CSDN程序员研修院 java代码审计一般是工具+人工的手段进行审计,整个审计主要通过以下三个 ...

  9. PHP代码审计之简单思路方法

    今天继续给大家介绍渗透测试相关知识,本文主要内容是PHP代码审计之简单思路方法. 免责声明: 本文所介绍的内容仅做学习交流使用,严禁利用文中技术进行非法行为,否则造成一切严重后果自负! 再次强调:严禁 ...

  10. PHP代码审计--百家CMS4.1.4项目实战(下)

    PHP代码审计--百家CMS4.1.4项目实战(下) 这是百家cms的第二部分-代码审计 关于百家cms的安装配置及黑盒测试,可以参考上一篇文章: PHP代码审计--百家CMS4.1.4项目实战(上) ...

最新文章

  1. ubuntu下安装lamp环境
  2. [LeetCode] Flatten Binary Tree to Linked List
  3. android n模拟器,BlueStacks推出Android N模拟器
  4. 容器化单页面应用中RESTful API的访问
  5. unity3d学习笔记(一)-在一个GameObject上进行多个AudioSource的控制
  6. 单双目相机畸变校正--极线校正
  7. 这台相机没镜头!美国教授新发明,一块玻璃可成像,拍照给计算机看
  8. jmeter 登录用户并发压力测试案例_用jmeter进行多用户并发压力测试 [转]
  9. (3.13)常用知识-元数据函数
  10. 数据库管理软件SQLPro for SQLite for Mac 2022.30
  11. rand()和srand()GetTickCount函数用法
  12. WIFI状态下SecoClient链接故障
  13. [转]3D Ripper DX模型截取器
  14. 鸿蒙之始有几个老婆,先天五太
  15. 第六届ACM省赛总结--吕云飞
  16. python terminal 库_zhihu-terminal 终端版知乎客户端
  17. 第07课:项目实战——自己动手写一个神经网络模型
  18. delphi xe “[FIREDAC][PHYS][SQLITE] DATABASE IS LOCKED”错误
  19. linux下激活窗口 qt_Qt激活窗口
  20. 用计算机算出爸爸我爱你,爸爸,我爱你。

热门文章

  1. 什么是Redis?为什么要用Redis?
  2. 计算机图形学(三)-图形学中的基本变换(缩放、平移、旋转、剪切、镜像)
  3. 论文笔记-基于代码属性图和Bi-GRU的软件脆弱性检测方法
  4. C中strchr()函数用法
  5. 截屏与截长图功能的实现
  6. weblogic8.1 下载地址
  7. java_home的配置
  8. Goroutines和线程对比
  9. Python工具库安装
  10. IOS7 适配以及向下兼容问题