Tuesday, December 30, 2014

31C3 CTF - Nokia 1337 (Pwn 30)

Here you are, playing a CTF with you mates in Hamburg. You notice there's a new task, “Nokia 1337”.
Enter the trilogy: pwn this phone. Please use only the qemu provided.
Remote instance requires proof of work: nc 188.40.18.78 1024
Connect locally via telnet to localhost:10023 after qemu booted completely.
You download the image, fire it up in Qemu and...

See the phone boot up on http://q3k.org/nokiaboot.webm
...Oh. Oh my.

Club Mate and Rum (it's called a Tschunk!) in hand, you give it a shot...

The challenge

The challenge is the first part of a trilogy of awesomeness prepared by the organizers of the 31C3 CTF. You were given a qemu machine that booted up into a Nokia-like ncurses interface. The machine was an emulated Xilinx Zynq ARM SoC. There was a low-privileged mobile user and a root user. The mobile user had the ncurses UI (a binary with DWARF debug symbols, phew) set as a shell. There was also a baseband communication coprocessor that was essential to solving the second and third parts of the challenge.

Locally, we could log in as root and see that there was a /home/mobile/flag file, containing placeholder text. Remotely, we could only access the ncurses UI via telnet. Obviously, we had to get remote code execution through the UI and read the flag.

The “phone” didn't have that much functionality. You can send and receive SMS messages, save, remove and retrieve contacts, and save, remove and use SMS templates. All storage was handled by an SQLite3 database.

The bug

As previously mentioned, the UI is a DWARF-enabled ARM binary. It's nonrelocatable, and has a writeable and executable data section. This makes our life easier.

After doing some analysis of the binary, I found an interesting function - db_get_template, which is used to retrieve a saved template from the database into a buffer. Why is it interesting? Well, let's take a look at its' signature:
Set sail for fail.
The index parameter is given by the caller to identify the ID of the template we want to retrieve. intidptr can be provided by the user to get information on the internal DB ID of the template. textptr is the output buffer address given by the caller. lenptr can be passed to receive the amount of data written to the buffer.

As you might've noticed, there isn't really a way for the caller to get the template length before calling the function - this looks bad. So, where is this function used? Well, when we wish to insert the template into our SMS. Here's the relevant code:
Iceberg, right ahead!
This is the code called when the user selects a template to be inserted into an SMS message. text, at 0x1C600, is a global buffer of the currently edited text message, text_len is a global int of the current text message length. How large is text, you ask?
Your shipment of fail has arrived,
 And since a template can also be 160 characters... Whoops! We have an overflow past the end of this buffer.

The exploit

Let's see what can we do with this. What's past this buffer? There's a 0x30-byte long structure named screen_input_dialog_arguments that contains data on how an input screen module should be called when inputting a number (when sending a message). While there's a few function pointers that we could overwrite there, there are also a whole bunch of pointers into complex structures. While doable, it's not something I'd like to have to fix up with my exploit in order to get one of the pointers called without crashing. Maybe there's a better attack vector?

Eww.

The next structure that we can overflow is screen_sms_write, a structure defining the callbacks that are called by the UI layer when we leave, enter, or input data in the SMS write screen. This looks promising! The first callback normally points to sms_write_enter, and is called when we enter the SMS write screen. So, in theory, we could overflow that pointer, leave the SMS text editor, re-enter it and then the code execution would jump wherever we want. Nice, let's try that.

Groovy, Baby! Yeah!
Let's say we make a 160-byte long template, with the last four bytes containing the address we want to write into the first callback at 0x1C6D4. Since the message buffer starts at 0x1C600, this means that our combined message+template text should have 216 bytes. Since the template is 160-bytes long, our message should be 56 bytes long. Here's what I did to test whether this attack works:

  • I created a new template, with 156 'B' characters and 4 'Z' characters. I saved it into memory.
  • I created a new message, with 56 'A' characters. I then inserted the previously crafted template at the end.
  • I exited the message editor and re-entered it.
  • I observed that the UI crashed.
So, we get a crash. If we attach GDB into the qemu stub and break around, we do indeed see a failed jump to 0x5A5A5A5A ('ZZZZ' treated as a pointer). So our smashing works!

Surprisingly, this was the easy part of the exploit. Now onto the hard part, especially if you're new to ARM exploitation....

Weaponization

We need a shellcode. Apparently, all public ARM shellcodes suck, especially if they can't contain 0x00, 0x0A, 0x1A, 0x1B and 0x1C characters. I ended up writing my own, and it's not very pretty:

'shiiii' is the sound I make each time I look at this.
The next step was to automate typing in the shellcode and other long strings into the UI. I wrote a proxy server in Python that would let me connect from a Telnet terminal and also trigger certain automated actions (typing in production token, credentials, template and SMS message). My final exploit looks like this:
  • Create a new template, with 156 'A' characters and 4 bytes of our shellcode address - 0x1C604. Remember, we can't send zeroes, so I had to add 4 to the message buffer address.
  • Create a new message with our shellcode, padded from the left with 4 'Z' characters, and from the right with enough 'Z' characters to make the whole thing be 56 bytes long. Now our shellcode is at 0x1C604 in memory.
  • Insert our template. Now we've overflowed 0x1C64, our shellcode address, into the sms_write_enter callback.
  • Exit the message screen, and re-enter it. Now we're executing our shellcode, which in turn dooes execve("/bin/sh\0", 0, 0).
  • Get flag.
  • ????
  • PROFIT
The final exploit code can be found at https://github.com/q3k/ctf/tree/master/31c3/nokia.

And here's a video of the pwn happening:


An excellent challenge and CTF overflow. Thanks CCCAC and StatumAuhuur for the challenge, and thanks fail0verflow and pasten for the fierce competition!

But there are two parts left.... stay tuned.

No comments:

Post a Comment