Codeigniter Object Injection Vulnerability via Encryption Key

Codeigniter is one of my favorite PHP framework. Like every one else, I’ve learned PHP MVC programming with this framework. Today, I decided to analyze Codeigniter for PHP Object Injection Vulnerability.

I’ll focus on Session mechanism of Codeigniter at rest of this write-up . All method that I will explain are located in CodeIgniter/system/libraries/Session.php file. Also I used Codeigniter 2.1 stable release for this research.

Codeigniter Session Mechanism

CI use serialization methods of PHP to store variables in user session. But Codeigniter session mechanism is not working like we expect. It stores session variables in client’s cookie. We expect that Codeigniter stores session variables at server side, mostly on disk instead of user cookie. I don’t know why developers decided to this way.

Following description grabbed from codeigniter documentation.

The Session class stores session information for each user as serialized (and optionally encrypted) data in a cookie. Even if you are not using encrypted sessions, you must set an encryption key in your config file which is used to aid in preventing session data manipulation.

In this write-up we will analyze the possibilities of session data manipulation and so on.

Codeigniter Session Data Structers

Let’s start read some codes. But before go further let me explain how Codeigniter creates sessions and put variables into the session -actually cookie!-

I will use CI shortcut instead of Codeigniter at rest of the write-up by the way.

Lets start to review codes with construct method of Session class. Following codes are a part of __construct method.

CI try to read value from current client’s cookie. If it fails then it created new one. Let assume we dont have any cookie right now. So CI going to call sess_create function. Following codes belongs to sess_create function which is one of the Session class’s methods.

 sess_create responsible for create session variables and set them to the user. As you see, it create array in order to store session id, ip address, user-agent etc  in session. When userdata array ready, it calls _set_cookie() which is another Session class function. Now it’s time to analyze _set_cookie function’s codes.

There is one description about code.

// if encryption is not used, we provide an md5 hash to prevent userside tampering

CI uses md5 for encrypt serialized session data. It use encryption_key for salt. Then adds result of the md5 encryption to end of the $cookie_data.

I want to explain the above code. $cookie_data is going to send to the client. It contains ip_address, user-agent etc. CI use encryption_key as a salt key. As an attacker we know $cookie_data and result of the md5 encryption. Because CI add result of the md5 calculation to end of the $cookie_data then sends it to us -attacker. Let me show real data.

You see ci_session variables at above. It’s cookie variable and end of the value you will see 550d610647f0ee0d019357d84f3b0488 . It’s result of md5! If we try to reverse that;

Value of $cookie_data variables = a:5:{s:10:”session_id”;s:32:”e4f2a5e86d65ef070f5874f07c33b043″;s:10:”ip_address”;s:9:”127.0.0.1″;s:10:”user_agent”;s:76:”Mozilla/5.0+(X11;+Ubuntu;+Linux+x86_64;+rv:28.0)+Gecko/20100101+Firefox/28.0″;s:13:”last_activity”;i:1397754060;s:9:”user_data”;s:0:””;}

$this->encryption_key = is what we are trying to get!

Result of md5 calculation = 550d610647f0ee0d019357d84f3b0488

Obviously we can use brute force attack in order to detect salt.I mean encryption key.

For example ; let assume following defination.

$this->encryption_key = WE DONT NOW!

Value of $cookie_data variables  = a:1:{s:4:”test”;i:1;}adf8a852dafaf46f8c8038256fd0963a

You can use brute force techniques in order to detect encryption_key!  In order to brute force this md5,, you can think encryption_key as plain text that you want to reach, so Value of $cookie_data variables becomes a salt. And ofcourse reverse md5 prototype md5(plain-text, SALT) to md5(SALT,plain-text).

This is demonstration.We will have too long $cookie_data in real life example. As I mentioned before, $cookie_data becomes salt in order to brute force md5. Unfortunately HashCat does not support for this kind of salt key.

Codeigniter Session Data Integrity and Verification

We learned how CI create cookie data. Now we will have analyze CI’S cookie data verification system. As I assumed before, we didn’t have a cookie. This time we have a cookie in our HTTP request. Let’s see how CI checks it and verify it. In order to do that, we need to understand codes of sess_read() method of Session class.

Remember _construct method of Session class. It try to read cookie from client with sess_read method. This is the reason why we will analyze sess_read method.

Line 4 = Get cookie from client.

Line 7 = Check returned value. If it is false, that means client don’t have cookie!

Line 13 = If encryption is enabled. -In write-up case it’s not!-

Line 20 = Strip out hash from cookie. -Remember my previous explanation. CI addes md5 hash end of the session data -

Line 21 = Strip out session data from cookie.

Line 24 = Md5 calculation in order to check data integrity.

Does the md5 hash match? This is to prevent manipulation of session data in userspace

Line 32 = Call _unserialize method of Session data! -Now we can think about Object Injection Vulnerability.-

Rest of the code CI check session variables and user-agents. Basically CI want to see same user-agent and ip address. As we analyzed sess_write method CI writes those variables into the session.

Lets analyze _unserialize method’s codes.

Yes! It calls unserialize method with  user supplied data which is client’s Cookie in this case.

RECAP

Before go to the exploitation part, I want to recap what we learned until now.

  1. CI use serialize and unserialize method in order to store variables in Session.
  2. CI don’t use real Session dialectic. CI stores session variables in Client-site (cookie) instead of Server-Site (hard-dirve..)
  3. CI do md5 calculation in order to detect user site tampering.
  4. Checks user-agent and ip address are same with session data.
  5. Call unserialize method.

IN CONCLUSION

We have some obstacles.

  • CI doesn’t use destruct or wakeup methods…
  • Codeigniter loads libraries by $autoload['libraries'] variable. If Session class defined in first place in that array, you can NOT reach rest of the classes. Because we are exploiting Session and CI initialize Session class before user land libraries.

Let me clarify it. CI going to create objects from classes by order. That means classes which located in system/core file path will create first. Then CI going to look $autoload['libraries'] array and create object by order again. So, location of the session class initialization is too much important in order to reach different classes.

I wrote vulnerable codeigniter application to use it as an example. Following expression is related with that application.

https://github.com/mmetince/codeigniter-object-inj

Now we can use weakness of session integrity check and unserialize method together.

As you figure out, we need to know encryption_key to use that vulnerabilities for evil! There is two method for that.

1 – Like I explained before, use weakness of md5 and failed design of CI’s session data integrity together. Brute force it! I suggest you do that when you believe encryption_key is not too long.

2 – A lot of developer usually push their application to github without change encryption_key. And people who use one of that application usually don’t change encryption_keys.

We already know the encryption_key which is h4ck3rk3y in this case. Let’s start!

http://localhost:8080/index.php/welcome

When I call the above URL, it returned following HTTP response to me.

We see Set-Cookie http header variable. Let analyze it

You see Expires dates and Max-Age at end of the string.They are not important for now. Let strip them out.

Now we will seperate md5 and cookie from that string  like CI do.

md5 = 30f9db14538d353e98dd00d41d84d904

Session data= a:5:{s:10:”session_id”;s:32:”b4febcc23c1ceebfcae0a12471af8d72″;s:10:”ip_address”;s:9:”127.0.0.1″;s:10:”user_agent”;s:76:”Mozilla/5.0+(X11;+Ubuntu;+Linux+x86_64;+rv:28.0)+Gecko/20100101+Firefox/28.0″;s:13:”last_activity”;i:1397759422;s:9:”user_data”;s:0:””;}

We have learned that CI puts user-agent into the session data as you can see above. Actually session data string is a PHP array.

We know CI going to check ip address and user-agents after unserialize it. But already done with Object Injection before that controls. We can change it to whatever we want!

Now it’s time to create our object in order to exploit class. Following class can be found under application/libraries in our example.

You see __destruct method save class variable into the cache.php file. Serialized form of Cacheclass going to be like following string.

We will change it to like following one in order to write eval codes into to cache.php file.

Now we need to calculate true md5 value for malformed session data in order to pass integrity control of sess_read method.

And result is fc47e410df55722003c443cefbe1b779. We will add this md5 end of the our new Cookie value.

When you send above http request to the CI you will see the following codes in content of cache.php

Tweet about this on Twitter74Share on Facebook106Share on Reddit8Share on LinkedIn14Pin on Pinterest0Share on Google+1Share on TumblrPrint this page

11 Responses to Codeigniter Object Injection Vulnerability via Encryption Key

  1. Snyff says:

    “I don’t know why developers decided to this way”

    It’s normally used to make load balancing easy: you don’t need to share a pool of sessions (on disk/database for example), you just need to share the key. Same behaviour can be observed in Rack (ruby on rails), some go frameworks (revel i think) and java frameworks (Play and Ninja).

    Also the usage a md5 instead of using hmac to perform the signature isn’t ideal.

    Good write-up anyway :)

    • mehmet ince says:

      I’m glad you like it. Thank you for your answer but it still seems dangerous for me. I always afraid of that giving some important variables to client-site and trust them when they come back to the server side.

      For instance, I found a open source CMS which is developed with Codeigniter. It doesn’t change encryption_key at installation process. So, everyone use same key on internet right now. I’m able to decrypt session string via that static encryption key. So we reached session array which contains privileges and permissions. We can easly unserialize it, update is_admin to True and serialize it again. Final step is ofc ourse encrypt it again with same key and add result to Cookie header. Attacker gain Administrator right via PHP Session Array Injection – session array injections sounds cool :) – . Developers need to be really careful about this kind of cases .

    • matt says:

      “Also the usage a md5 instead of using hmac to perform the signature isn’t ideal.” This is true, but its not the issue. You do have to worry about collisions with md5, but thats not the part that he is pointing out is brute-forceable. Even if they switch to sha256 nothing changes about this issue. The issue is leaking / brute-forcing the secret key side of the equation.

      • snyff says:

        I was more thinking of something along the line of length extension attacks (even if it does not apply here).

  2. matt says:

    It is always a security issue to leak a secret key. Im not sure I agree that there is a vulnerability in the key or CI. The vulnerability in the fact that you leak / have a brute forceable key that is protecting the session store which happens to be a cookie. If the key is not leaked this is a perfectly reasonable and secure solution.

    You say: “CI uses md5 for encrypt serialized session data.” but this is not exactly true. It uses md5 and a secret to sign data. There is a big difference between “hashing” (which md5 does and is used as a checksum) and encryption. Encryption should be used by default.

    Using serializeable strings as the session is very common. The normal built in session store in PHP works the exact same way (see: session_encode PHP doc) . Ruby uses the Marshal framework to do the same thing as well and uses cookies. (see: http://robertheaton.com/2013/07/22/how-to-hack-a-rails-app-using-its-secret-token/).

    I think it would be best to just use some custom session store like JSON instead of objects, however the important security lesson is don’t let users control the session, and this is done when using a cookie based session store by protecting encryption and / or signing keys.

    • mehmet ince says:

      Thank you for correction. Actually I was trying to say same thing with you but you are right “CI uses md5 for encrypt serialized session data.” wasn’t clear enough and also wrong.

      As you mentioned, storing session data in cookie is reasonable as long as keep secret key in “secret”. For example; Django changed serialization method to json in order to cover weakness of Pickle. In my opinion, PHP frameworks need to change serialize to json too.

  3. boolgool says:

    you say that you write a self-class and put it to CI’s lib dirctory,in that class you add destruct function and some dangerous actions ,then you use your encyprt key :h4ck3rk3y construct request data(cookie) and request you website,then exploited it.
    is what i say right?
    but if last anwser is ‘yes’,i will say that it has very very little things to do with CI,because of 2 point:
    1.the class you write include destruct function which be put into CI’s lib class is just a YY;
    2.the encypt key can be brute force out or not is relation to FUZZ.

    • mehmet ince says:

      Yes you are right. I demonstrate object injection via example application which is based on Codeigniter. I mean you can change session array to whatever you want, for example you can append new field into the array or you can change user_id = 13 to 1 in order to gain administrator privileges. But I’m saying again I demonstrate only for 1 case. You don’t need to __destruct method for all case. You can simply change session data!

      For instance let assume CI set is_admin to False in our cookie. If we know the encryption key we can change it to True in order to gain admin access. So Object injection attacks can be change depends on particular application.

      Read following vulnerability for another example. http://www.mehmetince.net/pyrocms-object-injection-vulnerability-another-step-damn-thee-steps-damn-thee/

      • boolgool says:

        yeah! good job!
        for the first eye of this paper,i see that you just found a bug in CI framework,(i really thought it was just a bug yesterday).
        but after you post No-CMS admin hi-jack exp here:http://www.exploit-db.com/exploits/32976/
        i will agree that it’s not just a bug in a framework bug a vul in some CMS based-on CI just like No-CMS and pyrocms.
        the last question is that you put object-injection and CI-Encryption-Key bug together,i really don’t think it’s a good idea.

  4. This isn’t a vulnerability (at least what you describe). The code can definitely be improved, but it’s not the vulnerability that you claim it to be:

    “Obviously we can use brute force attack in order to detect salt.I mean encryption key.”

    That “obviously” is an extreme hand-wave. If you used a properly random key (128+ bits of cryptographic-secure random data), then you can’t effectively brute force it. Simply because you’d need a ridiculous amount of hardware to do it. To put it in perspective, at 100 billion hashes per second (current records for MD5), you’d need to run that computer for approximately 4.15 billion times the age of the universe to have a 50% chance of brute-forcing a 128 bit key. (2^128 / 10,000,000,000 / 60 / 60 / 24 / 635 / 13,000,000,000).

    As far as md5() being broken, this method does avoid the length extension attacks by appending the key to the data (if it was md5($key . $data), then collisions would be easier).

    With a strongly generated secret, there is no hole. So the vulnerability is on admins who aren’t generating proper keys.

    As far as how to fix it, the only real way to fix it would be to add a key derivation routine in there to resist the brute forcing if weak key material is used (think PBKDF2 + salt, with a high iteration count). Switching from md5($data . $key) to hash_hmac("sha256", $data, $key) would be great from other vectors, but wouldn’t directly solve this problem at all.

    • mehmet ince says:

      Thank you Anthony for your explanatory comment. Of course it wouldn’t be possible if you used random string as an encryption_key. But I analyzed 2 popular open source CMS based on Codeigniter. They doesn’t generates random string for encryption key! I mean encryption key is static. So as an attacker we know encryption key, that lead to attacker to do Object Injection attacks. Because Codeigniter use serialize/unserialize in order to get back session array which is can be spoofed by client when encryption key public! So this is definitely vulnerability in my opinion. Example : http://www.mehmetince.net/pyrocms-object-injection-vulnerability-another-step-damn-thee-steps-damn-thee/

      I remember that I said following two sentences in write-up.

      1 – Like I explained before, use weakness of md5 and failed design of CI’s session data integrity together. Brute force it! I suggest you do that when you believe encryption_key is not too long.
      2 – A lot of developer usually push their application to github without change encryption_key. And people who use one of that application usually don’t change encryption_keys.

      I saw a lot of application use 4-5 length for encryption key. It’s worth to brute force it. This is the my reason whay i said “Obviously we can use brute force attack in order to detect salt.I mean encryption key.”

      Main focus of that write-up is call attention to possibilities of Object Injection and static encryption_key. I’m glad you read it:)

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">