익스플로잇 실습 / 튜토리얼
BlazeDVD 6.1 – Stack Based Buffer Overflow
By WraithOfGhost
BlazeDVD_v6.1를 이용하여 간단한 스택 기반 오버플로우에 대하여 알아 볼 것이다. 익스플로잇을 위해 구성된 환경은 아래와 같다.
- Windows XP Professional SP3 – KOR - Python 2.7.10
- 사용 모듈 : pydbg
일단 BlazeDVD 프로그램은 plf 파일을 읽어서 해당 파일로부터 재생 목록을 가져와서 실행하는 프로그램이다. 수많은 ‘A’ 문자로 이루어진 plf 파일을 생성한 뒤 크래시 유발을 시도 한다.
위와 같이 파이썬 코드를 작성한 뒤 실행하면 ‘A’ 문자 500개로 이루어진
“BlazeExploit.plf” 파일이 생성된다.
어플리케이션에서 해당 파일을 열면 오류가 발생한다. 상세 정보를 보면 Offset 값이 “41414141 = AAAA” 이기 때문에 스택 (버퍼) 오버플로우가 발생한 것으로 추정할 수 있다. 성공적으로 크래시가 발생하였기 때문에 좀 더 상세한 분석을 위해 디버거를 이용할 것이다.
위와 같이 파이썬의 pydbg 모듈을 이용하여 간단한 디버거를 작성한다.
참고로 해당 디버거를 실행하기 전에 반드시 BlazeDVD 어플리케이션을 먼저 실행해야 한다. 또한 디버거의 작성 방법은 먼저 콜백 함수를 선언하고 pydbg 클래스에 등록하는 것인데, 세부적인 동작 방식은 아래와 같다.
1. 프로세스 이름 설정
작업관리자에서 보여지는 해당 어플리케이션 이름
2. 콜백 함수 설정
이벤트가 발생했을 때 호출될 콜백 함수 선언
3. crash_bin 객채 생성
크래시 발생 시 메모리 상태와 레지스터 값을 확인할 수 있는 crash_bin 객체 생성
4. 크래시 발생 시 상태 값 저장
크래시 발생 주소 근처의 어셈블리 명령어, 레지스터, 스택, SEH 상태 기록 from pydbg import *
from pydbg.defines import * import struct
import utils
processName = "BlazeDVD.exe" # 1
dbg = pydbg() def handler_av(dbg): # 2
crash_bin = utils.crash_binning.crash_binning() # 3
crash_bin.record_crash(dbg) # 4
print crash_bin.crash_synopsis() # 5
dbg.terminate_process() # 6
for(pid, name) in dbg.enumerate_processes(): # 7
if name == processName: print "[information] dbg attach:" + processName dbg.attach(pid) print "[information] start dbg" dbg.set_callback(EXCEPTION_ACCESS_VIOLATION, handler_av) # 8 dbg.run()
5. 상태 값 출력
크래시 발생 시점에 저장한 상태 값을 화면에 출력
6. 프로세스 종료
버퍼 오버플로우가 발생된 프로세스 종료
7. 프로세스 아이디 추출 및 프로세스 핸들 구하기
앞서 설정된 이름으로 프로세스 아이디를 도출한 뒤 아이디에 해당하는 핸들을 구해서 pydbg 클래스 내부에 저장
6. 콜백 함수 설정
이벤트를 등록하고, 크래시가 발생했을 때 호출될 콜백 함수 설정
이제 BlazDVD를 실행하고 위 소스를 실행하여 디버거를 실행한다. 그리고 이전에 만든 BlazeExploit.plf 파일을 열면 어플리케이션이 즉시 종료되면서 디버거는 다음과 같은 메시지를 출력한다.
메시지는 크게 4가지 영역으로 나뉜다. 첫 번째는 오류 메시지인데 어떤 스레드가 어떤 종류의 오류를 발생시켰는지 보여준다.
쓰레드 : 3488 / 오류 종류 : access violation (메모리 접근 오류)
두 번째는 CONTEXT DUMP 영역인데 프로세스가 실행 중에 사용하는 레지스터 정보를 보여준다.
EIP:41414141 ~ ESP +14:41414141
세 번째는 disasm 영역인데 오류가 qkftood한 주소 주위의 어셈블러 명령을 보여준다.
0x41414141 unable to disassemble => 올바른 명령어가 아님
마지막 영역은 SEH unwind 영역이다. SEH는 Structured Exception Handling의 약자로 윈도우즈 OS에서 제공하는 구조적 예외 처리 기법이다. 링크를 추적 해서 예외 처리와 관련된 정보를 출력한다.
0012f5c8 ~ push ebp
위 영역에서 지금 rhkstlaadlT게 봐야 하는 부분은 세 번째, CONTEXT DUMP 영역이다. 입력 값을 조정해가면서 EIP / ESP 레지스터에 저장되는 데이터의 변화를 봐야 한다.
현재 크래시를 유발하는 파일은 “A” 문자가 500개 이기 때문에 몇 번째 바이트에서 EIP 레지스터가 덮어 씌어지는지 알 수 없다. 따라서 일정한 규칙을 가진 패턴을 입력 데이터로 사용하여 데이터의 흐름을 추적해야 한다.
위에서 만든 문자열을 한줄로 만들어서 다시 크래시를 유발하는 파일을 생성 한다.
위와 같이 파이썬 소스를 작성한 뒤 실행하면 일정한 문자열 규칙을 가지는
“blazeExpl2.plf” 파일이 생성 된다. 이전과 동일하게 “디버거 실행” ->
“BlazeDVD 실행” -> “blazeExpl2.plf 파일 오픈” 과정을 진행하면 디버거는 아래 사진과 같은 정보를 출력한다.
디버거를 보면 이번에는 EIP 레지스터 값이 “35623561” 임을 알 수 있다.
해당 값을 아스키 코드로 변환한 값은 “5b5a” 인데, 주소는 입력한 방향과 반대로 들어가기 때문에 제대로 된 문자열은 “a5b5”가 된다. 따라서 이전에 생성한 문자열에서 “a5b5”가 시작하는 위치를 찾아야 한다.
에디트 플러스에서 이전에 생성한 문자열을 놓고 “a5b5”를 검색한 뒤 제일 앞쪽에 커서를 두면 해당 문자열이 몇 번째 글자에서 시작하는지 보여준다.
“a5b5” 문자열은 261번째 글자에서 시작되고 있다.
위와 같이 새로운 크래시 유발 파일을 생성하는 소스를 작성한다. 먼저 처음 260 바이트까지는 오버플로우를 유발하는 데이터이고, 그 다음 4 바이트는 EIP 주소이다.
이전과 동일하게 해당 파일을 열면 디버거는 위와 같은 정보를 출력한다.
예상한 것처럼 EIP 주소는 “42424242 = BBBB”로 되어 있다. 그리고 ESP 레지스터를 보면 테스트 문자열의 일부인 “i0” 으로 시작하는 값이 저장되어 있다. 이전과 마찬가지로 “i0” 글자가 몇 번째에서 시작하는지 찾아야 한다.
i0 글자는 17번째에서 시작하고 있다. 따라서 앞의 16 바이트를 임의의 값으로 채우고 shellcode를 작성하면 간단하게 원하는 행위를 실행시킬 수 있다.
따라서 클시 유발 파일은 위와 같은 형식으로 이루어지게 된다. 단 NOP 코드를 실제로는 최소 25개로 세팅해야 실제 계산기가 실행된다. 이유는 올리 디버거를 이용하여 상세 분석을 해야 하기 때문에 본 문서에서는 건너 뛴다.
자 그럼 이제 EIP 주소를 세팅해야 한다. 위 사진에서 보면 “jmp esp” 명령어 주소를 사용한다고 되어있는데 이유는 이전 디버거 정보에서 본 것처럼 esp 레지스터에 사용자가 입력한 값이 들어가기 때문이다. 즉, shellcode를 esp 레지스터에 넣어주고 eip 레지스터에 “jmp esp” 주소를 넣어주면 shellcode가 실행된다.
“findjmp.exe” 프로그램을 이용하여 jmp esp 주소를 쉽게 찾을 수 있다. 해당 프로그램은 2개의 인자를 받는데, 첫 번째는 명령어를 찾을 대상 DLL 파일이고 두 번째는 레지스터 이름이다. 대부분의 프로그램에서 일반적으로 사용되는
“user32.dll” 파일을 이용하여 꽤 많은 “jmp esp / call esp” 명령어가 찾아지 는데 가장 처음에 보여지는 “jmp esp” 주소를 이용하면 된다.
그럼 이제 크래시를 유발하고 계산기를 띄우는 파일을 생성하는 코드를 작성 하면 된다. 참고로 계산기를 띄우는 shellcode는 칼리 리눅스를 이용한 것이다.