Win10内核池子那点破事儿,
笑喷了也得整明白!😂

老铁们,敲黑板划重点啦! 这玩意儿,简单说,就是Windows 10内核里管内存那块地儿,叫“池子”,最近来了个“新管家”(Segment Heap),看着挺唬人,其实还是留了点“后门”!这帮哥们儿(作者)贼精,发现了新管家的“小毛病”,研究出一套骚操作:用个针尖儿大的堆溢出(就是不小心多写了几个字节),就能一路火花带闪电,从“看大门的权限”(Low Integrity)直接窜到“玉皇大帝级别”(SYSTEM)!你说逗不逗?!

一、开场白:啥是内核池子?跟澡堂子有区别不?

你想啊,皇上(内核)干活也得用纸笔吧?这“池子”(Pool)就是皇上专用的文具库。以前这库房规矩森严,跟外面老百姓用的(用户态堆)完全两码事,贼讲究。

但是!微软这大哥寻思:“哎呀妈呀,管两套库房太累了!” 于是乎,从Win10的19H1版本开始,就把外面那套比较溜的“分段管理法”(Segment Heap)也搬进宫里了。想着省事儿呗?

可皇宫毕竟是皇宫,要求多啊!所以这套管理法到了内核里,也得稍微改改,加点“御用专供”的规矩。这文章就专门扒拉这“御用版”管理法(主要瞅x64),看看有啥能让人钻空子偷笔墨纸砚的地方。

以前想偷东西(攻击),主要打那个叫POOL_HEADER的“户口本+门牌号”的主意。每个内存块前面都有这玩意儿,写着“这块地儿多大”、“谁家的”等等。后来微软学精了,加了各种防盗措施:比如搞了个叫ExpPoolQuotaCookie“祖传动态防伪码”,把户口本里一个重要信息(ProcessBilled指针,记着是谁花的钱)给加密了,让你瞎改就露馅!还有NonPagedPoolNx,相当于“不仅锁门窗,还把电闸给拉了”,不让你在里面点灯(执行代码)。

关键转折点,笑点来了:换了新管家后,虽然小块地(小于0xFE0字节)前面还挂着老式的“户口本”(POOL_HEADER),但里面好多格子都空着,跟领导办公室似的,就差没落灰了! 这不就给咱这种“闲杂人等”可乘之机了嘛!

二、新管家(内核分段堆)长啥样?跟外面有啥不一样(猫腻)?

这新管家大体上还是按“大小个儿”分活儿:

而且还不止一个库房,按内存种类分了好几个(NonPagedPoolPagedPoolNonPagedPoolNx等),每个都有个_SEGMENT_HEAP结构体管着。

(此处应有图,但咱想象一下:一堆箭头指来指去,各种框框套框框,跟迷宫似的,看着就头大。这就是那些啥_HEAP_啥_CONTEXT的内部构造图,反正挺复杂。)

还有个叫“动态旁路列表”(Dynamic Lookaside)的小灶,专门给刚用完、大小合适的“盘子碗筷”(0x200到0xF80字节的块)留着,下次有人要,直接递过去,省得再去后厨洗了,快!

重点来了,扶好下巴:POOL_HEADER(老户口本)现在混成啥样了?

虽然新时代了,但小于0xFE0的小块地前面,还得挂着这老古董。但它现在:

特别注意,容易整懵圈的:缓存对齐(CacheAligned)这档子事

你要是申请时非要“住单间”(标记了CacheAligned),系统一看,哎呀,现在这地址不凑巧,不是“门牌号带8”的吉利数(没对齐),咋办?

它可能就得在前面给你垫块砖(加个padding),甚至给你挂俩门牌号(两个POOL_HEADER)! 第一个在最前面,第二个在对齐的那个“吉利”地址。只有第二个门牌号才写着“单间已对齐”,而且它里面的PreviousSize(隔壁老王)那里偷偷记着:“我离真正的大门牌有多远”

(想象图:一个内存块,前面一个正常门牌,走了几步,又挂个特殊门牌,上面写着“豪华单间,已对齐”,还带个小纸条指回大门牌。)

三、开整!利用这些“破绽”搞点“小动作”!

以前那户口本查得严,跟政审似的,不好下手。现在新管家来了,老户口本好多地方不看了,机会这不就来了嘛!

招式一:改BlockSize,“小捣蛋”变“大破坏”!

你要是有个小溢出,能把隔壁邻居(下一个块)户口本上的BlockSize(第3个字节)给偷偷改成个大胖子尺寸(比如改成大于0x200的值)。等这个邻居搬家(释放)时,系统一看:“嚯!这家伙是个大户啊!” 就把它扔进了“大户专用回收站”(对应大小的旁路列表)。

然后,你再去跟系统说:“给我来个大户型的房子!” 系统可能就把那个实际上“小身板儿、假户口”的邻居房给你了!你往里搬家具(写数据),以为地方大着呢,结果“咣当”一下,家具全怼到隔壁老老王家去了!(二次溢出,而且溢出范围可能大得多!) 把个只能捣乱3字节的小坏蛋,升级成能祸害将近4KB的大魔王!刺激不?

招式二(核能级骚操作):玩坏PoolType里的CacheAligned位!—— “指鹿为马,乾坤大挪移!”

还记得要“住单间”时可能有俩门牌号,搬家(释放)时系统要用第二个门牌上的小纸条(PreviousSize)算回大门牌地址这事儿不?

天大的BUG来了(当时是):在老的管理制度下,算回去之后,系统还得反复核对:“这纸条写的距离对不?俩门牌加起来大小对不?” 等等一堆检查。但换了新管家后,这些保安大哥估计集体去隔壁村打麻将了,岗哨全空了!检查都没了!(作者写文章时就是这样,可能后来补上了,但当时就是个大漏勺!)

这就好比啥?你改了人家的门牌号,人家系统还傻乎乎地信了!

咱的操作贼简单粗暴:

  1. 找个机会(堆溢出),把下一个倒霉邻居(叫“倒霉蛋B”)的户口本(POOL_HEADER)给改了:把PoolType(第4字节)那里,强行打上“我要住单间!”CacheAligned)的标记。
  2. 同时,把PreviousSize(第1字节,就是那个记距离的小纸条)改成咱想要的一个数,比如0x15
  3. 注意别手抖,别把PoolType里的PoolQuota(记账)标记给打开了,不然那个加密狗(Cookie检查)会出来咬人,容易翻车。

然后,坐等好戏!等“倒霉蛋B”搬家(释放)的时候:

  1. 系统:“收到搬家申请!哎?这户标记了要住单间!得特殊处理!” 于是去找那个小纸条(PreviousSize)。
  2. 系统拿出计算器(误),按公式 真正该拆的房子地址 = 倒霉蛋B的地址 - 小纸条上的数 * 16 来算。
  3. 因为小纸条被咱改成0x15了,它一算,算到了 倒霉蛋B地址 - 0x150 这个十万八千里外的地方!
  4. 系统一脸懵逼,但还是坚信计算结果:“嗯!这儿才是真正的老宅子!” 然后就跑去那个地址,读那里(可能是咱事先精心伪造的)“假户口本”,按假户口本说的去拆迁!

结果就是:系统在错误的地方,拆了一栋根本不存在(或者不是它该拆)的房子! 这个“错误的地方”被咱精准地控制在“倒霉蛋B”前面,最多能偏0xFF0字节远!这简直是“指哪儿拆哪儿”的神技啊!

四、实战演练:手把手教你从“青铜”变“王者”(还带搞笑解说)!

假设咱现在手里有个“小喷枪”(堆溢出漏洞),能把口水(数据)喷到隔壁邻居家的门牌号上(覆盖头几个字节,至少能改PoolTypePreviousSize)。目标:从“扫地僧”(Low Integrity)直接飞升成“天庭扛把子”(SYSTEM权限)!

核心战术:就用上面那个“指哪儿拆哪儿”的绝技,在咱能控制的“肇事者A”(有漏洞的块)家里,挖个坑,伪造一个“地下室”(Ghost Chunk),然后通过控制这个地下室里的“租客”(特殊对象),一步步拿到“透视眼+隔空取物”(任意地址读写或减法)的超能力,最后把自己的身份证(Token)改成皇帝的,大功告成!

具体步骤,笑点密集,跟上别掉队:

第1步:排兵布阵,撒豆成兵!先得搞“堆喷射”(Heap Spraying),说白了就是往内核里疯狂塞东西,跟超市抢打折鸡蛋似的,把地方占满,想办法让“肇事者A”和“倒霉蛋B”成为“脸贴脸、门对门”的好邻居。(具体咋喷?后面再唠叨两句)
第2步:开喷!把口水喷到邻居家!对着A一顿猛灌数据,让它“噗”地一下溢出来,正好把B家的门牌号(POOL_HEADER)给糊上一层“修正液”PoolType改成“要单间!”,PreviousSize改成咱算好的值(比如0x15),让它指向A家客厅某个位置(比如A地址 + 0x30)。
第3步:在A家里挖坑埋雷!先把A“请出去”(释放),然后火速再“请回来”(重新申请),这次回来的时候,让它肚子里装满咱的“私货”(比如用PipeAttribute对象填满)。关键是在刚才算好的客厅位置(A地址 + 0x30),偷偷放一个假的门牌号(Fake POOL_HEADER)。这假门牌得装得像:大小写个0x21(这样拆迁队以为是0x210的房子,方便咱后面回收利用),户口性质跟A一样,但千万别写“要单间”和“要记账”,不然容易穿帮。
第4步:释放B,召唤“地下幽灵”!现在,把“倒霉蛋B”给“扔进垃圾桶”(释放)。拆迁队(系统)来了,一看B家被改过的门牌:“哎呀,要单间!得用小纸条算算老宅在哪!” 拿着咱伪造的小纸条一算,直接算到A家客厅里去了! 然后它就按客厅里那个假门牌说的,在A家里(从A地址 + 0x30开始)拆了个0x210大小的“违章建筑”!这块被错误拆掉的地儿,就成了“地下幽灵”(Ghost Chunk),它神不知鬼不觉地覆盖了A和B的一部分。这块“三不管”的幽灵地皮会被扔进0x210大小的“待回收垃圾桶”(旁路列表)。
(想象图:B被释放,但箭头指向A内部,A里面一部分被标记为“已释放”,这就是那个幽灵!)
第5步:逮住幽灵,严刑拷问!赶紧跟系统说:“我要个0x210大小的房子!” 系统很可能就把刚才那个“幽灵地皮”给你了!咱用能读写的“私货”(比如PipeAttributePipeQueueEntry)把它占了。现在,像审贼一样读里面的东西,就能知道这幽灵的确切地址,还能看到它压在下面的“秘密”(被覆盖的内核数据),里面说不定就有内核地址、甚至那个烦人的“防伪码”(ExpPoolQuotaCookie)!情报到手!芜湖~
第6步:彻底策反幽灵里的“租客”!再把“肇事者A”踢出去、请回来一次。然后,又把“幽灵地皮”申请回来。这次,用咱的“私货”对象的功能(比如PipeAttribute能改数据指针),把幽灵地皮里那个“租客”(特殊对象)的关键“联系方式”(比如数据地址、大小指针)全都改成咱家的电话号码(指向用户态内存)! 以后内核再想找这个“租客”办事,实际上就得听咱用户态的遥控了!“报告长官,我们已经成功打入敌人内部!”
第7步:开启“天眼通”,想看啥看啥!有了这个被咱完全控制的“内鬼”,内核就成了透明的!想读哪里读哪里!先找到内核(ntoskrnl.exe)的家门在哪(基地址),然后把那个“防伪码”(Cookie)的值也抄下来,再找到咱自己这个进程的“档案”(EPROCESS结构地址),还有最重要的“身份证”(TOKEN)地址。跟开了全图挂似的!
(想象图:一个管道对象,它的数据指针被改成指向用户区,内核读它时,数据从用户区来。)
第8步:搭个“假衙门”,准备“偷梁换柱”!在内核里找个没人注意的角落(比如用能写数据的PipeQueueEntry对象),搭一个假的“县衙”(Fake EPROCESS)。再把“肇事者A”请出去、请回来。然后又把“幽灵地皮”弄回来。这次,在幽灵地皮的入口(A地址 + 0x30)再换个新的假门牌,内容是:大小还是0x21,户口性质这次要打上“需要查户口!”(PoolQuota)的标记,户主(ProcessBilled)那里填上一个“加密电报”假衙门地址 ^ 防伪码 ^ 幽灵地皮地址。(用刚才偷来的情报算好)。
第9步:再次释放幽灵,触发“隔空打牛”!又双叒叕把“幽灵地皮”扔垃圾桶!系统一看:“嚯!要查户口!” 立刻去读户主信息,用防伪码和地址解密,解出来咱那个假衙门的地址! 然后系统就傻乎乎地跑去假衙门,试图给它登记簿上的某个数字(配额计数器)减去一大笔(BlockSize * 0x10,也就是0x210)。因为衙门是假的,它实际上就在咱指定的那个内存地址上,咣咣来了个“大额减法”! 咱就得到了在任意地方减任意数(0到0xFF0之间,16的倍数)的超能力!“乾坤大挪移第七层,成了!”
第10步:修改身份证,登基!光有超能力不行啊,得用在刀刃上!Windows提权这事儿吧,光改一个权限位(比如调试权限SeDebugPrivilege)还不够,因为它身份证(Token)上有俩本本:一本叫Privileges.Present“理论上你能有啥本事”),一本叫Privileges.Enabled“实际上你现在开了哪些挂”)。默认情况下,咱这“青铜”号,Present本本上就没写着能开“调试挂”。所以,得用两次“隔空打牛”: 这样,俩本本上都有“调试挂”了!“双证齐全,持证上岗!”
(想象图:一个令牌结构,里面的两个64位数字被红笔圈出,箭头指向它们,旁边写着“- 0xXXXXXX”。)
第11步:收工,放礼花!最后一步,堂堂正正地调用系统API AdjustTokenPrivileges,跟系统说:“爷要开调试挂!” 系统一看,俩本本都许可了,没毛病!立马给你开了!有了这挂,整个系统都是你的后花园了! 找个倒霉的系统进程(比如winlogon.exe),塞个小纸条(shellcode)进去,让它给你弹个SYSTEM权限的命令行窗口!砰!搞定!原地起飞,俯视众生!可以开始在系统里瞎溜达了! 🎉🎉🎉

用啥“神器”来搞这些骚操作?

上面老说用“私货”、“特殊对象”,到底是啥玩意儿能这么好使?得满足:咱能随便申请随便扔、大小能凑合(至少到0x210)、还得能帮咱偷看(任意读)甚至动手脚(任意写)。

“堆喷射”(Heap Spraying)听着挺玄乎,咋整?

“堆喷”说白了就是“占坑技术哪家强?” 目的是让咱的“肇事者A”和“倒霉蛋B”能手拉手做邻居。具体咋占坑,得看A多大,掉进哪个“车间”(LFH还是VS)。

总之,“堆喷”就是个“又得有耐心,又得有点小运气”的活儿。

五、总结陈词,可以下班了不?

这篇破文章,絮絮叨叨地给咱揭了揭Win10 19H1之后,内核那嘎达内存池的“底裤”。虽然请了新管家(Segment Heap),但那个老掉牙的“户口本”(POOL_HEADER)还在小户型前面赖着不走,而且好多规矩都忘了,以前的保安(检查)也撤了岗,这就给了咱这些“爱搞事”的一条“溜门撬锁”的新路子。

咱看到了咋利用这变化,特别是那个“指鹿为马还不用负责任”的“缓存对齐”漏洞(对齐混淆攻击),只需要一丢丢“手滑”(极小的堆溢出),就能像玩多米诺骨牌一样,一步步搞出“隔空打牛”(任意地址减法),最后把自己的平民身份证换成龙袍!(拿到SYSTEM权限)。

这招儿,对付那些看起来“人畜无害”的小漏洞,说不定就能“四两拨千斤,蚂蚁拱大象”!Windows安全这场“猫鼠游戏”,真是越来越刺激,越来越好玩(对攻击者来说)!