Home >Backend Development >PHP Tutorial >PHP cast type and the dangers of remote management plug-ins_PHP tutorial
Type coercion in PHP is very similar to that in C: the variable to be converted is preceded by the target type enclosed in parentheses.
Allowed casts are:
(int),(integer) - Convert to integer
(bool),(boolean) - Convert to Boolean type
(float),(double),(real) - Convert to floating point type
(string) - Convert to string
(array) - Convert to array
(object) - Convert to object
Note that spaces and tabs are allowed within brackets
You can also use settype (mixed var, string type) for forced conversion.
Remote management plug-ins are very popular tools for WordPress site administrators. They allow users to perform the same operation on multiple sites at the same time, such as updating to the latest release or installing plug-ins. However, in order to implement these operations, the client plug-in needs to grant significant permissions to the remote user. Therefore, it is important to ensure that the communication between the management server and the client plug-in is secure and cannot be forged by an attacker. This article will look at several plug-ins available, whose weaknesses could allow an attacker to completely compromise the site they are running on.
ManageWP, InfiniteWP, and CMS Commander
These three services have the same basic client plug-in code (visually it was originally implemented by ManageWp, and then the other two adjusted it), so they all have signature bypass vulnerabilities and can lead to remote code execution.
The management server registers the private key of a client plug-in to calculate the message authentication code for each message, instead of requiring the user to provide administrator credentials [MAC, which we usually see as the MAC address of the hardware, Here is the Message Authentication Code]. A message digest is generated when a message passes through a message digest algorithm using a shared secret key. The MAC is then attached to the message and sent out. After receiving it, the receiver uses the shared secret key to calculate the received message, generate MAC2, and then compare it with MAC1. Message digest is used to verify the authenticity and integrity of the message [students who have studied cryptography should know]. It is a good way to ensure communication security. However, the implementation flaws of the client plug-ins of these three services have led to serious loopholes.
An incoming message authenticated by helper.class.php looks like this:
// $signature is the MAC sent with the message // $data is part of the message if (md5($data . $this->get_random_signature()) == $signature) { // valid message }
Using non-strict equals means that type "spoofing" [type conversion] occurs before comparison. The output of the md5() function is always a string, but if $signature changes to an integer, the type conversion that occurs during comparison can easily forge a matching MAC. For example, if the real MAC starts with "0" or a non-numeric character, then 0 can be matched. If it is "1xxx", then the integer 1 can be matched, and so on. [This is actually a feature of PHP, and of course other languages also have it. When a string and a number are compared for non-strict equality, if the first character is a number, it will be converted into the corresponding integer for comparison. If If it is a character other than 0-9, it will be treated as 0. Note from php.net: If you compare a number and a string or compare a string involving numeric content, the string will be converted to a numerical value and the comparison will be based on the numerical value. conduct].
Convert string to numeric value:
When a string is treated as a numerical value, the result and type are as follows:
If the string does not contain '.', 'e' or 'E' and its numeric value is within the range of integers (defined by PHP_INT_MAX), the string will be treated as an integer. In all other cases the value is treated as float.
The beginning of the string determines its value. If the string begins with a legal numeric value, that numeric value is used. Otherwise its value is 0 (zero). Legal values consist of an optional sign, followed by one or more digits (possibly with a decimal point), and then an optional exponent part. The exponent part consists of 'e' or 'E' followed by one or more digits.
// $signature is the MAC sent with the message
// $data is part of the message
if (md5($data . $this->get_random_signature()) == $signature) {
// valid message }
Unfortunately, the attacker can provide an integer as a signature. In init.php, the incoming request will be decoded using base64_decode(), and then the result will be deserialized. The use of Unserialize() means that the type of input data can be controlled. A fake serialization message is as follows:
a:4:{s:9:"signature";i:0;s:2:"id";i:100000;s:6:"action";s:16:"execute_php_code";s: 6:"params";a:2:{s:8:"username";s:5:"admin";s:4:"code";s:25:"exec('touch /tmp/owned') ;";}}
This message uses the integer 0 as a signature, and then uses the execute_php_code provided by the plug-in to execute arbitrary PHP code.
$signature = 0; // $data is the action concatenated with the message ID $data = 'execute_php_code' . 100000; if (md5($data . $this->get_random_signature()) == $signature) { // valid message if the output of // md5() doesn't start with a digit }
This forged example may not be used directly. First of all, the key value of id needs to be larger than the value of the previous legitimate message [using the increased message ID is used to prevent replay attacks. Today there are both request forgery and replay attacks. Yes, this reminds me of CSRF, cross-site request forgery, is there a man-in-the-middle attack below?), and secondly, there must be an integer used to match the signature. These two requirements can be broken through brute force cracking.
for i from 100,000 to 100,500: for j from 0 to 9: submit request with id i and signature j
The above pseudocode attempts to send a fake message with a large ID value, and performs ten separate digital fingerprint matches for each ID [As mentioned earlier, for a string, only one number can be used in the comparison Matching, here from 0-9 because every situation can be encountered].
This flaw can be fixed by using the congruence operator [===] and checking the incoming fingerprint. These plug-in services have been fixed by using strict congruence operators [php.net instructions: a===b, then a and b have equal values and types; a==b, when the type occurs After conversion, determine whether the values are equal].
There are other issues, but they haven’t taken action yet. First of all, this approach has weaknesses [append key to $data and then hash], you should use HMAC [Hash-based Message Authentication Code, which takes a key and a message as input and generates a message digest as output ]. Second, only the action and message IDs for the operation are used to create the signature. This means that an active attacker can change the parameters in the message and the signature will still be valid [such as changing the execute_php_code message to execute arbitrary code]. For protection, the MAC should contain the entire message.
[Note that MD5-based message digest is a fallback. If possible, these plug-ins use openssl_verify(); ***The Openssl 1.0.f heartbleed vulnerability announced in 2014-04 is known as a century-level vulnerability***]
Worpit
Worpit is another remote management service, but it uses a client plug-in built from scratch. It also has a cast vulnerability that allows an attacker to log in with administrator privileges.
This plug-in provides a method for remote administrator login, using only Woprit to deliver a temporary token value that can be configured by the system. This plugin checks whether the token value provided in the request matches the value stored in the database.
if ( $_GET['token'] != $oWpHelper->getTransient( 'worpit_login_token' ) ) { die( 'WorpitError: Invalid token' ); }
Tokens are deleted from the one-time use database. This means that most of the time there is no token in the database. Therefore, calling the getTransient() method may return false. A non-strict comparison is that this means any "falsey value, such as the string 0, will be treated as a valid token. An example URL when logged in as an administrator:
This token will be deleted from the database once used, which means that most of the time there is no token in the database. Therefore, the call to the getTransient() method is likely to return false. Non-strict comparisons are also used, which means that any value equivalent to false, such as the string 0 will be treated as a valid token. Example of logging in as an administrator: http://victim/?worpit_api=1&m=login&token =0
At this point, the site is controlled by the attacker, and he has the authority to install malicious plug-ins or modify existing plug-ins.
The fix here is to use !== and do other checks and retrieve from the database.
Conclusion:
Always remember to check that user input is of the expected type and use strict comparisons in functions where security is important, such as checking authentication tokens.