Back home
中文
H7 / SECURITY RESEARCH NOTES

Espcms Injection Discovery Case

On this page5 sections

Preface

I plan to fully and thoroughly reproduce every vulnerability case mentioned in the book Code Auditing: Enterprise Web Code Security Architecture and provide the source code packages, so that newcomers who start with this book can reproduce the cases themselves and gain a deeper understanding.


Vulnerability Title

Espcms Injection Discovery Case, p. 47 of the book

Preparations

  • Seay source code audit system
  • Phpstorm (another IDE is also fine; I usually use Phpstorm for coding, so I am accustomed to it)
  • Espcms source code package

Getting Started

  • Install espcms. I encountered a pitfall here: after installation, an error stating that the database could not be connected appeared when logging in to the back end. The solution is shown below:

image.png

  • According to the book, using the Seay source code audit system to audit this cms automatically reveals the sql injection location described in the book, as shown below:

image.png

  • Open this cms's source code with Phpstorm and locate the oncitylist() method. Here is a small tip: ctrl+shift+f opens Phpstorm's global search, and searching directly for oncitylist() locates it

  • Next, analyze the oncitylist() method. The code is shown below:

image.png

You can see that the first four lines of this method involve two variables, $parentid and $verid. Both are received through the accept() method and then assigned. Line 5 defines the name of the database table to query. The ctrl+b shortcut in Phpstorm can be used to view the source code of an element. Here, we can determine that the db_prefix constant is 'espcms', and after concatenation, we can see that the database table to query is espcms_city. In line 6, you can see that the $parentid variable is passed in when defining the sql statement, namely the variable received through the accept() method earlier. Then line 7 executes the sql statement and assigns the returned result set to the $rs variable. Put simply, line 8 onward prints the results retrieved from the database.

  • At this point, notice that parentid is an externally supplied parameter concatenated into the sql statement, so whether it is properly filtered is the key to preventing sql injection. Here, the accept method is used to receive the parentid parameter, so now examine the source code of the accept method

image.png

Analyzing the accept method, we find that when the second passed parameter is R, it means that either the $_GET array or the $_POST array is referenced and assigned to the $var variable. The purpose is to obtain from the $_GET array or $_POST array the value of the first parameter passed to the accept method. If the value is obtained, it is passed into the daddslashes function and the returned value is assigned to the $putvalue variable; otherwise, NULL is assigned to $putvalue Next, examine the source code of the $daddslashes function

image.png

Analyzing this function, we can see that the get_magic_quotes_gpc() function here determines whether the magic_quotes_gpc option is enabled, returning true if it is enabled and false otherwise. If magic_quotes_gpc is enabled, single quotation marks, double quotation marks, backslashes, and null values in $_GET,$_POST,$_COOKIE are escaped with backslashes. The business logic formed by the passed parameters together with this function is this: if you have not enabled the magic_quotes_gpc option, I use the addslashes() function to escape single quotation marks, double quotation marks, backslashes, and null for you. Having analyzed this function, we return to the accept function. Because the accept method's default parameter $htmlcode is true and $rehtml is false, the $putvalue escaped on the previous line is passed into the htmldecode method. Let us examine the source code of htmldecode:

image.png

Analyzing this function, the trim() function removes whitespace characters from the beginning and end of a string, while the htmlspecialchars() function converts special characters into HTML entity characters, such as greater-than and less-than signs and double quotation marks. The preg_replace() function replaces functions such as script here, so this htmldecode function is equivalent to an xss filter. Next, we return to the accept() method. To summarize, the purpose of the accept() method here is to filter the received parameters for sql injection and xss. Then return to the oncitylist() function. Here, the ctrl + e shortcut switches among recently used files. We return to the citylist.php file and carefully examine the sql statement being executed

image.png

We find that the $parentid variable passed into the sql statement is not enclosed in single or double quotation marks, which means that a numeric sql injection may exist, making the earlier filtering of single and double quotation marks and so on useless. Next, we analyze how to construct the corresponding exp (short for exploit), which means exploitation in Chinese. Here, we simply call it exp, haha. First, we know that the problematic method is named oncitylist(), a method of the important class. Then we see that the parent directory of the citylist.php file is named control, and examine the other files under the control directory. They all define an important class, so anyone who knows a little development can tell that this is the controller in the MVC design pattern. That makes things easier. First, locate the program entry file, index.php. This file is alongside the control directory, and both belong to the adminsoft directory. The source code of index.php is shown below, captured in sections:

image.png

You can see that the file begins by defining many constants. We can see that the adminfile constant is defined as adminsoft. Next, look at the definition of the admin_ROOT constant. This involves FILE, which PHP calls a magic constant and which refers to the absolute path and filename of the current file. The strrpos() function calculates the position of the last occurrence of a specified string in the target string, returning an int value. Here, it means returning the position of the adminfile constant (that is, adminsoft) in the absolute path of this index.php file. Next, look at the substr() function. It returns a substring of a string, equivalent to extracting a string of a certain length from a specified position. Taken together, this line of code therefore returns the absolute path up to the adminsoft directory path, equivalent to the absolute path /upload/adminsoft. Next, look at the code on lines 31 and 34, involving the indexget() function. This function appears on line 57, as follows:

image.png

You can see that it is basically identical to the accept() function, including the indexdaddslashes() function being identical to the accept() function's daddslashes() function. There is no need to say more here. Next, continue downward. The source code is shown below:

image.png

Let us first analyze the code. Lines 37 through 44 mean that if the $archive variable matches any item in the array, the file corresponding to the $archive variable is included. This file is under the control directory within the adminsoft directory, the C controller in MVC. Therefore, we can see that $archive is the controller being passed. If the file does not exist, an access error is reported and the program exits. The important class is then instantiated, namely the important class in the citylist.php file we saw earlier. The code on lines 47 through 49 invokes the corresponding $action method, so we know that both $archive and $action can be supplied by the user. We can then construct the corresponding exp as follows: localhost/espcms/upload/adminsoft/index.php?archive=citylist&action=citylist&parentid=-1 union select 1,2,databases(),4,5 The screenshot is shown below:

image.png