본 포스팅을 따라하시면 손쉽게 VS2010에서 64비트 WDK를 개발하실 수 있습니다.

시스템 변수를 추가하는데 1,2,3 순서대로 클릭한 후 그림과 같이 새 시스템 변수를 입력합니다. (자신의 경로명에 맞게)

지금부터 프로젝트를 생성하여 빌드를 할건데 귀찮으신 분은 아래 프로젝트 파일로 테스트 해 보시길 바랍니다. 

sample 프로젝트 다운로드  

sample.zip

 

Visual Studio 2010을 실행하여 메이크파일 프로젝트를 생성합니다.

 

빌드 명령줄을 화면과 같이 입력합니다.

 

속성 페이지에서 포함 디렉토리에 그림과 같은 경로를 추가합니다. (자신의 경로에 맞게)

라이브러리 디렉토리도 추가해야 하는데 c:\WinDDK\7600.16385.1\lib\win7\i386 한 줄 추가합니다.

이제 빌드를 위해 필수 3가지 파일을 준비합니다.

ddkbuild.cmd

MAKEFILE

SOURCES

 

파일들을 자신의 프로젝트의 sample.vcxproj가 있는 디렉토리에 복사 후 SOURCES 파일을 열어 TARGETNAME과 SOURCES를 자신의 환경에 맞게 설정합니다.

빌드가 잘 되는지 테스트 합니다.

이제 asm을 추가할 차례입니다.

프로젝트 디렉토리에 AMD64 디렉토리를 생성합니다.

(필자의 경로 : D:\source\sample\sample\amd64)

SOURCES 파일에 아래 한 줄을 추가합니다.

AMD64_SOURCES=func.asm

프로젝트에서 func.asm을 생성 후 다음과 같이 입력합니다.

작성한 asm을 C에서 연동해봅니다.

빌드가 잘 되는지 테스트 합니다. 

 

이제 실제 머신에서 잘 동작하는지 테스트합니다.

드라이버 서명

원칙적으로는 64비트에서는 서명되지 않은 드라이버를 서비스에 등록할 수 없습니다.

하지만 OSR Loader + Driver Signature Enforcement Overrider (이하 DSEO) 툴 조합으로 이를 해결할 수 있습니다.

OSR Loader (AMD64 용) 다운로드OSRLOADER.exe

DSEO 다운로드dseo13b.exe

DSEO의 Sign a System File을 이용하여 sys를 서명합니다.

서명한 파일을 OSRLOADER로 실행하면 됩니다.

(만약 동작하지 않는다면 DSEO에서 Enable Test Mode로 설정하시기 바랍니다.)

EMET 5.0이 얼마 전에 출시 되었습니다.

새로운 기능이 추가되었는데 ASR이라고 하는 기능입니다.

EMET 공식 가이드 문서를 참조하자면,

Attack Surface Reduction (ASR)
The Attack Surface Reduction (ASR) helps reduce the exposure of applications at risk for attacks by blocking the usage of specific modules or plugins within the target application. For example, EMET can be configured to prevent Microsoft Word from loading the Adobe Flash plugin, or, with the support of Security Zones, can be configured to prevent Internet Explorer from loading the Oracle Java plugin on a website in the Internet Zone while continuing to allow Java on Intranet Zone websites. The mechanism simply prevents DLL loading on a per-process base, and it essentially adds the benefit to “killbit” specific modules in specific applications.

쉽게 설명하면 공격을 사전에 방지하기 위해 DLL을 안 올라가게 한다, 그런 내용 같습니다.

 

<iexplore.exe에서 차단하는 DLL - 제품마다 다르게 적용됩니다>

 

자바 취약점의 경우도 우선 애플릿을 호출하기 위해 JRE 모듈을 브라우저에 끌어다 씁니다.

 

위 그림은 자바 애플릿을 호출하기 위한 DLL 목록입니다.

ASR기능을 사용하면,

 

위에서 설정한 jp2iexp.dll 이 로드되지 않습니다. 즉, 애플릿이 동작하지 않게 됩니다. 

악성 페이지에서 효과가 있는 것을 확인 하였습니다.

혹 정상 페이지의 경우 어떻게 되는지 궁금해서 확인을 해보았습니다.

 

http://www.javakode.com/applets/03-color/

오진이 발생하면서 애플릿이 동작하지 않습니다. 위의 사이트에서 정상적으로 애플릿이 동작하는 것을 확인할 수 있습니다.

MS에서 자바 애플릿은 필요가 없다고 생각한 것인지, 아니면 확인하지 못한 오진인지 문득 궁금해집니다.

 

ARM에서 버퍼 오퍼플로우가 어떻게 동작하는지 알아보기 위해 라즈베리파이에서 테스트 해보았습니다.

아래 소스를 gcc 로 옵션 없이 컴파일 합니다.

두 개의 함수를 gdb로 디스어셈블링 하였습니다.

scanf 후의 스택입니다. 0x8440에 Breakpoint 가 걸렸습니다.

sp+0xc 지점이 복귀 주소입니다. ARM은 x86과는 다르게 retn 명령어가 없고 pop을 하면서 pc 레지스터에 바로 주소를 씁니다.
pc는 eip와 같다고 보시면 됩니다.

그러면 이번에는 AAAABBBBCCCCDDDD 를 입력합니다.

덮어 씌워졌습니다.

0x8440 실행 후의 스택입니다. 0x43434343은 r11에, 0x44444444는 pc에 들어갑니다.

crash 직후의 레지스터입니다.

이번에는 zip 파일 퍼징하는 방법을 포스팅 하겠습니다.

아래의 사이트를 토대로 테스트 하였습니다.

http://www.flinkd.org/2011/07/fuzzing-with-peach-part-1/

준비물 :

1. Peach 2.3.9 

작성된 plt이 2.3.9에서만 동작합니다. 이 버전에는 소스만 제공됩니다.

http://sourceforge.net/projects/peachfuzz/files/Peach/2.3.9/

2. Actice Python 2.7

http://www.activestate.com/activepython/downloads

3. 의존성 패키지

아래 파일들을 전부 다운로드합니다.

http://svn.code.sf.net/p/peachfuzz/svn/branches/Peach2.3/dependencies/py2.7-win32/

4. zip plt 파일

zip을 퍼징하기 위한 plt 파일입니다. 주요 경로 및 프로그램을 자신의 환경에 맞게 설정합니다. 

zip.xml

5. 타겟 프로그램

command 명령이 가능한 zip 프로그램을 사용하시면 됩니다.

 


 

Active Python 2.7을 설치 후, 의존성 패키지의 install.bat을 실행하면 패키지들이 한 번에 설치됩니다.

퍼징 전에 유효성 검사를 해보아야 합니다.

peach.bat -t zip.xml

만약 SEED 문제가 발생한다면,

<Strategy class="rand.RandomMutationStrategy" switchCount="1500" maxFieldsToMutate="7"/>

해당 라인을 제거해 주세요. 제거하지 않아도 퍼징은 가능합니다.

Peach Fuzzer는 매우 강력한 퍼징 프레임워크 입니다.

wav/pdf/doc 등 다양한 포맷을 퍼징할 수 있고, WinDbg와 연동하여 상세한 크래쉬 정보를 볼 수 있습니다.

본 포스팅을 따라하면  wav 포맷을 가지고 간단한 퍼징을 실습할 수 있습니다.

우선 .NET Framework 4와 WinDbg, Peach Fuzzer가 필요합니다.

저는 Windows 7 x64에서 테스트했으므로 WinDbg x64를 설치하였습니다. 정상 동작하기 위해선 반드시 64비트엔 64비트 디버거를 설치하여야 합니다.

다운로드 ::

Debugging Tools for Windows (x86) version 6.12.2.633

Debugging Tools for Windows (x64) version 6.12.2.633

http://peachfuzzer.com/ 


퍼징 준비물

1. WAV 플레이어

퍼징 타겟은 Sounder라는 프로그램으로 Command Line에서 재생 가능하고, 심플합니다.

http://www.elifulkerson.com/projects/commandline-wav-player.php

 

2. WAV 파일

퍼징을 위한 wav 샘플 파일입니다. 다운로드 한 뒤 Sounder.exe와 같은 디렉토리에 넣어주세요.

 sample.zip

 

3. PIT 파일

퍼징을 하기 위한 Peach XML 파일입니다. 뮤테이션 할 오프셋을 설정할 수 있습니다. 다운로드 한 뒤 peach 설치 디렉토리에 넣어주세요.

1.xml

본 파일을 다운로드 한 뒤 약간의 수정이 필요합니다.

<Data fileName="d:\\sample_fuzzer\\sample.wav"/>

<Param name="CommandLine" value="D:\\sample_fuzzer\\sounder.exe fuzzed.wav" />

<Param name="Executable" value="D:\\sample_fuzzer\\sounder.exe"/>

<Param name="WinDbgPath" value="C:\\Program Files\\Debugging Tools for Windows (x64)" />

빨간 부분을 자신의 경로에 맞게 수정합니다. WinDbgPath는 두 군데 수정하여야 합니다. 


이제 퍼징을 할 차례입니다.

cmd.exe를 관리자 권한으로 실행합니다.

d:\peach> peach 1.xml

이렇게 한 줄 입력하면 퍼징이 시작됩니다.

 

각종 로그는 logs 디렉토리에서 확인하면 됩니다.

자세한 튜토리얼은 http://old.peachfuzzer.com/v3/TutorialFileFuzzing.html 에서 확인하실 수 있습니다.

 

 

 

 

MiniFuzz는 마이크로소프트에서 제작된 파일 퍼징 툴입니다.

매우 단순한 툴이므로, ZIP/HWP/PDF 와 같이 정형화 된 파일 포맷을 가진 경우는 이 툴을 사용하여 만족할 만한 결과를 얻어낼 수 없습니다.

가장 심플한 버퍼 오버플로우 유발 예제를 가지고 퍼징 테스트를 해보겠습니다.

 target_fuzzer.exe

return 시 address를 덮어 씌우는 전형적인 BoF 예제입니다.

 

사용방법은 매우 간단합니다.

Browse로 타겟을 선택하고, Start Fuzzing 을 누르면 끝입니다.

퍼징 중 Access Violation을 잡아내면 로그가 남게 됩니다.

 

보다 자세한 사항은 로그파일에 남게 되는데, 그림의 logs 디렉토리에 로그가 남습니다.

 

<클릭하면 확대됩니다>

크래쉬 당시의 다양한 정보가 로그에 남게됩니다. 어느 파일을 가지고 퍼징을 했는지도 기록되는데요, 해당 샘플은 crashes 디렉토리에 있습니다.

 

 

 

 

windows 용으로 포팅한 zlib입니다. 

zlib-x86..zip

visual studio 2010 에서 테스트 하였습니다.

zconf.h 와 zlib.h 을 프로젝트에 추가한 후,

소스에 zlib.h을 include합니다.

라이브러리를 추가 종속성에 추가해 주세요.

 

빌드 시 아래와 같이 에러가 나는 경우, 특정 기본 라이브러리 무시에서 libcmt.lib을 입력해 주세요.

 

 

ADB Shell에 연결하여 작업하다 보면 불편한 점이 한두개가 아닙니다. vi는 원활하지 않고, 데이터를 폰에 넣으려면 adb push 를 입력해야 한다던지.. 여러모로 불편한 사항이 많습니다.

그러나 SSH 서버 앱을 설치하면 PC에서 바로 Putty 등을 이용해 ADB 없이 폰에 접속할 수 있습니다.

(마치 리눅스 서버에 접속하듯이 말이죠)

우선 스토어에서 SSH 서버 앱을 다운받습니다.

추천 앱 : Rooted SSH / SFTP 데몬

 

설정에서 아이디와 패스워드를 설정하면 끝입니다.

파일 업/다운로드는 SFTP 를 이용하면 됩니다.

추천 프로그램 : WinSCP

 

 

 터미널은 기존에 사용하시던 것 사용하시면 됩니다.

 

ARM에서의 조건 분기에 대해 알아보기 위해 아래와 같은 소스를 컴파일하여 실행하였습니다.

바이너리를 IDA에 넣고 디스어셈블링하였습니다.

strcmp 로 문자열을 비교해서 같으면 플래그가 0으로 설정됩니다.

BNE는 Branch Not Equal 로 같지 않으면 분기하라는 의미인데 0x852C를 보면 wrong이 있습니다.

주목해볼만한 것은 인텔 아키텍쳐와는 다르게 바이트 단위가 아닌 줄 단위로 분기한다는 것입니다.

이것은 4바이트 단위로 해석하는 arm-mode 특성 때문에 가능한 것이지요. 개인적으로 정말 마음에 듭니다.

무조건 right 이 출력되게 아래와 같이 코드 패칭을 하였습니다.

해당 라인에 NOP를 의미하는 0x90(2021.1.28 수정 : arm에서의 nop는 0x00 0xf0 0x20 0xe3입니다.)을 채워넣었습니다. 패치 후에 해당 부분이 정상적으로 디스어셈블링이 되지 않았습니다.

(gdb) break *0x8510       
Breakpoint 1 at 0x8510
(gdb) r
Starting program: /data/local/s/a.out
input password : wwwww

Breakpoint 1, 0x00008510 in main ()
(gdb) set *0x8514 = 0x90909090

0x00008514 <+244>:   90 90 90 90     umullsls        r9, r0, r0, r0   <--- NOP

(gdb) c
Continuing.
right!
[Inferior 1 (process 10098) exited with code 012]

잘 동작 합니다.



아래의 소스를 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

 

 

+ Recent posts