当钓鱼邮件找上门:一条Chrome全链漏洞的拆解之旅

admin 2026-06-18 05:15:05 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细拆解了一条利用已知公开漏洞构建的Chrome全链攻击路径。该攻击链始于一封带有HTML附件的钓鱼邮件,通过CVE-2018-17463(V8类型混淆漏洞)实现渲染器RCE,接着利用CVE-2019-5826(IndexedDB中的UAF)完成沙箱逃逸,最终结合一个WindowsLPE漏洞,成功在目标主机上获取SYSTEM权限。文章深入分析了各环节的技术细节与利用原语,旨在帮助读者理解从点击恶意附件到完全控制主机的完整过程。 综合评分: 85 文章分类: WEB安全,渗透测试,红队,漏洞分析,二进制安全


cover_image

当钓鱼邮件找上门:一条Chrome全链漏洞的拆解之旅

幻泉之洲

2026年6月11日 09:48 北京

在小说阅读器读本章

去阅读

一封邮件、一个HTML附件、一次点击。账号被盗,资金转走。这不是科幻小说。Hackyboiz团队用两条真实的全链漏洞,演示了从Chrome渲染器到Windows SYSTEM权限的完整攻击路径。本文是第一篇,讲的是CVE-2018-17463 + CVE-2019-5826这条链。

好久没发研究文章了。

后台其实一直在忙。Hackyboiz进入了新的赛季,团队内部也在重新整编。这个团队最早是2018年建起来的,几年下来,不少当初的核心成员因为各种原因各奔东西。最近ogu123接手了队长,我们折腾了不少改动,准备搞点新事情。倒霉的是,我们的X账号莫名其妙被扬了,想更新动态都没地方说,挺憋屈的。

就在这段过渡期,我们内部搞了个研究项目。名字叫——

项目目标很直白:搞两条Chrome的全链漏洞出来。

说实话,真正的0day全链对我们来说还有点早。大家都有正经工作,也没人在这个级别的利用上有太多经验。所以我们把目标定成了用已知的1day漏洞搭链子。结果呢?比预想中快太多了。😄

原计划二月到六月,四个月搞定。四月中旬就已经收工了。☠️

二月三月大量时间花在啃之前的1day案例和复现现成的利用上。四月集中搭链子,互相分享发现和技巧。学到的玩意儿确实不少,觉得值得写成系列文章记下来。

这次是来真的——预计要写十篇。

这事怎么开始的

说回原点,整件事的起点是对V8类型混淆漏洞的兴趣。去年我花了不少时间钻研V8 RCE利用。本来计划接着写V8堆沙箱逃逸作为后续,结果越搞越大,直接变成了这个项目。所以等那篇文章的人别急,这个系列里会补上。

学得越深,一个问题就越冒出来:“好,那链这部分到底怎么弄?”

差不多同一时间,我开始觉得AI写利用的能力进步得有点吓人。如果趋势确实往那个方向走,也许更值钱的技能不是找漏洞,而是把漏洞武器化。与其追新的0day,不如踏踏实实练好利用原语。于是我把g0ngjae、banda和ji9umi拉进了项目——当然是“绑架”来的。

我们本来还有更野的链式想法,想搭更花哨的东西。但现实生活总会来搅局。先到这吧,把学到的东西整理出来。

项目目标

找新0day不在这次的范围里。我们要的是把公开的1day研究透,亲手复现,亲手串起来。

一条典型的Chrome利用链分三大块(有时两块也够):

  • 渲染器攻破
  • Chrome沙箱逃逸
  • Windows本地提权(LPE)

每条链都会在系列文章里细讲。😇

第一条链:Matteo Malvica在NDC Security 2025上讲的CVE-2018-17463(渲染器RCE) + 冯震和刘耕铭在Black Hat 2019上讲的CVE-2019-5826(沙箱逃逸)。Windows提权环节,我们原打算随便找一个能用的LPE漏洞。结果banda自己找到了一个0day。可惜等我们做完的时候,那个漏洞已经修了,没法提交漏洞披露。那就直接用在我们链的最后一步吧。

第二条链让我第一次看到的时候头皮发麻——

Theori那篇VM到宿主机全链的研究,我们选了CVE-2023-3079、CVE-2023-21674和CVE-2023-29360这三个Chrome和Windows部分来复现,VM逃逸那块不碰。感谢Theori的研究员发了那么棒的文章。

写这个系列的一个动力是:我在学漏洞链式利用的时候,网上有很多疑问根本找不到详细讨论。所以这次不藏着,从看懂漏洞到搭出能用的链,过程全拿出来。

点击钓鱼邮件的HTML附件,到底发生了什么?

某个傍晚,你作为基金经理刚结束一天的工作,收件箱弹出一封新邮件。

“我知道你昨晚干了什么。”

主题行意味不明。就这一句话。

你想不起来昨晚有什么异常——事实上你睡得很早。但这话让你心里不舒服。好奇心占了上风。

打开HTML附件,加载画面闪了一下,然后是白页。什么都没发生。

“又是个垃圾邮件。”

关掉标签页,写完日报,处理完剩下的邮件,深夜离开办公室。你觉得什么都没发生。你以为。

第二天早上踏进公司,气氛不对。电话响个不停,人跑来跑去,同事脸上的表情说明出大事了。

“客户账户的钱没了!”

“有大额国际电汇!”

“日志显示,最后一次登录是从你的账户……”

你脑子一片空白。你什么都没干。没装软件。没输密码。没批过任何交易。你只是打开了一个邮件里的HTML附件。

那个HTML文件在Chrome里打开的时候,到底干了什么?

跟着攻击链一步一步走。

Chrome全链漏洞概览

Chrome是桌面和移动端用得最多的浏览器之一。

用户量大,漏洞影响就大。同时Chrome本身就是个极复杂的软件。现代网页要求浏览器管理大量内存、复杂的对象图,还要协调渲染引擎、JavaScript引擎、网络栈这些组件。把Chrome漏洞利用成功,攻击者能拿到任意内存访问甚至在浏览器进程里执行代码。而且Chrome和操作系统交互很深——文件访问、图像处理、网络、各种系统服务。拿下浏览器,往往是拿下整个系统的第一步。

换句话说,Chrome不仅用户喜欢,安全研究员和攻击者也喜欢。

Windows上很多传统桌面应用,一个利用打穿就能直接代码执行。Chrome不一样,它设计了好几层防御。就算控制了渲染器进程,攻击者还得面对浏览器沙箱这道坎。

这些防御意味着现代的Chrome漏洞利用通常是多阶段的。一条全链大致长这样:

整个系列里,我们会把每一层拆开来看。

链1: CVE-2018-17463 + CVE-2019-5826 + Windows LPE 1day

这段视频演示了我们在这个项目里做的第一条全链。

打开一个HTML文件,就能在宿主机上拿到SYSTEM权限执行命令。

这条链把V8 RCE(CVE-2018-17463)、沙箱逃逸(CVE-2019-5826)和我们团队开发的Windows LPE漏洞串在一起。从一个HTML文件到最后SYSTEM权限,一条完整的利用链。我们确实挺得意的。😄

部分链基于公开研究,现在简单介绍,技术细节后面文章细讲。这条利用链在Windows 11的VMware里,Chrome 69.0.3497.100上完成开发和测试。

CVE-2018-17463: V8类型混淆 – 渲染器RCE

攻击者控制的输入肯定是从渲染器里跑的HTML、CSS和JavaScript开始的。

处理过程中触发类型混淆。利用对象重叠,我们可以构建相对读写原语,一般叫addrOf和fakeObj。有个要点:这个漏洞出现的时候,V8堆沙箱还没引入。🫡

漏洞允许我们覆盖backing-store指针,所以可以利用两个ArrayBuffer对象构建任意地址读写(AAR/W)。有了这些原语,就可以泄露出WebAssembly实例对象的地址和它的RWX跳表指针。但原来的addrOf原语依赖破坏重叠的属性存储,可能不小心搞坏目标对象,降低利用稳定性。

我们换了个更稳的办法。给ArrayBuffer对象加一个out-of-line属性,让这个属性指向目标对象。读属性存储里对应的偏移,就能泄露对象指针,不直接破坏对象字段。稳定性好太多了。

稳定的AAR/W到手之后,后面就顺了。创建一个WebAssembly实例,V8会分配RWX跳表。泄露跳表地址,用任意地址写原语把shellcode写进去。

然后,调用函数就行……

计算器弹出来了。

弹计算器意味着RCE达成。

关于利用原语的说明

学漏洞利用的时候我发现,讲清楚“原语是什么”的资料不多。我的理解是:原语是一种能力,可重复、可控制、能作为下一阶段利用的输入。某种意义上,原语就是利用API。

addrOf返回对象地址。AAR从任意地址读内存。AAW往任意地址写数据。每个原语暴露一个有用的能力,组合起来搭出完整的利用链。

但别高兴太早。上面的演示是关闭Chrome沙箱跑的。正常Chrome环境下,光靠渲染器RCE弹不出计算器,因为渲染器被沙箱限制着。🫠

拿到渲染器RCE之后呢?

答案是,从Chrome精心打造的监狱里逃出去。

CVE-2019-5826: IndexedDB中的UAF – 沙箱逃逸

V8堆沙箱和Chrome沙箱是两个完全不同的东西。简单说,V8堆沙箱是渲染器进程内部的内存隔离机制,Chrome沙箱是进程隔离机制,限制渲染器和其他不可信进程的权限。后面文章会细讲。

渲染器RCE到手之后,可以用PEB walking(用gs:[0x60]找到kernel32.dll和VirtualAlloc等API)绕过ASLR。接下来讨论里,默认ASLR已经被干掉了。

目标:CVE-2019-5826,Black Hat USA 2019上公布的漏洞。影响Chrome的IndexedDB实现,use-after-free(UAF)条件可以用于逃逸Chrome沙箱。先尝尝味道,不做详细的PoC分析。😋

IndexedDB的大量实现在浏览器进程里。这是个内置的NoSQL数据库,允许网页应用在客户端存大量结构化数据。渲染器进程通过API和IndexedDB交互,实际请求由浏览器进程处理。

漏洞出在异步请求队列和Chrome强制清理逻辑的交互上。某些情况下,这两个机制会脱节,导致数据库对象在被销毁后还能被引用。说白了就是经典的对象生命周期管理bug,最终是个UAF,让攻击者能够和一个已经死了的对象打交道。

第1步. UAF

IndexedDB内部维护一个pending_requests队列来管理数据库操作。数据库对象有引用计数,生命周期最终由ref_count机制决定。

按下面的操作序列可以搞出悬空指针:

  • Open(“db1”, 1);
  • Open(“db1”, 2);
  • DeleteDatabase(“db1”, force_close=True);
  • AbortTransactionsForDatabase();

根子在DeleteDatabase(force_close=true)和请求处理逻辑的交互上。ForceClose()遍历connections_里的每个活跃连接并逐一关闭。有趣的事发生在最后一个连接关闭时。一旦最后一个连接移除,挂起的Open(v2)请求被处理,启动版本升级事务。这个操作创建新的升级连接并插回connections_数组。问题是原来的关闭循环已经在收尾阶段,新插入的连接从未被正确清理。

因此Close()里的这个条件不再为真:

connections_.empty() && !active_request_Copy

条件不满足,factory_->ReleaseDatabase()调用被跳过。数据库条目留在了database_map_里,即使对象最终应该被释放。之后AbortTransactionsForDatabase()中止版本变更事务,对应IndexedDBDatabase对象的引用计数降到零,对象被销毁。但database_map_的清理被跳过了,map里还存着指向已释放对象的指针——悬空指针。

悬空指针被访问时,利用拿到经典UAF条件。

第2步. 堆喷

利用悬空指针就要做堆喷。IndexedDB有个createObjectStore()函数,正常存key_path字符串。但我们渲染器RCE都拿到了,可以绕开Blink的类型检查,往这个接口里塞任意字节。

于是我们构造一个0x170字节的payload,模拟IndexedDBDatabase对象的布局,满堆喷。试了大概50次,喷的对象就稳稳落在释放的槽位里了。🤤

第3步. 堆地址泄露

堆喷成功后,释放的0x170字节槽位被我们伪造的IndexedDBDatabase对象重新占用。关键字段布局从+0x150的pending_requests_ deque开始。

在构造喷的payload时,我们把deque初始化为全空。同时在+0x148的active_request_放一个非空标记值(0x4142434445464748)。这两个字段够用了。

接着通过悬空指针再发一个Open(“db1”)请求。执行流走到IndexedDBDatabase::AppendRequest(),调用pending_requests_.push_back(…)。这时候两件事发生:

  1. 因为active_request_非空,AppendRequest()排完队立刻返回,ProcessRequestQueue()和OpenRequest::Perform()不会执行,Chrome不会解引用假对象而崩溃。
  2. 队列操作触发堆分配。deque初始buffer_=NULL, capacity_=0,第一次push_back()强制base::circular_deque分配backing buffer。新分配堆缓冲区的地址即时写入buffer_,也就是假对象的+0x150处。

一次Open()调用就能填充字段,但我们发了十五次。deque扩了几次容,每次重分配都更新buffer_指向最新的backing store。十五次插入后,deque容量十六个槽位,缓冲区约16×8=0x80字节。这个尺寸下阶段用得上。

剩下问题怎么把指针读回来。表面看简单:读喷的key_path数据,取+0x150内容。但有个坑。走正常Commit()→SuccessDatabase路径,指针会消失。因为Commit()序列化元数据到LevelDB,回调从磁盘重建全新的元数据对象。key_path字符串是新创建的副本,不是原来喷的内存。

拆成两个阶段。先提交版本变更事务,让喷的缓冲区保持存活。再发一个Open(“db_reuse”, version=0),返回还留在内存里的元数据。关联的key_path对象仍然引用原来的缓冲区——就是重新占用悬空指针槽位的那块。回调检查每个key_path是否+0x148==0x4142434445464748。标记匹配,喷成功了。回调读+0x150处的八个字节,验证像有效堆指针,存为leaked_heap_addr_。我们拿到IndexedDB线程堆上deque backing buffer的地址。

不用瞎猜堆位置了,有了一个实实在在的堆锚点。

第4步. AAF(任意地址释放)原语

pending_requests用circular deque实现。deque需要扩容时,不是直接往相邻内存长,而是分配新backing buffer,复制内容,释放旧buffer。这个行为配合假对象极其好用。

把active_request_设为非空后,配置pending_requests:

pending_requests

  • buffer_ = leaked_heap_addr_ ← 要释放的地址
  • capacity_ = 1, begin_=end_ = 0(空,待扩容)

这个状态下,任何导致deque扩容的操作最终会释放buffer_存的地址。换句话说,完全控制pending_requests内容,就能释放任意地址——任意地址释放(AAF)原语。

触发它,先回收堆泄露阶段用过的槽位。DeleteDatabase(“db_reuse”, force_close=true)释放key_path字符串,0x170字节槽位回到free list。第三步用这个槽位泄露堆地址,现在再用它构造AAF payload。Open(“db_aaf”)开新升级事务,CreateObjectStore()堆喷。喷的string16缓冲区重新占用释放的0x170字节槽位。这次假对象的pending_requests deque被故意做成看上去空但需要扩容。

通过悬空指针再发Open(“db1”),AppendRequest()->push_back()。deque没有可用容量,ExpandCapacityIfNecessary()触发。deque分配新backing buffer,释放旧的。关键在旧buffer指针不再是合法deque分配——是我们选的目标地址(buffer_ = leaked_heap_addr_)。

于是扩容逻辑实际执行了free(leaked_heap_addr_)。在攻击者视角,一个任意地址被释放了。

active_request_有非空值(比如0x101),AppendRequest()排完队立刻退出,不往下走ProcessRequestQueue(),避免了假请求解引用无效对象导致的异常,浏览器继续正常运行。AAF原语到手:释放选定地址,浏览器进程不崩。

第5-6步. 分配假Factory → 浏览器RCE

能任意地址释放,游戏基本结束了。

下一步通过覆盖虚函数表劫持控制流。Chromium浏览器进程里有个IndexedDBFactory对象,负责管理每个源的IndexedDB数据库。渲染器调用Open()或DeleteDatabase()这些API,浏览器进程最终会走到这个Factory对象的虚方法调用。这让Factory成为极好的目标。

用AAF原语,把Factory对象回收为攻击者控制的数据,覆盖它的vtable。对应虚方法被调用时,执行流跳进我们的ROP链。

就这样,一个始于渲染器进程的悬空指针,最终导致浏览器进程权限下执行WinExec()——在Chrome沙箱之外。

执行利用大概需要40秒到一分钟。比我们希望的长,但利用做了多次堆喷和好几轮堆操作,立刻执行不总能实现。我个人挺想花时间提升可靠性和速度,但那就是未来的事了。😅

另外小麻烦是可用ROP空间有限。PoC里做栈迁移后调用WinExec()弹计算器。串联Windows LPE阶段时,空间不够直接把整个payload塞进去。于是我们把LPE打成一个单独的exe(a.exe),通过浏览器进程ROP链启动它。能用,但多半有更干净的办法。谁有更好的技术,请一定联系[email protected]。🙇‍♂️

Windows LPE载荷(by banda)

V8利用和沙箱逃逸两个阶段重度依赖Chrome内部细节——对象布局、Mojo IPC、浏览器进程对象生命周期——内核LPE阶段在整个链里相对独立一些。在中等完整性级别拿到执行权之后,内核部分基本可以单独做。

考虑哪种内核LPE适合这条链的时候,我决定搞一个Hackyboiz之前没深入讲过的bug模式。结果是基于MDL滥用模式的Windows LPE载荷。

这篇先大致介绍漏洞模式和利用原语,用一个已修不能再用的例子。完整分析留到系列尾部专门的内核LPE文章。😁

尝鲜:MDL Unregister Double-Free

MDL(Memory Descriptor List)是个内核结构,描述哪些物理页对应一个虚拟地址缓冲区。我遇到的bug模式也是MDL漏洞的老朋友:生命周期管理问题。典型MDL生命周期:分配、锁定页面、映射、解除映射、释放。出问题的地方常是驱动把MDL对象存在槽位表或队列里,允许多个请求引用同一个MDL指针。多线程可能同时和同一个对象交互,制造生命周期竞争。

[unregister flow] input.va  ↓ find matching slot  ↓ slot = &a1[6 * idx]  ↓ slot state clear  ↓ if slot[16]:    user_va = slot[15]    mdl     = a1[2 * (3 * idx + 9)]

   MmUnmapLockedPages(user_va, mdl)    IoFreeMdl(mdl) else:    mdl = slot[17]    if mdl:        IoFreeMdl(mdl)

注册/注销设计尤其容易出现这类问题。分析的模式里,unregister过程查注册时存的映射虚拟地址和MDL指针。槽位查找→槽位清空→MmUnmapLockedPages()→IoFreeMdl()的顺序没有足够同步。

两个线程可能竞跑unregister路径,释放同一个MDL两次——double-free。

Windows 22H2内核驱动利用原语

确认MDL bug模式后,下一步是在Windows 22H2上把它变成有用的原语。这种漏洞有几种利用方式,这条链的目标是:让中等完整性级别进程和一个有漏洞的内核驱动交互,最终操作当前进程的token对象。

大体流程:

  • 泄露当前进程TOKEN对象的内核地址
  • 确定token里权限字段的位置
  • 利用MDL漏洞拿到一个指向目标内核内存的可写用户态映射
  • 通过映射修改token的权限位图
  • 启用SeDebugPrivilege之类的权限
  • 访问SYSTEM拥有的进程,起一个SYSTEM级shell

这补上了全链的最后一块:Windows LPE载荷,在浏览器攻破和沙箱逃逸都成功后提权。

所有漏洞串在一起,一个恶意网页或钓鱼附件最终可以拿到SYSTEM权限,拿下整台Windows。

⛰️Wipeload系列开篇

以上就是Wipeload系列的第一篇。

更多文章正在写了。虽然没有订阅按钮、点赞和通知铃,但还是希望大家保持关注。😇

第二步会回到类型混淆漏洞,聊聊传统AAR/W原语怎么在现代V8缓解下演变成Caged AAR/W。2020年左右V8引入堆沙箱,渲染器利用难度暴涨。好消息是防御方乐了,漏洞开发者就没那么开心了。🫠

之后会深入Theori研究里三个漏洞的链,以此为基础探索现代Chrome利用的核心概念。不只是漏洞本身,还会讲原语、绕过缓解、浏览器内部、利用技术,全部串在一起。

读完整个系列,能完全搞懂Chrome全链漏洞是什么吗?

这就是目标。我们会尽全力做到。

参考

https://i.blackhat.com/USA-19/Wednesday/us-19-Feng-The-Most-Secure-Browser-Pwning-Chrome-From-2016-To-2019.pdf

https://blog.kiprey.io/2020/10/CVE-2019-5826/


参考资料

[1] https://hackyboiz.github.io/2026/06/06/OUYA77/Wipeload_step1/en/


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:幻泉之洲 《当钓鱼邮件找上门:一条Chrome全链漏洞的拆解之旅》

暗网快讯【20260611】139期 网络安全文章

暗网快讯【20260611】139期

文章总结: 本期暗网快讯汇总了2026年6月11日前后暗网及泄露论坛中出现的多起数据泄露事件,涉及美国海军高级军官PII及位置数据、意大利娱乐场所、尼日利亚与多
常见的5个电子证据误判 网络安全文章

常见的5个电子证据误判

文章总结: 文档分析了电子证据在司法实践中的5个常见误判:截图不等于有效证据、删除数据仍可恢复、录音证据需满足合法性完整性、转账记录需明确法律性质、电子证据不比
评论:0   参与:  0