Analyzing the security risks of PHP cast type and remote management plug in

  • 2021-07-06 10:33:02
  • OfStack

Remote administration plug-ins are currently a popular utility for WordPress site administrators that allows users to perform the same actions on multiple sites at the same time, such as updating to the latest release or installing plug-ins. However, in order to achieve these operations, the client plug-in needs to give remote users great permissions. Therefore, it is important to ensure that the communication between the management server and the client plug-in is secure and cannot be spoofed by an attacker. This article looks at a few available plug-ins and exploits their vulnerabilities that can even completely compromise the site itself that is running these plug-ins.

ManageWP, InfiniteWP, and CMS Commander

These three services have the same client-side plug-in base code (initially the ManageWp implementation was visually tweaked by the other two), 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, which is used to calculate the message authentication code of every message, instead of requiring the user to provide administrator credentials (MAC, which we usually see as the MAC address of hardware, here is Message Authentication Code). When a message passes through a message digest algorithm using a shared key, a message digest is generated. The MAC is then sent after the message. After receiving the message, the receiver calculates the received message with the shared secret key to generate MAC2, and then compares it with MAC1. Message digest is used to verify the authenticity and integrity of messages (students who have studied cryptography should know this), which is a good way to ensure communication security, but the implementation defects of client plug-ins of these three services lead to serious vulnerabilities.

One incoming message authenticated by helper. class. php is as follows:


// $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] will occur before comparison. The output of the md5 () function is always a string, but if $signature changes to an integer, then the type conversion that occurs during the comparison can easily forge a matching MAC. For example, if the real MAC begins with "0" or a non-numeric character, then 0 can match, if it is "1xxx", then the integer 1 can match, and so on. (This is actually one of the features of php, Of course, other languages will also have it. When a string and a number are compared in a non-strictly equal way, If the first character is a number, it will be converted into a corresponding integer for comparison, and if it is not a 0-9 character, it will be treated as 0. php. net official explanation: If you compare a number with a string or compare a string involving a number content, the string will be converted into a numerical value and the comparison will be carried out according to the numerical value).

String to numeric value:

When a string is taken as a numeric 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 valued as integer. In all other cases, it is taken as float.

The beginning of the string determines its value. If the string starts with a legal numeric value, that numeric value is used. Otherwise its value is 0 (zero). The legal value consists of an optional sign followed by one or more digits (which may have a decimal point) followed by an optional exponential portion. The exponential portion consists of 'e' or 'E' followed by one or more numbers.


<?php
var_dump(0 == "a"); // 0 == 0 -> true
var_dump("1" == "01"); // 1 == 1 -> true
var_dump("10" == "1e1"); // 10 == 10 -> true
var_dump(100 == "1e2"); // 100 == 100 -> true
var_dump('abcdefg' == 0); // true 
var_dump('1abcdef' == 1); // true 
var_dump('2abcdef' == 2); // true 
?>

Unfortunately, an attacker can provide an integer as a signature. In init. php, the incoming request is decoded using base64_decode () and the result is deserialized. The use of Unserialize () means that you can control the type of input data, and a spoofed serialized 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 is signed with the integer 0 and then executes arbitrary PHP code using the plug-in supplied execute_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 fake example may not be used directly. First of all, the key value of id needs to be larger than the value of the previous legal message (using the added message ID to prevent replay attacks, today there are both request forgery and replay, which reminds me of CSRF, cross-station request forgery, is there a man-in-the-middle attack below), and secondly, there should be an integer for matching signatures, which can be broken through by 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 false message with a large ID value, and performs 10 separate digital fingerprint matches for each ID (as mentioned earlier, for a string, only one number can be matched during comparison, and here it is from 0 to 9 because every one situation can be encountered).

This 1 flaw can be fixed by using the congruence operator [===] and checking the incoming fingerprint. These plug-in services are repaired by using strict congruence operators (php. net description: a===b, then a and b values are equal and the types are equal; a==b values are judged after type conversion).

There are other problems, but they haven't taken action yet. First of all, this 1 approach is weak (the key is appended to $data and then hashed), and HMAC should be used (Hash-based Message Authentication Code, with 1 key and 1 message as input and 1 message digest as output). Second, the operation-only action and the message ID are used to create the signature. This means that an active network attacker can change the parameters in the message while the signature is still valid (for example, change the execute_php_code message to execute arbitrary code). For protection, MAC should contain the whole message.

(Note that message digest based on MD5 is a fallback, and these plug-ins use openssl_verify () if possible; ***The Openssl 1.0. f heartbleed vulnerability published in 2014-04 is known as a century-class vulnerability ***)

WORPIT

Worpit is another remote administration service, but it uses a client plug-in built from scratch, and it also has a cast vulnerability that could allow an attacker to log in with administrator privileges.

This plug-in provides a remote administrator login method, using only Woprit to pass the temporary token value that can be configured by the system. This plug-in 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' ); 
}

Token is deleted from the database used once. This means that most of the time there is no token in the database. Therefore, it is possible to return false when calling getTransient () method. Less strictly, this means that any "falsey value, such as the string 0, will be treated as a valid token. 1 example URL logged in as administrator:

This token1 is removed from the database when used, which means that most of the time there is no token in the database. Therefore, a call to the getTransient () method is likely to return false. Less stringent comparisons are also used, meaning that any value equivalent to false, such as the string 0, is treated as a valid token. Examples of login as an administrator are: http://victim/? worpit_api=1 & m=login & token=0

At this point, the site is under the control of the attacker, who has access to install malicious plug-ins or modify existing plug-ins.

The repair scheme here is to use! = = and other checks and retrievals from the database.

Conclusions:

1 Be sure to remember to check that user input is of the expected type and use it for strict comparisons in functions where security is important, such as checking authentication tokens.


Related articles: