Joomla deserialization Vulnerability Detection
In December 15, 2015, major security vendors in China paid attention to a message about the Joomla Remote Code Execution Vulnerability from overseas sites. Then we started a round of vulnerability analysis competition, which was faster, more accurate, and more mocking.
As I don't know much about the php session serialization mechanism, I am excited to read various analyses to learn the principles of this vulnerability. Although these analysis articles help me reproduce the exploitation of this vulnerability and seem to have clearly explained the principle, I still haven't found a problem in these articles.
0 × 01 ignored corner
I will first write down the ignored questions here. Let's take a look at these articles with questions. The ignored problem is:
Does Joomla change the default SESSION Processing Method of PHP?
If you have read the analysis articles on this vulnerability like me, you will surely be impressed with the following passage:
Key name + vertical bar + value processed by the deserialization of the serialize () function.
The appearance of this sentence in the analysis article has the following explanations:
Or:
In addition:
It seems that they all point to Joomla's own SESSION mechanism, rather than the object injection problem caused by the default SESSION mechanism of PHP. As I don't understand the php session mechanism, I have to go to several articles on this topic to learn.
0 × 02 php session Customization
If you want to customize the SESSION processing process in PHP, you need to use the session_set_save_handler function to register the relevant custom processing methods, after registration, use session_start to start the SESSION mechanism and then use the defined function to process the SESSION. The sample code is as follows:
Php function sess_open ($ sess_path, $ sess_name ){
Print "Session opened.
Print "Sess_path: $ sess_path
Print "Sess_name: $ sess_name
Return true ;}
Function sess_close (){
Print "Session closed.
";
Return true;
}
Function sess_read ($ sess_id ){
Print "Session read.
";
Print "Sess_ID: $ sess_id
";
Return '';
}
Function sess_write ($ sess_id, $ data ){
Print "Session value written.
";
Print "Sess_ID: $ sess_id
";
Print "Data: $ data
";
$ Fp = fopen ('C:/wamp/www/999.txt ', 'w ');
Fwrite ($ fp, $ data );
Fclose ($ fp );
Return true;
}
Function sess_destroy ($ sess_id ){
Print "Session destroy called.
";
Return true;
}
Function sess_gc ($ sess_maxlifetime ){
Print "Session garbage collection called.
";
Print "Sess_maxlifetime: $ sess_maxlifetime
";
Return true;
}
Session_set_save_handler
("Sess_open", "sess_close", "sess_read", "sess_write", "sess_destroy", "sess_gc ");
Session_start ();
When assigning values to SESSION-related variables, the registered sess_write method is called for processing. To facilitate understanding of the input parameter content, I print the data variable. Let's take a look at the assignment process:
We can see that the data variable is passed in the serialized content of the php session mechanism. That is to say, even if we customize the SESSION processing function, if we do not process the data, the SESSION is the PHP serialized SESSION. After learning about this, let's look back at the Joomla SESSION Processing Section:
Php public function register () {// Use this object as the session handler
Session_set_save_handler (array ($ this, 'open'), array ($ this, 'close'), array ($ this, 'read '),
Array ($ this, 'write'), array ($ this, 'deststroy'), array ($ this, 'gc '));}
Public function write ($ id, $ data ){
// Get the database connection object and verify its connected. $ db = JFactory: getDbo ();
$ Data = str_replace (chr (0). '*'. chr (0), '', $ data );
Try
{
$ Query = $ db-> getQuery (true)
-> Update ($ db-> quoteName ('#__ session '))
-> Set ($ db-> quoteName ('data'). '='. $ db-> quote ($ data ))
-> Set ($ db-> quoteName ('time'). '='. $ db-> quote (int) time ()))
-> Where ($ db-> quoteName ('session _ id'). '='. $ db-> quote ($ id ));
// Try to update the session data in the database table.
$ Db-> setQuery ($ query );
If (! $ Db-> execute ())
{
Return false;
}
/* Since $ db-> execute did not throw an exception, so the query was successful.
Either the data changed, or the data was identical.
In either case we are done.
*/
Return true;
}
Catch (Exception $ e)
{
Return false;
}
}
From the code above, we can see that Joomla registers the write function as the processing function for writing SESSION content. However, in addition to a character replacement for data variables, the write function saves the data to the database without any operations. This replacement will not affect SESSION serialization and deserialization.
One sentence mentioned in these analysis articles:
Key name + vertical bar + value processed by the deserialization of the serialize () function.
It is actually the default serialization format for PHP to Process sessions.
So here I can conclude that:
Joomla's custom SESSION processing is not the culprit of object injection, and the SESSION is still serialized according to the default mechanism.
Here we have obtained the answer to the question we raised earlier, but we do not seem to understand this vulnerability any more. Since all sessions are serialized using the default PHP mechanism, how is this vulnerability formed? Now, let's continue with the analysis with a new question: is there a problem with the default SESSION mechanism of PHP?
0 × 03 The old driver took me to the trap
In addition to pointing to Joomla custom SESSION processing, another thing in common is to reference the article "security risks caused by improper configuration and use of PHP Session serialization and deserialization processors" by the old driver ryat.
This article points out that if the method used to write and read sessions is different, it may cause object injection problems. Here we will introduce the example in the ryat article:
Php // foo1.php
Ini_set ('session. serialize_handler', 'php _ serialize ');
Session_start (); $ \ _ SESSION ['ryat'] =\ \ _ GET ['ryat'];
// Foo2.php
Ini_set ('session. serialize_handler ', 'php ');
// Or session. serialize_handler set to php in php. ini session_start ();
Class ryat {
Var $ hi;
Function _ wakeup (){
Echo 'Hi ';
}
Function _ destruct (){
Echo $ this-> hi;
}
}
The php_serialize method is used for storing sessions, while the php method is used for reading sessions. The serialization format corresponding to these two methods is as follows:
In the case of Different Storage and reading methods, we can easily understand the problem of object injection, through foo1.php? Ryat = | O: 4: "ryat": 1: {s: 2: "hi"; s: 4: "ryat ";} to use the character before the vertical line as the key name, so that the serialize () function deserializes the content we entered after the vertical line. Does Joomla modify SESSION. serialize_handler in the session like the instance code? However, no. I rummaged through the Joomla code and did not find any Code related to session. serialize_handler. Therefore, Joomla still uses the unified SESSION serialization and deserialization methods.
I have been stuck in this place for a long time. To find the reason, I decided to break away from the Joomla code and use the php session udf I mentioned earlier to reproduce this deserialization vulnerability, this makes the whole serialization and deserialization process very simple, and I can print the serialized content. During my first attempt, I found a problem:
The injected vertical lines are printed in an intact manner! Oh, my God! The old driver left a button in his article. When PHP sessions are serialized in php mode, they do not check, filter, or escape the input content. Here we can get the answer to the second question:
Php session mechanism has potential object injection risks
The source code of PHP serialized SESSION content is as follows:
C
# Define PS_ENCODE_LOOP (code) do {\
HashTable * _ ht = Z_ARRVAL_P (PS (http_session_vars ));\
Int key_type ;\
\
For (zend_hash_internal_pointer_reset (_ ht );\
(Key_type = zend_hash_get_current_key_ex (_ ht, & key, & key_length, & num_key, 0, NULL ))! = HASH_KEY_NON_EXISTANT ;\
Zend_hash_move_forward (_ ht )){\
If (key_type = HASH_KEY_IS_LONG ){\
Php_error_docref (NULL TSRMLS_CC, E_NOTICE, "Skipping numeric key % ld", num_key );\
Continue ;\
}\
Key_length --;\
If (php_get_session_var (key, key_length, & struc TSRMLS_CC) = SUCCESS ){\
Smart_str_appendl (& buf, key, key_length );
If (memchr (key, PS_DELIMITER, key_length) | memchr (key, PS_UNDEF_MARKER, key_length )){
PHP_VAR_SERIALIZE_DESTROY (var_hash );
Smart_str_free (& buf );
Return FAILURE;
}
Smart_str_appendc (& buf, PS_DELIMITER );
Php_var_serialize (& buf, struc, & var_hash TSRMLS_CC );
} Else {
Smart_str_appendc (& buf, PS_UNDEF_MARKER );
Smart_str_appendl (& buf, key, key_length );
Smart_str_appendc (& buf, PS_DELIMITER );\
}\
}\
} While (0)
0 × 04 Joomla vulnerability Principle
The user-agent is used to inject serialized object code into the SESSION content. SESSION content is stored in the database and part of the content is truncated by UTF-8 malformed characters, ensure that the serialized string after the injected object is still in the correct structure. By using PHP's default SESSION serialization php method, you can pass in a string with a vertical line to make the previous serialized content a key value. This ensures that the serialization process will not stop before parsing and injecting the object content, this allows you to parse custom objects.
If you are interested in SESSION deserialization, you can take a look at the LN article and analyze the process from the perspective of PHP source code.
0 × 05 Summary
The most meaningful aspect of this vulnerability is that it tells us that using the default SESSION mechanism in PHP will have the risk of object injection. If you store SESSION serialization content (files or databases), pay attention to some truncation features of these storage objects. Otherwise, they can be used together with SESSION serialization features.