preface

The implementation of code audit tools is based on the code audit experience developed to optimize the work efficiency of tools, we want to learn code audit must be familiar with the idea of code audit. And code audit is based on PHP language learning, the most basic requirement of learning code audit is to be able to read the code.

There are four common approaches to code audit:

Tracing the parameter transfer process according to sensitive keywords; Find controllable variables and forward trace the transfer process of variables; Look for sensitive function points and read through function point codes; Read the full text code directly. Sensitive function backtracking parameter process is the most widely used method to trace the parameter transfer process according to the sensitive function, because most vulnerabilities are caused by improper use of functions. In addition, non-function misuse vulnerabilities, such as SQL injection, will be discussed in detail later. The advantages and disadvantages of this approach are as follows:

Advantages: Just search the corresponding sensitive keywords, you can quickly dig the desired vulnerabilities, directional mining, efficient and high quality; Disadvantages: Because I did not read through the code, I did not have a deep understanding of the overall architecture of the program, and it would take some time to locate and utilize the vulnerabilities. In addition, I could not cover the logical vulnerabilities. Espcms injection mining cases:

Download url: http://down.chinaz.com/soft/27695.htm

Open the Seay source code audit system, click new project in the upper left corner, select the espCMS folder that you downloaded, click Automatic Audit, start the audit, get the possible vulnerabilities, the path of the vulnerability file, and the list of vulnerability codes.

Let’s pick one of the codes

Double-click to navigate directly to this line of code. After selecting the variable, you can see the passing process of the variable. Click on the parentid function on the left and see the parentid function in the details below. You can see the parenTID variable obtained in the details below.

Right click on this line of code, position function body Accept, right click, and select position function

Class_function.php = class_function.php = class_function.php

This is a function that gets GET, POST, and COOKIE parameters. The variables we pass in are parentid and R, which means we can GET parentid in POST and GET. Finally, we pass a daddslashes() function. It’s actually a wrapped addslashes() function that filters characters such as single quotes. SQL = “select∗fromsql =” select * from SQL = “select∗fromdb_table where parenTID =$parentid”; You don’t need single quotes to close it, you can inject it directly.

In citylist.php, you can see the oncityList () function. In the important class, select the class name, right click, and select Global Search.

$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)) {include admin_root.adminfile. "/control/$archive.php"; $control = new important(); $action = 'on'. $action; if (method_exists($control, $action)) { $control->$action(); } else {exit(' Error: system method error! '); }Copy the code

The addslashes() function does not allow it to include any files, but only local PHP files. EXP is used to instantiate classes and call functions.

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

Read full text code read full text code also has certain skills, otherwise it is difficult to read the Web program, it is also difficult to understand the business logic of the code. First of all, we should look at the general structure of the program, such as what files are in the home directory, what files are in the module directory, what files are in the plug-in directory, and also pay attention to the size of the file, the creation time, you can probably know the program to achieve those functions, what are the core files.

The home directory for Discuz is shown below:

When looking at directory structures, pay special attention to the following files:

  1. Function set file

Function set files are usually named with keywords such as functions or common. These files contain some common functions that can be called by other files uniformly, so most files are included in the header of the file. One trick to finding these files is to open index.php or some functionality file.

  1. The configuration file

The configuration file is usually named with the config keyword. The configuration file contains configuration information such as the functional configuration options and database required for running Web programs. This file gives you an idea of some of the functionality of the program. In addition, when viewing this file, pay attention to whether the parameters in the configuration file are in single or double quotation marks. If the parameters are in double quotation marks, there may be a code execution vulnerability.

  1. Security Filtering file

Security filtering files are very important for code audit. They are usually named with keywords such as filter, safe, and check. These files are mainly used to filter parameters, such as SQL injection and XSS filtering, file paths, and parameters of executed system commands.

  1. The index file

Index is the entry file of a program, so we only need to read the index file to roughly understand the architecture of the entire program, the running process, and the files included.

Knight CMS Read through the audit case. (1) View the application file structure

Start by looking at what files and folders there are, and look for files and folders with keywords like API, admin, manage, and include in their names. You can see that there is an include folder, which is where most of the core files are stored.

In this folder, you can see dozens of files. Weak common.fun.php is the core file of this program, and the basic functions are basically implemented in this file. As soon as you open the file, you’ll see a bunch of filter functions, starting with an SQL injection filter function:

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; }}Copy the code

This function filters incoming variables using the addslashes() function, which filters out single quotes, double quotes, NULL characters, and slashes. Remember that when mining SQL injection vulnerabilities, as long as parameters are concatenated into SQL statements, they will not be injected unless there is wide-byte injection or other special circumstances.

Further down is an XSS filter function mystrip_tags() with the following code: mystrip_tags()

function mystrip_tags($string)
{
$string = new_html_special_chars($string);
$string = remove_xss($string);
return $string;
}

Copy the code

This function calls the new_html_special_chars() and remove_xss() functions to filter XSS as follows:

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

As you can see in the new_html_special_chars() function, this function encodes HTML entities for ampersand, double quotes, and Angle brackets, and does secondary filtering using the strip_tags() function. The remove_xss() function replaces tag keywords, event keywords, and sensitive function keywords.

Further down is the function getip() to getip addresses, which can be forged:

Function getip() {if (getenv(' HTTP_CLIENT_IP ') and strcasecmp(getenv(' HTTP_CLIENT_IP '), 'unknown')) {function getip() {if (getenv(' HTTP_CLIENT_IP '), 'unknown')) { $onlineip = getenv (" HTTP_CLIENT_IP "); }elseif (getenv(' http_x_for ') and strcasecmp(getenv(' http_x_for '), 'unknown'); $onlineip = getenv (" HTTP_X_FORWARDED_FOR "); }elseif (getenv(' REMOTE_ADDR ') and strcasecmp(getenv(' REMOTE_ADDR '), 'unknown')) {$onlineIP =getenv(' REMOTE_ADDR '); }elseif ($_SERVER[' REMOTE_ADDR ']) and $_SERVER[' REMOTE_ADDR '] and $_SERVER[' REMOTE_ADDR ']) { $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"; }Copy the code

Many applications suffer from injection vulnerability due to not verifying the IP format when obtaining the IP, but here you can only forge the IP.

A further area of concern is inserttable(), the SQL query operator, and updatetable(), where most SQL statements are executed, so we need to pay attention to filtering issues in this area.

function inserttable($tablename, $insertsqlarr, $returnid=0, $replace = false, $silent=0) { global $db; $insertKeysql = $comma = ' '; Foreach ($insertsqlarr as $insert_key => $insert_value) {$insertkeysql.= $comma. '. $insertvaluesql. = $comma. '\' \ '. $insert_value. '\' \ '; $comma = ', '; } $method = $replace? "REPLACE" : "INSERT"; Echo $method. "INTO $tablename ($insertKeysQL) VALUES ($insertValuesQL)", $silent? 'SILENT' : ' '; die; $db->query($method. "INTO $tablename ($insertKeysQL) VALUES ($insertValuesQL)", $silent? 'SILENT' : ', '); if($returnid && ! $replace) { return $db->insert_id(); }else { return $state; }}Copy the code

Where (wheresQL ()) = wheresQL ();

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; }Copy the code

There is also an access token-generating function asyn_userkey(), which concatenates the user name, password salt and password to perform an MD5. During access, you only need to add the generated key to the value of GET parameter key to verify whether you have the permission. It is used in authentication processes such as registration and password retrieval, with the following code:

function asyn_userkey($uid) { global $db; $SQL = "select * from". Table (' members')." Where the uid = '". Intval ($uid)." 'LIMIT 1 "; $user=$db->getone($sql); Return the md5 ($user [' username '] $user [' pwd_hash] $user (" password ")); }Copy the code

As mentioned above, the configuration file usually contains the keyword “config”. We just need to search the file name with the keyword:

PHP and cache_config.php are the real config files. Open config.php to see the code:

<? The PHP $dbhost = "localhost"; $dbname = "74 CMS"; $dbuser = "root"; $dbpass = "123456"; $qs_ pre = ""; $QS_cookiedomain = ' '; The $74 CMS QS_cookiepath = "/ /"; $QS_pwdhash = "K0ciF: RkE4xNhu @ S"; Define (' QISHI_CHARSET ', 'gb2312'); Define (' QISHI_DBCHARSET ', 'GBK'); ? >Copy the code

Obviously, there is probably a problem with double quotes parsing code execution that we talked about earlier. Usually this configuration is set during system installation, or there is a setting in the background.

MySQL > connect to mysql.class.php (include\mysql.class.php);

Function connect($dbhost, $dbuser, $DBPW, $dbName = ' ', $dbCharset = 'GBK', $connect=1){$func = empty($connect)? 'the mysql_pconnect' : 'the 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)!" ); }}}Copy the code

There’s a key part of this code, a security breach. Mysql_query (” SET NAMES GBK “); After executing this statement, if the version is greater than 5, execute the following code: Mysql_query (” SET character_set_connection = “. $dbcharset. “character_set_results =”. $dbcharset.” Character_set_client = binary “, $this – > linkid); That is, this line of code will not be executed if MySQL version is less than 5, but “set names GBK” does three things, which is the same as: SET character_set_connection= ‘GBK’, Character_set_results = ‘GBK’, Character_set_client = ‘GBK’ Wide-byte injection exists for almost all database-related operations.

Through a general understanding of the system files, we have a certain understanding of the overall architecture of this program, but it is not enough, we need to read the index.php file to see which files and functions are called when the program is running.

Open the home page file index.php to see the following code:

if(! File_exists (dirname (FILE). '/ data/install the lock')) header (" Location: install/index. PHP "); Define (' IN_QISHI, true); $alias QS_index = ""; Require_once (dirname (FILE). '/ include/common inc. PHP');Copy the code

Check whether the lock file exists. If not, go to install\index.php

Next is the file including \include\common.inc. PHP, followed by the file view

Require_once (QISHI_ROOT_PATH. 'data/config. PHP "); The header (" content-type: text/HTML. Charset = ". QISHI_CHARSET); Require_once (QISHI_ROOT_PATH. 'include/common. Fun. PHP "); Require_once (QISHI_ROOT_PATH. 'include / 74 cms_version. PHP ");Copy the code

PHP contains three files at the beginning of the file, data\config.php for the database configuration file, include\common.fun.php for the base function library file, Include \74cms_version. PHP is the application version file.

Take a look at the following code:

f (! empty($_GET)) { $_GET = addslashes_deep($_GET); } if (! empty($_POST)) { $_POST = addslashes_deep($_POST); } $_COOKIE = addslashes_deep($_COOKIE); $_REQUEST = addslashes_deep($_REQUEST);Copy the code

This code calls addslashes_deep() in the includecommon.fun.php file to filter GET, POST, and COOKIE parameters.

Require_once (qishi_root_path. ‘include/tpl.inc.php’);

Include \tpl.inc. PHP file, follow this file to see:

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. ' '.$_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);Copy the code

If you look at the file include\template_lite\class.template.php, which is a mapper template class, you can see that this code instantiates this class object to the ¥smarty variable.

To follow up, go back to the index.php file code:

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); }Copy the code

Determine if it is cached, and then call the display() function to output the page. Then follow up with the other function entry files as you would with the index.php file to complete the code read through.

Based on the experience, we briefly introduce the vulnerabilities of several function points:

  1. File upload function

Here said the file upload in many function point will appear, such as post editing, data editing, picture upload, upload attachments, this feature is the most common vulnerabilities arbitrary files uploaded, back-end application not strictly limit to upload format, cause you can upload or existence of bypass, and in addition to the file upload function, also often occur SQL injection vulnerabilities.

  1. File management function

In the file management function, if the program file name or file path directly in the parameter transfer, it is likely to have any file operation vulnerability, such as any file read, etc., using the method is to use in the path.. And/or.. \ Jump to directory. In addition to arbitrary file manipulation vulnerabilities, there can also be XSS vulnerabilities, where programs output file names in the page, and often neglect to filter the file names, resulting in file names with special symbols such as Angle brackets stored in the database, which will be executed when the page is displayed.

  1. Login authentication function

The login authentication function is not a process, but the authentication in the whole operation process. Currently, most authentication methods are based on cookies and Session. Many programs will put the authentication information such as the current login user account into cookies, perhaps in encryption mode. If the Cookie information is not added with something like salt, it can cause any user login vulnerability. As long as the user’s dishonest information is known, authentication token can be generated. Some programs even directly put the user name into the Cookie, and directly read the data of the user name during the operation, which is also commonly referred to as the overreach vulnerability.

  1. Password retrieval function

Retrieving the password does not look like any file upload that can harm the security of the server, but if you can reset the administrator’s password, it can also indirectly control the service authority and even get the service authority. There are many scenarios to exploit the vulnerability of password recovery, the most common of which is captcha explosion. At present, especially for APP applications, most of the requests for back-end verification codes are 4 bits, and there is no limit to the number of errors and validity time of verification codes, so there is a burst vulnerability.