아래의 소스를 vi로 gcc test.c 로 컴파일 합니다.

gdb 로 컴파일 된 파일을 실행합니다.

아래와 같이 실행하면 디버거가 붙은 상태로 실행이 됩니다.

root@android:/data/local # gdb a.out
GNU gdb (GDB) 7.5.1
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabi".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /data/local/a.out...(no debugging symbols found)...done. 

아래 명령어들을 순차적으로 실행하며 진행합니다.

run / r

프로세스를 처음부터 실행합니다. 디버깅 도중 run을 입력하면 프로세스가 리셋되어 처음부터 실행됩니다.

(gdb) run
Starting program: /data/local/a.out
hello world!
[Inferior 1 (process 19840) exited with code 012]

 

 disas

 특정 주소를 디스어셈블링합니다. 함수명이나 주소를 입력하면 됩니다.

(gdb) disas main
Dump of assembler code for function main:
   0x00008360 <+0>:     push    {r11, lr}
   0x00008364 <+4>:     add     r11, sp, #4
   0x00008368 <+8>:     ldr     r3, [pc, #12]   ; 0x837c <main+28>
   0x0000836c <+12>:    add     r3, pc, r3
   0x00008370 <+16>:    mov     r0, r3
   0x00008374 <+20>:    bl      0x830c
   0x00008378 <+24>:    pop     {r11, pc}
   End of assembler dump.

 

break / b

브레이크 포인트를 설정합니다. 함수명이나 주소를 입력하면 됩니다.

(gdb) break *main
Breakpoint 1 at 0x8360
(gdb) break *main+4
Breakpoint 2 at 0x8364
(gdb) break *0x837c
Breakpoint 3 at 0x837c

 

info breakpoints / info b

브레이크포인트를 확인합니다.

(gdb) info b
Num     Type           Disp Enb Address    What
1       breakpoint     keep y   0x00008360 <main>
2       breakpoint     keep y   0x00008364 <main+4>
3       breakpoint     keep y   0x0000837c <main+28>

위와 같이 브레이크 포인트를 걸고 run을 실행하면 설정한 브레이크 포인트에 걸리게 됩니다.

delete 1 을 입력하면 1번 브레이크 포인트가 삭제됩니다.

 

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /data/local/a.out

Breakpoint 1, 0x000083b0 in main ()

 

info registers / i r

현재 레지스터 상태를 확인합니다. 왼쪽은 16진수 오른쪽은 10진수입니다.

(gdb) i r
r0             0x1      1
r1             0xbefffc54       3204447316
r2             0xbefffc5c       3204447324
r3             0x0      0
r4             0x8394   33684
r5             0xbefffc54       3204447316
r6             0x1      1
r7             0xbefffc5c       3204447324
r8             0x0      0
r9             0x0      0
r10            0x0      0
r11            0x0      0
r12            0x4007600c       1074225164
sp             0xbefffc38       0xbefffc38
lr             0x4003c125       1073987877
pc             0x83b0   0x83b0 <main>
cpsr           0x60000010       1610612752

 

x

특정 메모리의 값을 값을 확인합니다. disas가 되지 않는 것은 x/10i 같은 명령어로 살펴볼 수 있습니다.

(gdb) x $r1
0xbefffc54:     0xbefffd60
(gdb) x 0xbefffc54
0xbefffc54:     0xbefffd60

(gdb) x/10i 0x40005dce
=> 0x40005dce:  mov     r0, r4
   0x40005dd0:  blx     0x40005fc8 <close>
   0x40005dd4:  movs    r1, #0
   0x40005dd6:  mov     r0, r5
   0x40005dd8:  bl      0x40006a84 <bsd_signal>
   0x40005ddc:  cmp     r5, #16
   0x40005dde:  bhi.n   0x40005e30
   0x40005de0:  movs    r1, #1
   0x40005de2:  ldr     r3, [pc, #80]   ; (0x40005e34)
   0x40005de4:  lsls.w  r0, r1, r5

 

nexti / ni

한 라인 씩 실행합니다.

(gdb) nexti
0x000083b8 in main ()
(gdb) nexti
0x000083bc in main ()
(gdb) nexti
0x000083c0 in main ()
(gdb) nexti
0x000083c4 in main ()
(gdb) ni
0x000083c8 in main ()

이 상태에서 continue / c 를 입력하면 다음 브레이크 포인트까지 실행됩니다. 브레이크 포인트가 설정되어 있지 않으면 끝까지 수행하게 됩니다.

본격적인 분석에 앞서서 readelf 를 사용하여 elf 포맷을 보겠습니다.

root@android:/data/local # readelf -h a.out
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0x8330
  Start of program headers:          52 (bytes into file)
  Start of section headers:          1384 (bytes into file)
  Flags:                             0x4000002, has entry point, Version4 EABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         6
  Size of section headers:           40 (bytes)
  Number of section headers:         20
  Section header string table index: 17 

a.out의 헤더를 출력해 보았습니다. entry point address는 프로그램의 시작 주소입니다.

(gdb) disas 0x8330
Dump of assembler code for function _start:
   0x00008330 <+0>:     mov     r0, sp
   0x00008334 <+4>:     mov     r1, #0
   0x00008338 <+8>:     add     r2, pc, #4
   0x0000833c <+12>:    add     r3, pc, #4
   0x00008340 <+16>:    b       0x8318
   0x00008344 <+20>:    b       0x8360 <main>
   0x00008348 <+24>:    muleq   r1, r4, r3
   0x0000834c <+28>:    muleq   r1, r12, r3
   0x00008350 <+32>:    andeq   r0, r1, r4, lsr #7
   0x00008354 <+36>:    andeq   r0, r1, r12, lsr #7
   0x00008358 <+40>:    nop                     ; (mov r0, r0)
   0x0000835c <+44>:    nop                     ; (mov r0, r0)
End of assembler dump. 

gdb로 보면 _start 함수를 가리키고 있습니다.

0x8344를 보면, main 으로 무조건 점프하라고 되어 있습니다. (b = 무조건 점프)

0x8360에 브레이크 포인트를 걸고 run을 입력합니다.

브레이크 포인트가 걸리면, 레지스터를 확인합니다.

SP는 현재 스택 위치 (x86의 esp에 해당)

PC는 현재 명령어 위치 (x86의 eip에 해당)

R0~ R12는 범용 레지스터 (x86의 eax, ebx, ecx, edx에 해당)

(gdb) info reg
r0             0x1      1
r1             0xbefffc54       3204447316
r2             0xbefffc5c       3204447324
r3             0x0      0
r4             0x8344   33604
r5             0xbefffc54       3204447316
r6             0x1      1
r7             0xbefffc5c       3204447324
r8             0x0      0
r9             0x0      0
r10            0x0      0
r11            0x0      0
r12            0x4007600c       1074225164
sp             0xbefffc38       0xbefffc38
lr             0x4003c125       1073987877
pc             0x8360   0x8360 <main>
cpsr           0x60000010       1610612752 

스택을 확인합니다.

(gdb) x/10xw $sp
0xbefffc38:     0x00008348      0x00000000      0x00000000      0x00000000
0xbefffc48:     0x00000000      0x400054c1      0x00000001      0xbefffd60
0xbefffc58:     0x00000000      0xbefffd72

nexti를 치면 다음 명령어로 이동합니다. pc는 0x8364가 되며 디스어셈블 내용은 위 상단 disas 설명 표를 확인해주세요.

0x00008360 <+0>:     push    {r11, lr}

R11, LR을 스택에 push 합니다. push의 스택은 아래와 같습니다.

(gdb) x/10xw $sp

0xbefffc30:     0x00000000      0x4003c125      0x00008348      0x00000000
0xbefffc40:     0x00000000      0x00000000      0x00000000      0x400054c1
0xbefffc50:     0x00000001      0xbefffd60

LR, R11 순서대로 스택에 쌓이게 됩니다.

LR은 Link Register의 약자로 복귀 주소를 넣습니다. 해당 주소 0x4003c125를 보면 __libc_init() 함수를 가리키고 있습니다.

0x00008364 <+4>:     add     r11, sp, #4

SP에 4를 더해서 R11에 저장합니다. 

r11            0xbefffc34       3204447284 

0x00008368 <+8>:     ldr     r3, [pc, #12]   ; 0x837c <main+28>

IDA디스어셈블러는 LDR R3, =(0x8380 - 0x8374) 로 해석합니다. 

r3             0xc      12 

 

0x0000836c <+12>:    add     r3, pc, r3

R3 = PC + R3 

r3             0x8380   33664 


0x00008370 <+16>:    mov     r0, r3

puts()인자 값으로 R0을 사용합니다.

0x00008374 <+20>:    bl      0x830c

0x830c는 puts()입니다.

bl은 링크포함 분기인데, 특이할 점은 다음 명령어의 주소(0x8378)가 LR에 들어갑니다. 즉, 복귀할 주소를 저장해둡니다. puts() 안으로 들어가기 위해선 nexti가 아닌 stepi 명령어를 입력해야 합니다.

인자 값으로 넣은 R0을 어디서 써먹는지 추적해 보았습니다.

   0x830c:      add     r12, pc, #0, 12
   0x8310:      add     r12, r12, #8, 20        ; 0x8000
   0x8314:      ldr     pc, [r12, #364]!        ; 0x16c 

0x40041244 in puts () from /system/lib/libc.so

   0x40041244 <+0>:     push    {r4, r5, r6, lr}
   0x40041246 <+2>:     sub     sp, #32
   0x40041248 <+4>:     mov     r6, r0

libc.so에서 R0을 가져옵니다.

0x00008378 <+24>:    pop     {r11, pc}

PC에 R11의 값을 넣고 POP합니다. POP하면 RETURN이 되어 해당 주소로 갑니다.

PC가 r11의 값을 가지면서 0x0을 pop하기 때문에, 아까 LR에 적재한 0x4003c125로 돌아가게 됩니다.

 r11            0xbefffc34       3204447284

(gdb) x/10xw $sp
0xbefffc30:     0x00000000      0x4003c125      0x00008348      0x00000000
0xbefffc40:     0x00000000      0x00000000      0x00000000      0x400054c1
0xbefffc50:     0x00000001      0xbefffd60

 

 

준비물

1. 루팅된 폰

루트권한은 필수입니다.

2. adb

* 우선 폰의 환경설정-개발에서 USB 디버깅 체크를 하여 주세요.

Android SDK를 설치하면 사용할 수 있습니다.

http://developer.android.com/sdk/index.html

adb 툴은 \platform-tools에 있습니다.

폰을 USB연결 후 adb devices를 치면 연결된 장치가 보입니다.

(블루스택이 설치되어 있는 경우 블루스택 디바이스로 연결되는 수가 있으니 주의 바랍니다.)

확인 후 adb shell로 들어가서 명령 프롬프트가 뜨면 성공입니다.

3. busybox & busybox X

루팅 후 앱스토어에서 다운로드합니다. 리눅스용 필수 프로그램들을 사용할 수 있습니다.

(일부 기기에서 설치 후 동작이 되지 않을 수도 있습니다. busybox installer 로 설치를 시도하는 것을 권장합니다.)

(busybox가 설치 안되는 하위 기기는 사용할 수 없습니다.)

(설치 후 심볼릭 링크가 정상동작 하지 않는 경우 tinybox를 설치해도 무방합니다.)

busybox X 는 busybox에서 제공하지 않는 setarch, ulimit와 같은 명령어를 제공하여 줍니다.

4. gdb

디버거입니다. 

gdb

출처 : http://searchme.tistory.com/37

다운로드 한 뒤 adb push [파일전체경로] [핸드폰 경로] 로 올리면 됩니다.

예) adb push d:\gdb /data/local

그 다음 /data/local 디렉토리로 가서 chmod 777 로 권한을 주면 실행할 수 있습니다.

5. gcc

폰에서 직접 컴파일하여 실행하기 위해 사용합니다.

출처 : http://rwiki.sciviews.org/doku.php?id=getting-started:installation:android

원본 파일의 링크가 삭제되었으므로 아래 첨부파일들을 다운로드 하시길 바랍니다.

android_gcc_supplement.tar.bz2

android_gcc_r2a.zip.001android_gcc_r2a.zip.002android_gcc_r2a.zip.003android_gcc_r2a.zip.004

android_R_r1a2.tar.bz2

설치 방법 :

adb shell mkdir /data/local/gcc
adb push android_gcc_r2a.tar.bz2 /data/local/gcc
adb push android_gcc_supplement.tar.bz2 /data/local/gcc
adb push android_R_r1a2.tar.bz2 /data/local/gcc
adb shell
cd /data/local/gcc
tar xjf android_gcc_r2a.tar.bz2
tar xjf android_gcc_supplement.tar.bz2
tar xjf android_R_r1a2.tar.bz2

환경 변수 등록 : bashrc

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/data/local/gcc/lib
export PATH=$PATH:/data/local/gcc/bin

6. SSH Server

폰에 SSH 서버를 설치합니다. ADB를 이용하지 않아도 되는 장점이 있습니다.

http://codetronik.tistory.com/31

본 포스팅은 기존 제작된 익스플로잇을 무작정 따라하는 내용을 담고 있습니다.

(천만 스크립트 키드 양성을 위한 그날까지..!) 

 


 

유형 : Stack Buffer Overflow

타겟 프로그램 : BlazeDVD Pro Player 6.1

테스트 환경 : Windows XP SP3 Eng

링크 : http://www.exploit-db.com/exploits/32737/ (제작자 : Deepak Rathore)

설명 : Playlist를 열때 Stack Buffer Overflow를 일으킵니다.


[재현]

  

[그림1 - 프로그램 실행 화면]

 

링크에서 타겟 프로그램과 Exploit을 다운로드 합니다.

Perl 설치가 귀찮으신 분은blazeExpl.plf<-- 이것을 다운로드 해 주세요.

 

[그림2 - Playlist Exploit 파일]

해당 파일을 Hex Editor로 열면 위의 그림과 같이 나옵니다.

위의 박스친 부분의 주소를 사용자PC에 맞게 고쳐줘야 하는데, ASLR이 적용되지 않은 모듈에서 jmp esp를 찾으면 됩니다.

 

[그림3 - JMP ESP 찾기]

저는 0x7C86467B에 있는 jmp esp를 사용했습니다.

 

[그림4 - Exploit 성공]

Playlist 를 열어서 그림4와 같이 나오면 성공입니다.

[분석] 

[그림5 - 버퍼를 스택에 복사]

Playlist를 로드하면 위의 코드에서 스택에 Playlist의 버퍼를 복사합니다. 

(스샷을 연속적으로 찍지 않아서 스택의 주소가 그림마다 다릅니다. 양해를..)

[그림6 - 스택 비교]

위의 그림은 스택 버퍼가 쓰여지기 전과 후의 모습입니다.

 [그림7 - RETN]

계속해서 Step over를 하면 최종적으로 그림7과 같은 코드를 만나게 되어 리턴합니다.

 

[그림 8 - RETN 시 스택]

RETN 직전 스택을 보면 ESP가 0x7C86467B를 가리키고 있는데 해당 주소는 JMP ESP 를 담고 있습니다.

말 그대로 ESP로 점프하라는 것이며, 한번 더 Step over 하면 스택에 0x10이 더해진 0x12F0D0으로 이동합니다.

 

[그림 9 - 쉘코드]

NOP후 쉘코드가 실행됩니다.

+ Recent posts