Preface
This article fully and thoroughly reproduces every vulnerability case mentioned in the book Code Auditing: Enterprise Web Code Security Architecture and provides the source code packages, so that newcomers who start with this book can reproduce the cases themselves and gain a deeper understanding.
Vulnerability Title
Arbitrary User Login in All ESPCMS Versions, p. 65 of the book
Preparations
I will not discuss downloading and installing the source code package, or the pitfalls encountered during installation, because they were all mentioned in the previous article reproducing this cms and will not be repeated here
Getting Started
This vulnerability is located in the /interface/membermain.php file. The interface directory is under the root directory, in the same directory as index.php. Looking at the other php files alongside membermain.php, we find that they all define classes with the same name. From this, we can speculate that index.php in the directory alongside interface may be the entry file, which then instantiates the class files in the inteface directory and invokes the corresponding methods. Since this is speculation, we will verify it later. First, look at the source code of the membermain.php file:

Analyze the in_center() method. Lines 20-21 include a client.php file. Its path is formed by concatenating the admin_ROOT constant with public/uc_client/client.php. We find that there is also a public directory under the application's root directory. We then open that directory and find the client.php file, as shown below:

At this point, we can be basically certain that this is the client.php being included, so the so-called admin_ROOT constant must refer to the path of the parent directory. Next, let us first look at index.php alongside the interface directory, which is index.php under the application's root directory, and see whether the admin_ROOT constant is defined, as shown below:

We can see that the dirname() function and the FILE magic constant are used when defining this constant. By consulting the official manual, we can learn that the FILE magic constant returns the absolute path of the current file, while the dirname() function returns the parent directory of the current file, namely the directory containing the current index.php, which is the application root directory. This matches the client.php file included in membermain.php earlier exactly. Let us look again at index.php and also explain how membermain.php is invoked. The image shows index.php


The source code above is basically identical to the entry file in the previous espcms vulnerability analysis, so I will not say more here. We now basically know how it is invoked. Continue by returning to the /interface/membermain.php file from earlier. You can see that line 23 statically invokes the parent class's start_pagetemplate() method. Examine its source code:

From the name together with the source code, you can tell that it is a file that invokes the template, so it need not concern us. Continue next: line 24 invokes the parent class's member_purview() method. Examine this source code:

We find several class variables defined here. Among them, $ec_member_username, $ec_member_username_id, and the $userinfo variable are the key variables that cause the vulnerability, along with the intval() function. We will stop here for now and return to the membermain.php file. Continue downward; I will not explain it line by line, because these points were covered in the previous reproduction. Line 29 constructs an sql statement. You can see that it involves two tables, one named espcms_member and the other espcms_member_value. The two tables are queried with a left join. Here, considering the table names and table structures, as shown below:

We can speculate that these are member-related tables. When we log in to the website back end, we can see a member option in the navigation bar. Click it, and first add three members, test3, test2, and test1. We then find that three records have been added to the espcms_member table, but there are no records in the espcms_member_value table. We do not need to concern ourselves with the espcms_member_value table here, because a left join query is used, so all data from espcms_member will necessarily be returned, as shown in the two images below:


Then return to line 29 from earlier. We find that an $ec_member_username_id variable is introduced in the where clause. This is precisely the variable defined when line 24 invokes the parent class's member_purview() method. Let us examine the member_purview() method again:

We can see that this variable is received through the accept() method. I will not discuss the accept() method here; the previous article covered it. In other words, this variable is taken directly from the cookie and then concatenated into the sql statement. However, this cookie is encrypted. The issue here is not that it is unencrypted and the cookie can therefore be guessed or anything like that. No, this vulnerability is genuinely very interesting. We registered three members earlier. Let us log in on the front end, as shown below:

As mentioned earlier, you can see that two parameters, ac and at, are passed in the url. They are the controller name and corresponding method name, respectively, and the request is sent to the index.php file we discussed earlier, the index.php from the beginning. We can therefore see that the method that displays a member's personal information after login is the in_center() method in the membermain.php file. We previously mentioned that when the in_center() method queries the user, it concatenates the parent class's $ec_member_username_id. Let us look at how this variable in the parent class is obtained, as shown below:

We can see that two variables, $ecisp_member_username and $ecisp_member_info, are received from the cookie through the accept() method. The two variables are then decrypted and assigned to the $ec_member_username variable and the $user_info variable. (Here, $ec_member_username is assigned as a class property, so it can be inherited by a subclass, whereas $user_info is only an ordinary assignment and can only be used within this function.) Then look at lines 421-422. The list() function here assigns the values in the $user_info variable sequentially to each variable among the function's parameters. The first assignment is to $ec_member_username_id. Here, the assigned $ec_member_username_id variable is still an ordinary assignment. The key point is line 424, where an intval() function is used to convert the $ec_member_username_id variable to an integer and then assign it to the class property $ec_member_username_id. In this way, $ec_member_username_id is also inherited by the subclass. The problem lies in the intval() function. First look at the manual, then search for it. The manual says that it returns the integer value of a variable. More precisely, when a string is passed in, it returns the integer represented by the numeric string before the first nonnumeric character in the string. For example, intval('test3') returns 0, but intval('3test') returns 3. This gives us the exploitation chain. The cookie is encrypted, so we cannot decrypt it, but as stated earlier, two variables are received: one $ecisp_member_username and one $ecisp_member_username_id. They use the same encryption method, so we can register a user named '3test'. We then replace the encrypted value of $ecist_member_username in the cookie at $ecisp_member_username_id. After $ecisp_member_username_id is decrypted and converted by intval(), it becomes the integer 3 and is then concatenated into the sql statement, causing the personal information of the user with id=3 to be queried and returned to the front end. As shown below:





As shown, you can see that I logged in as the 3test user. After modifying the cookie and refreshing the page, the request returns test3, the user with id 3. This is equivalent to being able to access personal information in the database without limit, resulting in arbitrary user login. So at this point, you can see that this is not a simple arbitrary-user-login issue, such as an unencrypted cookie that can be constructed to log in. The exploitation here is genuinely very interesting!