2017-09-21 11:44:42
阅读:147次
点赞(0)
收藏
来源: theevilbit.blogspot.com
作者:天鸽
译者:天鸽
预估稿费:200RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
前言——Windows内核池喷射
我计划写这一系列文章已经有一年了,我对这些东西做过一些研究,但是常常会忘记,也没有正确地写下笔记。我想探索的是什么样的对象可以用于内核池喷射,主要关注于它们消耗多少空间,它们拥有什么属性,并且到最后写了一段代码,将池孔大小(pool hole size)作为输入,然后动态地告诉我们在这里使用什么样的对象能够控制池分配以达到溢出的目的。我思考这一问题已经有段时间了,让我再一次感到兴奋的是当我看到了来自 @steventseeley 的一条推特(https://twitter.com/steventseeley/status/904443608216031233),我决定把它写出来,另一方面也是我自己的兴趣使然。
微软有一个很棒的内核对象列表,我们可以通过调用用户模式功能来创建内核对象,尽管它不是很完整,但仍然是一个很好的开始:https://msdn.microsoft.com/library/windows/desktop/ms724485(v=vs.85).aspx另一个重要的链接,是一个我们能够找到 pool tag(译者注:poll tag是调试时用于识别块的字符)的列表,当查看池分配时也是十分方便的:https://blogs.technet.microsoft.com/yongrhee/2009/06/23/pool-tag-list/
在这篇文章中,我想探讨的是 Mutex 对象,由于笔记不完整它让我很头疼,我会讲到如何找到并查看对象在池空间中的实际分配以及对象本身的一些基本信息。
第一部分——环境配置及对象大小
在设置环境的部分,我们并不需要进行远程内核调试,进行本地内核调试就足够了,因为我们只探索内核内存,目前也就不需要设置任何断点。所以本地调试足以满足我们的需求。为此,我们需要在 Windows 中启用调试:
bcdedit-debugON之后,需要重启机器。一旦完成,我们就可以启动 WinDBG,转到内核调试,然后选择本地调试。我建议使用下面的命令来加载符号:
.symfix .reload此时我们就可以探索内核内存空间了。我将使用 Win7 SP1 x86 进行演示。
首先,如果我们希望获得更全面的对象列表,可以使用下面的命令:
!object\ObjectTypes我们会得到这样的东西:
lkd>!object\ObjectTypes Object:8be05880Type:(851466d8)Directory ObjectHeader:8be05868(newversion) HandleCount:0PointerCount:44 DirectoryObject:8be05ed0Name:ObjectTypes HashAddressTypeName ------------------- 00851d6900TypeTpWorkerFactory 851466d8TypeDirectory 018521a838TypeMutant 851cddb0TypeThread 03857c7c40TypeFilterCommunicationPort 048522a360TypeTmTx 05851d29c8TypeController 068521d0b8TypeEtwRegistration 07851fe9c8TypeProfile 8521a9c8TypeEvent 851467a0TypeType 098521cce0TypeSection 8521a900TypeEventPair 85146610TypeSymbolicLink 10851d69c8TypeDesktop 851cdce8TypeUserApcReserve 1185221040TypeEtwConsumer 8520e838TypeTimer 128522a8f0TypeFile 851fe838TypeWindowStation 14860a6f78TypePcwObject 158521ceb0TypeTmEn 16851d2838TypeDriver 188521db70TypeWmiGuid 851fe900TypeKeyedEvent 19851d2900TypeDevice 851cd040TypeToken 2085214690TypeALPCPort 851cd568TypeDebugObject 218522a9b8TypeIoCompletion 22851cde78TypeProcess 238521cf78TypeTmRm 24851d6838TypeAdapter 26852139a8TypePowerRequest 85218448TypeKey 28851cdf40TypeJob 308521c940TypeSession 8522a428TypeTmTm 31851cdc20TypeIoCompletionReserve 328520e9c8TypeCallback 3385894328TypeFilterConnectionPort 348520e900TypeSemaphore这是一个可以在内核空间中分配的对象的列表。我们可以通过查看更多的细节来探索几个关于它们的重要属性。使用命令 dt nt!_OBJECT_TYPE <object> 我们可以得到关于某对象(object)的更多细节,比如句柄总数等。但是最重要的是 _OBJECT_TYPE_INITIALIZER 结构的偏移量,它将给我们带来极大的方便。让我们看看它为我们提供了 Mutant 对象的哪些我想要的信息:
lkd>dtnt!_OBJECT_TYPE8521a838 +0x000TypeList:_LIST_ENTRY[0x8521a838-0x8521a838] +0x008Name:_UNICODE_STRING"Mutant" +0x010DefaultObject:(null) +0x014Index:0xe'' +0x018TotalNumberOfObjects:0x15f +0x01cTotalNumberOfHandles:0x167 +0x020HighWaterNumberOfObjects:0xc4d7 +0x024HighWaterNumberOfHandles:0xc4ed +0x028TypeInfo:_OBJECT_TYPE_INITIALIZER +0x078TypeLock:_EX_PUSH_LOCK +0x07cKey:0x6174754d +0x080CallbackList:_LIST_ENTRY[0x8521a8b8-0x8521a8b8]然后阅读下 _OBJECT_TYPE_INITIALIZER:
lkd>dtnt!_OBJECT_TYPE_INITIALIZER8521a838+28 +0x000Length:0x50 +0x002ObjectTypeFlags:0'' +0x002CaseInsensitive:0y0 +0x002UnnamedObjectsOnly:0y0 +0x002UseDefaultObject:0y0 +0x002SecurityRequired:0y0 +0x002MaintainHandleCount:0y0 +0x002MaintainTypeList:0y0 +0x002SupportsObjectCallbacks:0y0 +0x002CacheAligned:0y0 +0x004ObjectTypeCode:2 +0x008InvalidAttributes:0x100 +0x00cGenericMapping:_GENERIC_MAPPING +0x01cValidAccessMask:0x1f0001 +0x020RetainAccess:0 +0x024PoolType:0(NonPagedPool) +0x028DefaultPagedPoolCharge:0 +0x02cDefaultNonPagedPoolCharge:0x50 +0x030DumpProcedure:(null) +0x034OpenProcedure:(null) +0x038CloseProcedure:(null) +0x03cDeleteProcedure:0x82afe453voidnt!ExpDeleteMutant+0 +0x040ParseProcedure:(null) +0x044SecurityProcedure:0x82ca2936longnt!SeDefaultObjectMethod+0 +0x048QueryNameProcedure:(null) +0x04cOkayToCloseProcedure:(null)这里告诉了我们两个重要的事情:
此对象被分配给的池类型 - 在这里是非分页池(NonPagedPool)
功能偏移(这在实际的漏洞利用部分十分重要)
之后,我们来分配一个 Mutant 对象,然后在内核池中找到它。我写了一段简短的 python 代码来实现它:
fromctypesimport* fromctypes.wintypesimport* importos,sys kernel32=windll.kernel32 defalloc_not_named_mutex(): hHandle=HANDLE(0) hHandle=kernel32.CreateMutexA(None,False,None) ifhHandle==None: print"[-]Errorwhilecreatingmutex" sys.exit() printhex(hHandle) if__name__=='__main__': alloc_not_named_mutex() variable=raw_input('Pressanykeytoexit...')这段代码将为我们分配一个未命名的 mutex,打印出它的句柄并等待退出。我们需要等待着,所以我们可以在 WinDBG 中探索内核池,如果进程退出,则mutex 将被破坏。这里我得到了一个 0x70 的句柄,我们来看看怎样在 WinDBG 中找到它。首先我需要找到 Python 进程并切换上下文,可以这样做:
lkd>!process00python.exe PROCESS86e80930SessionId:1Cid:0240Peb:7ffd4000ParentCid:0f80 DirBase:bf3fd2e0ObjectTable:a8282b30HandleCount:41. Image:python.exe lkd>.process86e80930 Implicitprocessisnow86e80930第一条命令将为我们找到进程,第二条命令将切换上下文。然后我们查询句柄,就能得到内存中对象的地址:
lkd>!handle70 PROCESS86e80930SessionId:1Cid:0240Peb:7ffd4000ParentCid:0f80 DirBase:bf3fd2e0ObjectTable:a8282b30HandleCount:41. Image:python.exe Handletableata8282b30with41entriesinuse 0070:Object:86e031a8GrantedAccess:001f0001Entry:8c0d80e0 Object:86e031a8Type:(8521a838)Mutant ObjectHeader:86e03190(newversion) HandleCount:1PointerCount:1这样我们就可以找到池的位置,细节如下:
lkd>!pool86e031a8 Poolpage86e031a8regionisNonpagedpool 86e03000size:98previoussize:0(Allocated)IoCo(Protected) 86e03098size:90previoussize:98(Allocated)MmCa 86e03128size:40previoussize:90(Allocated)Even(Protected) 86e03168size:10previoussize:40(Free)Icp *86e03178size:50previoussize:10(Allocated)*Muta(Protected) PooltagMuta:Mutantobjects 86e031c8size:40previoussize:50(Allocated)Even(Protected) 86e03208size:40previoussize:40(Allocated)Even(Protected)它显示在非分页池中需要 0x50 字节大小的位置。无论我们重复多少次,都是 0x50。看起来确实如此。如果我们将之前的代码放在一个循环中,我们可以看到它能够工作,并且可以进行很棒的堆喷射:
851ef118size:50previoussize:50(Allocated)Muta(Protected) 851ef168size:50previoussize:50(Allocated)Muta(Protected) 851ef1b8size:50previoussize:50(Allocated)Muta(Protected) 851ef208size:50previoussize:50(Allocated)Muta(Protected) 851ef258size:50previoussize:50(Allocated)Muta(Protected) 851ef2a8size:50previoussize:50(Allocated)Muta(Protected) 851ef2f8size:50previoussize:50(Allocated)Muta(Protected) 851ef348size:50previoussize:50(Allocated)Muta(Protected) 851ef398size:50previoussize:50(Allocated)Muta(Protected) 851ef3e8size:50previoussize:50(Allocated)Muta(Protected) 851ef438size:50previoussize:50(Allocated)Muta(Protected) 851ef488size:50previoussize:50(Allocated)Muta(Protected) 851ef4d8size:50previoussize:50(Allocated)Muta(Protected) 851ef528size:50previoussize:50(Allocated)Muta(Protected) 851ef578size:50previoussize:50(Allocated)Muta(Protected) 851ef5c8size:50previoussize:50(Allocated)Muta(Protected) 851ef618size:50previoussize:50(Allocated)Muta(Protected) 851ef668size:50previoussize:50(Allocated)Muta(Protected) 851ef6b8size:50previoussize:50(Allocated)Muta(Protected) 851ef708size:50previoussize:50(Allocated)Muta(Protected)那么如果我们给 Mutex 取一个名字,会有什么样的变化?这是另一段 Python 代码:
defalloc_named_mutex(i): hHandle=HANDLE(0) hHandle=kernel32.CreateMutexA(None,False,"Poolsprayingiscool"+str(i)) ifhHandle==None: print"[-]Errorwhilecreatingmutex" sys.exit() printhex(hHandle)我给它传递了一个参数,因为如果我们要使用它来进行喷射,这将是很重要的,因为我们不能创建两个具有相同命名的 mutex。
一旦我们创建了 mutex,并且我们遵循与之前一样的逻辑,就可以看到其中有点不同:
*871d39e8size:60previoussize:30(Allocated)*Muta(Protected) PooltagMuta:Mutantobjects 这一次它需要 0x60 字节,这也是一致的。我们也可以做同样的喷射,但具有不同的大小。这里有一些重要的东西。如果我们看一看池分配,就可以看到这是一个从 pool chunk 的头位置偏移 0x20 的指针,指向 Mutex 的名字:lkd>dd871d39e8 871d39e8040c0006e174754d0000000000000050 871d39f800000000000000009a06fb38002e002e 871d3a08aab50528000000000000000200000001 871d3a1800000000000a000e86e0bd8099a4fc07 871d3a280008bb0200000001871d3a30871d3a30 871d3a3800000001000000000000000001d10000 871d3a48040b000c6d4d6956b299b8c89a087020 871d3a58a8246340000000000000000085d4f0b0 lkd>ddaab50528 aab50528006f0050006c006f0073002000720070 aab5053800790061006e00690020006700730069 aab5054800630020006f006f0020006c006f0031 lkd>dSaab50528 006c006f"????????????????????????????????" 006c00af"????????"
我的 WinDBG 看起来是不想打印出对象的名字,但是如果你以十六进制格式查看它的 UNICODE,它就是我们给 Mutex 的命名。如果我们检查这个字符串的存储位置:
lkd>!poolaab50528 Poolpageaab50528regionisPagedpool aab50000size:a8previoussize:0(Allocated)CMDa aab500a8size:28previoussize:a8(Free)3.7. aab500d0size:28previoussize:28(Allocated)NtFs aab500f8size:28previoussize:28(Allocated)MmSm aab50120size:38previoussize:28(Allocated)CMnbProcess:86ef6760 aab50158size:100previoussize:38(Allocated)IoNm aab50258size:38previoussize:100(Allocated)CMDa aab50290size:38previoussize:38(Allocated)CMNb(Protected) aab502c8size:28previoussize:38(Allocated)MmSm aab502f0size:20previoussize:28(Allocated)CMNb(Protected) aab50310size:60previoussize:20(Allocated)Key(Protected) aab50370size:20previoussize:60(Allocated)SeAt aab50390size:d8previoussize:20(Allocated)FMfn aab50468size:28previoussize:d8(Allocated)CMVa aab50490size:30previoussize:28(Allocated)CMVa aab504c0size:60previoussize:30(Allocated)Key(Protected) *aab50520size:38previoussize:60(Allocated)*ObNm PooltagObNm:objectnames,Binary:nt!ob可以看到它在分页池中!之后我们还会回顾这里,但在这里先透露一些东西:我们可以使用命名的 Mutex 在分页池区域(paged pool area)中创建自定义大小的分配,大小取决于我们给出的名称。这对于在分页池中进行喷射是非常有用的。
第二部分——使用pykd编写脚本
正如上一部分中讲到的,获得对象实际大小的过程是相当简单的,但是如果我们需要获得很多对象大小的时候,这将是一个繁重的手工作业,因此为了避免浪费太多时间,此过程应该被自动化执行。手动操作几次当然是有好处的,特别是对初学者而言,但是更多的重复就没有意义了。那么我们如何编写 WinDBG 脚本?用 pykd!pykd 是 WinDBG 的一个很棒的 Python 扩展,它甚至允许在没有手动启动 WinDBG 的情况下编写脚本。
第一件事就是安装 pykd,这有时很让人头疼。它并不总是像听起来那么简单。如果我们下载预编译的版本,并将 pykd.pyd 文件放在 WinDBG 的 winext 目录下,可能是最简单的方法。请让 WinDBG、Python、VCRedict 和 pykd 的架构相同(x86 或 x64),这一点很重要。你也可以通过 PIP 来安装 pykd,但是我在尝试导入它的时候并没有成功。另外一定要使用最新版本的 Python (2.7.13),当启动 pykd 时,一些较旧的版本(如 2.7.9)会使 WinDBG 退出。至于那些更老版本的 Python(2.7.1),它曾经是可以工作的。但是你一旦这么做,它将成为一个非常强大的扩展。
于是我写了一个简单的函数来获取对象名称和句柄,并且会查找对象的大小。也许还有其他更优雅的解决方案,但下面的脚本已经可以满足我的需求:
deffind_object_size(handle,name): #findwindbg.exeprocess wp=dbgCommand('!process00windbg.exe') #printwp #extractprocess"address" process_tuples=re.findall(r'(PROCESS)([0-9a-f]*)(SessionId)',wp) ifprocess_tuples: process=process_tuples[0][1] print"Process:"+process #switchtoprocesscontext dbgCommand(".process"+process) #findobject"address" object_ref=dbgCommand("!handle"+h) object_tuples=re.findall(r'(Object:)([0-9a-f]*)(GrantedAccess)',object_ref) ifobject_tuples: obj=object_tuples[0][1] print"Object:"+obj #findpool pools=dbgCommand("!pool"+obj) #printpools #findsize size_re=re.findall(r'(\*[0-9a-f]{8}size:[]*)([0-9a-f]*)(previous)',pools) ifsize_re: printname+"objects'ssizeinkernel:"+size_re[0][1] #closehandle kernel32.CloseHandle(handle)第三部分——研究分析
脚本将会减轻我们寻找池大小分配的工作。有了这个我会查看下面的对象:
Event
IoCompletionPort
IoCompletionReserve
Job(已命名的和未命名的)Semaphore(已命名的和未命名的)
从这一点来讲,过程是非常简单的,我们只需要调用相关的用户模式函数,创建一个对象,然后检查大小。我为 WinDBG 创建了一个简短的脚本,它能够自动化创建上述的对象,并检查大小,最后把它们打印出来。我把脚本上传到了这里:
https://github.com/theevilbit/kex/blob/master/kernel_objects.py
使用方法:
1.启动 WinDBG
2.依次点击 Kernel debug -> Local
3.执行命令:.load pykd
4.命令:!py path_to_the_script
结果如下:
NotNamedMutexobjects'ssizeinkernel:0x50 NamedMutexobjects'ssizeinkernel:0x60 Jobobjects'ssizeinkernel:0x168 Jobobjects'ssizeinkernel:0x178 IoCompletionPortobjects'ssizeinkernel:0x98 Eventobjects'ssizeinkernel:0x40 IoCompletionReserveobjects'ssizeinkernel:0x60 NotnamedSemaphoreobjects'ssizeinkernel:0x48 NamedSemaphoreobjects'ssizeinkernel:0x58这样我们就得到了一套不错的可用于内核池喷射的对象。那么什么是“kex”,它在期望什么呢?在后面的文章中你将看到内核中更酷的东西。
也许我应该在这个系列的开始做一些解释。我想写一些脚本,让我们在进行 Windows 内核利用开发的时候更快,我的第一个脚本是用于内核池喷射的。另外,如果你从来没有看过关于内核池溢出的东西,也没有用过内核池喷射技术。那么可以阅读:
http://trackwatch.com/windows-kernel-pool-spraying/
http://www.fuzzysecurity.com/tutorials/expDev/20.html
现在我们已经有了一个包含内核对象大小的列表(该脚本也可以在其他平台上运行,尽管可能需要针对 x64 架构做一些修改),我们可以进行自动化的喷射和制造空隙(hole)了。如果我们知道需要多大的空隙的话。
基本上:
1.一旦我们分析了漏洞,就会知道驱动程序将在内核池中分配的对象或缓冲器的大小是多少。
2.我们需要控制该分配的位置,所以我们需要在池中准备一个给定大小的空隙,以便内核在那儿分配新对象。
3.如果我们知道大小,就可以简单地计算出什么类型的对象利于喷射,还有我们需要释放掉多少个该对象。
4.如果我们知道这些所有的东西,就可以进行内核喷射并制造空隙了。
我们需要知道该对象的信息和我们使用溢出覆盖的池头部的信息,之后我会回来再讲的,因为在制造空隙时并不需要这些。我可能会失败,也做了一些准备,我希望覆盖数据也可以自动生成。现在,我只想根据给定的大小去制造空隙。所以我写了一个脚本,可以用于这个目的(请注意,这里是为 Win7 SP1 x86 硬编码的):
https://github.com/theevilbit/kex/blob/master/spray_helper.py
它会让你输入想要的空隙的大小,然后进行喷射,释放空间并在 WinDBG 中显示该区域。还要注意的是,它仍是使用本地内核调试器,我们无法设置断点,所以存在竞争条件的问题,当我们使用 !pool 命令时,可以像其他内核进程一样在可用空间中分配。我仍然使用本地内核调试器的原因是对于目前阶段的演示来说更简单。当我做真实的利用演示时,就需要进行远程调试了,但在这里我可以直接进行演示。下面是输出:
lkd>!pyc:\users\csaby\desktop\spray_helper.py Givemethesizeoftheholeinhex:440 Process:8572bd40 Objectlocation:857e15f0 Poolpage857e15f0regionisNonpagedpool 857e1000size:40previoussize:0(Allocated)Even(Protected) 857e1040size:40previoussize:40(Allocated)Even(Protected) 857e1080size:40previoussize:40(Allocated)Even(Protected) 857e10c0size:40previoussize:40(Allocated)Even(Protected) 857e1100size:40previoussize:40(Allocated)Even(Protected) 857e1140size:40previoussize:40(Allocated)Even(Protected) 857e1180size:40previoussize:40(Free)Even(Protected) 857e11c0size:40previoussize:40(Free)Even(Protected) 857e1200size:40previoussize:40(Free)Even(Protected) 857e1240size:40previoussize:40(Free)Even(Protected) 857e1280size:40previoussize:40(Free)Even(Protected) 857e12c0size:40previoussize:40(Free)Even(Protected) 857e1300size:40previoussize:40(Free)Even(Protected) 857e1340size:40previoussize:40(Free)Even(Protected) 857e1380size:40previoussize:40(Free)Even(Protected) 857e13c0size:40previoussize:40(Free)Even(Protected) 857e1400size:40previoussize:40(Free)Even(Protected) 857e1440size:40previoussize:40(Free)Even(Protected) 857e1480size:40previoussize:40(Free)Even(Protected) 857e14c0size:40previoussize:40(Free)Even(Protected) 857e1500size:40previoussize:40(Free)Even(Protected) 857e1540size:40previoussize:40(Free)Even(Protected) 857e1580size:40previoussize:40(Free)Even(Protected) *857e15c0size:40previoussize:40(Allocated)*Even(Protected) PooltagEven:Eventobjects 857e1600size:40previoussize:40(Allocated)Even(Protected) 857e1640size:40previoussize:40(Allocated)Even(Protected) 857e1680size:40previoussize:40(Allocated)Even(Protected) 857e16c0size:40previoussize:40(Allocated)Even(Protected) 857e1700size:40previoussize:40(Allocated)Even(Protected) 857e1740size:40previoussize:40(Allocated)Even(Protected)你可以看到我们有 17 x 0x40,也就是 0x440 的空闲空间,没有必要去处理更多细节。我可以给出任何其他的大小,例如:
lkd>!pyc:\users\csaby\desktop\spray_helper.py Givemethesizeoftheholeinhex:260 Process:8572bd40 Objectlocation:87b2fe00 Poolpage87b2fe00regionisNonpagedpool 87b2f000size:98previoussize:0(Allocated)IoCo(Protected) 87b2f098size:90previoussize:98(Free).... 87b2f128size:98previoussize:90(Allocated)IoCo(Protected) 87b2f1c0size:98previoussize:98(Allocated)IoCo(Protected) 87b2f258size:98previoussize:98(Allocated)IoCo(Protected) 87b2f2f0size:98previoussize:98(Allocated)IoCo(Protected) 87b2f388size:98previoussize:98(Allocated)IoCo(Protected) 87b2f420size:98previoussize:98(Allocated)IoCo(Protected) 87b2f4b8size:98previoussize:98(Allocated)IoCo(Protected) 87b2f550size:98previoussize:98(Allocated)IoCo(Protected) 87b2f5e8size:98previoussize:98(Allocated)IoCo(Protected) 87b2f680size:98previoussize:98(Allocated)IoCo(Protected) 87b2f718size:98previoussize:98(Allocated)IoCo(Protected) 87b2f7b0size:98previoussize:98(Allocated)IoCo(Protected) 87b2f848size:98previoussize:98(Allocated)IoCo(Protected) 87b2f8e0size:98previoussize:98(Allocated)IoCo(Protected) 87b2f978size:98previoussize:98(Allocated)IoCo(Protected) 87b2fa10size:98previoussize:98(Allocated)IoCo(Protected) 87b2faa8size:98previoussize:98(Allocated)IoCo(Protected) 87b2fb40size:98previoussize:98(Free)IoCo(Protected) 87b2fbd8size:98previoussize:98(Free)IoCo(Protected) 87b2fc70size:98previoussize:98(Free)IoCo(Protected) 87b2fd08size:98previoussize:98(Free)IoCo(Protected) *87b2fda0size:98previoussize:98(Allocated)*IoCo(Protected) Owningcomponent:Unknown(updatepooltag.txt) 87b2fe38size:98previoussize:98(Allocated)IoCo(Protected) 87b2fed0size:98previoussize:98(Allocated)IoCo(Protected) 87b2ff68size:98previoussize:98(Allocated)IoCo(Protected)可以看到这个喷射正是我们想要的。请注意,这一次使用了不同的对象。如果你测试很多次,请尝试着使用一个会导致不同对象分配的数字,那样你就可以得到更整洁的输出。
另一个值得注意的事情是,这里制造的空隙不是 100% 可靠的,但我相信已经非常接近了。我做了如下几点:我使用 100000 个对象对内核进行喷射,然后释放掉中间的 X 个。这很可能会让它们一个接着一个,并在我释放掉它们的时候提供给我们需要的空间,为了演示自动化过程,这是最简单的。但如果像下面这样的话会更可靠:
我尝试制造多个空隙,并释放多个 X,它们可能是彼此相邻的。
有一种方法可以从内核中泄露对象的地址,并计算它们是否相邻,从而释放空间。这是最可靠的方法。
随着我的进步,我会在将来实现它,但是现在我采用了第一种方法。
是的,我使用 Python 编码,而不是 Powershell,仅仅是因为我不能在 PS 中写代码,但是我完全同意人们说的,在 PS 中实现会更有意义。
本文由 安全客 翻译,转载请注明“转自安全客”,并附上链接。
原文链接:https://theevilbit.blogspot.com/2017/09/pool-spraying-fun-part-1.html