FortiGate SSLVPN CVE-2024-21762漏洞利用分析

一、漏洞简介 FortiGate二月份发布版本更新,修复多个中高危漏洞,其中一个严重级别漏洞是SSL VPN的未授权越界写漏洞,漏洞预警称该漏洞可能被在野利用。本文将介绍笔者分析利用该漏洞,实现远程代码执行的过程。 二、漏洞分析 本文漏洞分析使用的环境是FGT_VM64-v7.4.2.F-build2571。 2.1 diff patch 对修复版本的二进制进行对比(7.4.2和7.4.3),分析发现修复代码位于函数sub_18F4980(7.4.2)。 分析该函数不难发现,该函数逻辑是读取HTTP POST请求的body数据。同时根据Transfer-Encoding请求头判断是按照chunk格式读取,还是根据Content-Length读取。根据控制流图对比结果,存在两处代码修改: 解析chunk格式时,调用ap_getline读取分块长度,检查ap_getline的返回值是否大于16,大于16认为是非法的chunk length。 读取chunk trailer时,写入\r\n的偏移line_off的赋值来源,修复前line_off的值来源于*(_QWORD *)(a1 + 744),修复后line_off为ap_getline的返回值。 继续向前回溯,可以找到*(_QWORD *)(a1 + 744)的值正是第一处校验的chunk length字段的长度 同时阅读代码可以得知,当chunk length字段经过hex解码后值为0时,就会进入到chunk trailer读取的逻辑。 2.2 触发越界写 经过对patch的分析,我们可以得到以下结论: 1、解析chunk时,如果chunk length字段hex解码后值为0,则开始chunk trailer的读取。 2、调用ap_getline读取chunk trailer后,会根据chunk length字段的长度向缓冲区中写入\r\n。 因此,如果chunk length字段传入很多个0,0的长度大于剩余缓冲区长度的1/2时,就会触发越界写入\r\n。通过调试可知目标缓冲区位于栈上(函数sub_1A111E0),偏移0x2028的位置保存了返回地址。如果在偏移0x202e的位置写入\r\n,当函数返回执行ret指令恢复rip时就会因地址非法产生崩溃。 Crash PoC: pkt = b"""\ GET / HTTP/1.1 Host: %s Transfer-Encoding: chunked %s\r\n%s\r\n\r\n""" % (hostname.encode(), b"0"*((0x202e//2)-2), b"a") ssock = create_ssock(hostname, port) ssock.send(pkt) ssock.recv(4096) 崩溃现场: 三、漏洞利用 通过分析漏洞成因可知,利用该漏洞可以实现栈上越界写\r\n两个字节,越界范围接近0x2000。由于写入的内容非常有限,无法通过直接劫持rip实现RCE。因此需要把目光放在栈上保存的内存指针上。 3.1 失败的尝试 比较容易想到的是劫持rbp,通过覆盖rbp的低字节,使rbp刚好指向可控的内存区域。当上一级函数返回执行leave ret指令时,就可以完全劫持rip。然而验证时发现即使覆盖了栈上的rbp,也无法劫持rsp和rip,甚至程序不会产生崩溃。继续向上回溯,找到sub_1A111E0的父函数sub_1A26040,该函数在返回时并没有调用leave ret来恢复rsp,而是直接add rsp, 0x18,因此无法达到预期的效果。...

2024年03月13日 · 1 分钟 · zbleet

Ghidra脚本编写:从IR到反编译C

一、前言 作为一个二进制安全从业人员,面对不同的安全分析工具已经是家常便饭,目前业界主流的分析工具是IDA,关于其的博客也有很多,因此这里不再赘述; Ghidra,作为能够与IDA分庭抗礼的一款开源二进制分析工具,相关的资料却不如ida那么丰富,因此这篇推文旨在增加大家对Ghidra的了解。 二、前置介绍 为了避免底层的繁杂,提高逆向工程、自动化分析的效率(最直观的体现就是,避免二进制安全从业人员直接操作不同处理器的不同汇编指令集),几乎每个二进制分析工具都有自己的中间表示(Intermediate Representation,IR),例如IDA的microcode,Binary Ninja的LLIL与MLIL。Ghidra的IR叫做P-Code,一条汇编指令可以直接翻译为一个或多个P-Code(Ghidra文档中称之为Raw-Pcode,顾名思义,原始P-Code,即没有任何附加分析的P-Code),在Raw P-Code的基础上,Ghidra会做一些最基本的数据流分析,并根据分析结果来丰富P-Code集合,Ghidra文档将由数据流等分析引入的P-Code成为Additional P-Code。 P-Code由地址空间、Varnode以及pcode 操作组成,其中地址空间用于抽象程序内部内存,用于表示遗传连续的访问空间,例如register地址空间代表程序的通用寄存器、constant地址空间表示程序的常量;Varnode则抽象单个寄存器或者某一内存地址(可以抽象的里面为某一个变量),例如常量0xdeadbeef或者"bin/bash"可以表示为constant地址空间内的常量varnode;pcode操作类似与机器指令,详细的内容可以参考https://github.com/NationalSecurityAgency/ghidra/blob/master/GhidraDocs/languages/index.html。 如下是一个例子,在函数代码某一处调用了execl,并传递了对应了参数,编译器会将其编译为对应的汇编代码(连续的LEA、MOV指令加CALL指令),在该汇编指令的基础上,Ghidra通过分析,将其转换为多条P-Code,以下只展示与函数调用直接相关的P-Code(CALL)。 针对Ghidra分析得到的 --- CALL (ram, 0x101180, 8) , (unique, 0x10000055, 8) , (unique, 0x1000005d, 8) , (unique, 0x1000004d, 8) , (register, 0x0, 8) , (const, 0x0, 8) 这样一条CALL指令,CALL即为pcode操作,表示一次函数调用,(unique, 0x10000055, 8) , (unique, 0x1000005d, 8) , (unique, 0x1000004d, 8) 分别表示不同的Varnode,用于抽象一次函数调用不同的实参;(ram, 0x101180, 8)则表示函数execl在程序中的位置,位于ram地址空间的0x101180处长度为8字节的区域中。 Ghidra的P-Code十分丰富(高达67种),以上只是一条简单的例子,详细的介绍参考https://github.com/NationalSecurityAgency/ghidra/blob/master/GhidraDocs/languages/html/pcoderef.html。 三、具体案例 上图为JuiletCWE数据集中存在命令注入漏洞的函数,从源代码层面很容易可以看出可控参数由"ADD"环境变量传入(通过getenv函数),最终在execl中被执行,其对应的数据流为getenv->strncat->execl。其对应的完整P-code指令如下图。这里只展示了与漏洞相关的P-Code指令: 从IR层面则很难去理解,下图标记出每一条CALL指令对应的函数名,可以看到,getenv函数的输出Varnode**(register,0x0,8),会直接作为strncat的参数传递,根据strncat的特性,(register,0x0,8)所抽象代表的变量会拼接到(register,0x8,8)代表的变量,从而存在一条数据流由(register,0x0,8)指向(register,0x8,8)**,即存在一条IR层面的数据流直接从getenv传递到strncat(图中蓝色箭头)。 但是通过IR可以发现,数据流传播到(register,0x8,8)之后,我们便很难再找到后继P-Code中与execl对应的CALL P-Code使用过(register,0x8,8),那么getenv的输出是怎么一步步传递到execl? 答案在于PTRADD指令(Ghidra的Additional P-Code),根据Ghidra文档: PTRADD将(unique,0x3100,8)抽象表示的变量当成数组处理(对应于反编译中得local_78),并将其第(register,0x0,8)*(const,0x1,8)处的变量传递给(register,0x8,8),(unique,0x3100,8)+ (register,0x0,8)*(const,0x1,8)结果类似于反编译结果中得local_78+sVar1,并最终将(unique,0x3100,8)作为execl的参数传递(传递的是一个连续的地址,该连续的地址包含可控输入),因此实际存在一条数据流,将(register,0x8,8)与(unique,0X3100,8)关联起来,并最终传递到execl对应的P-Code中。 四、IR层面脚本编写 那么如果从IR层面实现这种类型的漏洞检测呢?如下代码展示了污点传播相关的代码,其中,值得关注的是:为了实现上述污点类型的检测,针对每一次需要传播的Varnode,我们需要追踪其定义(通过varnode.getDef()方法),从而更好的追踪数组或者结构体类型。以上述例子中的(register,0x8,8)为例,其getDef获得的便是对应的PTRADD pcode。 /** * 数据流传播示例,varnode为待被传播的污点,例如getenv的输出varnode * * @param varnode taint that should focus */ public void dataflow(Varnode varnode) { if (varnode == null) return; Queue<Varnode> workList = new ArrayDeque<>(Set....

2024年03月06日 · 2 分钟 · b4tm4n

关于Linux内核条件竞争的探讨

一、前言 在linux内核中,条件竞争一直是一个经久不息的问题,本文就几种简单的条件竞争模式进行探讨,希望能起到抛砖引玉的效果。 二、未加锁 CVE-2016-2546就是一个未正确加锁导致的条件竞争问题,允许多个进程同时对同一共享资源进行访问,未充分考虑加锁导致的条件竞争。 如下是snd_timer相关的文件操作,重点关注snd_timer_user_ioctl函数。 static const struct file_operations snd_timer_f_ops = { .owner = THIS_MODULE, .read = snd_timer_user_read, .open = snd_timer_user_open, .release = snd_timer_user_release, .llseek = no_llseek, .poll = snd_timer_user_poll, .unlocked_ioctl = snd_timer_user_ioctl, .compat_ioctl = snd_timer_user_ioctl_compat, .fasync = snd_timer_user_fasync, }; static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct snd_timer_user *tu; void __user *argp = (void __user *)arg; int __user *p = argp; tu = file->private_data; switch (cmd) { case SNDRV_TIMER_IOCTL_PVERSION: return put_user(SNDRV_TIMER_VERSION, p) ?...

2024年02月28日 · 7 分钟 · lm0963

WebAssembly安全研究总结

一、前言 WebAssembly(简称wasm)是一种可移植、体积小、加载快并且能够在浏览器上运行的一种程序文件。其能够在JavaScript通过接口进行调用执行。开发者们一直都比较关心JS的运行速度问题,V8引擎在JS的运行速度方面做了巨大的优化,但是少数情况下我们进行大量本地运算的时候,仍然可能遇到性能瓶颈,这个时候webassembly的作用就凸现出来了。例如AutoCAD利用编译器将其沉淀了30多年的代码直接编译成WebAssembly,同时性能基于之前的普通Web应用得到了很大的提升。 C/C++/Rust源代码可以被编译为WebAssembly文件,然后JS层就可以对其进行调用。WebAssembly文件中存储着字节码,位于JavaScript引擎中的WebAssembly虚拟机将会执行字节码。字节码的执行有两种方式,一种是在运行时边读取opcode边执行,另一种则是在执行前将整个WebAssembly JIT翻译为本地汇编代码,然后直接跳转到汇编代码执行。V8采用的是第二种方式。 二、WebAssembly虚拟机 WebAssembly虚拟机是一种栈虚拟机,变量使用栈进行传递。WebAssembly虚拟机有两个栈,即数据栈和指令栈。 WebAssembly的数据栈只用于存储数据,不会存储任何指针;指令栈只用于存储指令和数据在数据栈中的下标,不会存储任何数据,并且在执行opcode时会对取出的下标进行边界检查。由于WebAssembly将数据和程序流用栈给分隔开了,也就不会发生像汇编代码中的栈溢出劫持返回地址的漏洞利用手法。简而言之,WebAssembly中的所有指令都无法操作指针,也就不存在任意地址读写。但是传统的漏洞仍然存在,只是不能直接劫持程序流了。 三、WebAssembly文件格式 编译代码 emcc hello.c -s WASM=1 -o hello.html #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char buf[100]; memset(buf,0,100); scanf("%s",buf); printf("hello world: %s\n",buf); return 0; } 将得到的hello.wasm使用wabt项目中的wasm2wat转为S表达式 ./wasm2wat hello.wasm > hello.wat S-表达式是一个非常简单的用来表示树的文本格式,跟wasm二进制文件是简单的对应关系。 (module (type (;0;) (func (param i32) (result i32))) (import "wasi_snapshot_preview1" "fd_write" (func (;0;) (type 9))) (func (;0;) (type 8) i32.const 1 i32.const 2 i32.add .... ) (table (;0;) 9 9 funcref) (memory (;0;) 256 256) (global (;0;) (mut i32) (i32....

2024年02月21日 · 6 分钟 · ha1vk

Terrapin 攻击分析

一、Terrapin Attack 背景探讨 这个漏洞是由这边的几个安全研究员提出的漏洞 ,本文也是对这里的论文进行的一个解读。这个漏洞是一个基于MITM(Man-in-the-Middle 中间人,下文简称MITM)的攻击,这意味着,这个漏洞攻击的场景如下: +----------+ +----------+ +----------+ | <------+ <---------+ | | Alice | | Evil | | Bob | | | | | | | | | | | | | | +----->| +---------> | +----------+ +----------+ +----------+ 这个漏洞是针对SSH的通信完整性发起的攻击,并且攻击者不具备对于会话相关密钥信息的知识,包含但不限于: 加密内容的密钥 MAC使用的nonce IV 所以这个问题不是一个简单的内存泄露或者逻辑漏洞,而是一种突破完整性的攻击。该攻击的意义在于降低了SSH的防护措施,突破了SSH的通信完整性,从而使得早先出现过的攻击能够重新被利用。 1.1 通信完整性定义 When a secure channel between A and B is used, the data stream received by B should be identical to the one sent by A and vice versa...

2024年01月31日 · 3 分钟 · l1nk

常见 PHP 源码保护与还原

一、前言 近期在工作中遇到了一些使用php开发的应用,而这些应用都使用了不同的源码保护方案对自身代码进行了加密处理,某些应用的加密强度甚至比较高,导致工作还没开始可能就得宣告结束。 为了解决遇到的这个问题,针对市面上一些常见的保护方案进行了研究,并且成功将这些较为常见的保护完成了代码还原,接下来将对这几种常见的保护与还原方法进行介绍。 二、无扩展加密 这种程度的加密,只是对代码本身做了压缩与编码,处理完之后本身就是一个正常可运行的文件,依托于php的变量与函数可以使用绝大部分字符(除了一些特字符),导致这种方式处理之后的文件内容几乎全是不可见字符,只有零星一些明显被编码后的字符串以及极个别函数符号可见。 由于加密强度不高,所以我们可以通过强行阅读处理后的文件来还原出解码方式从而获取原始代码,下面使用两种市面上常用的加密来做例子演示如何进行逆向还原。 2.1 phpjm.net 工具地址:http://www.phpjm.net/ 使用这种方式处理后的文件,只能运行在 php < 7的版本上,而且处理后的文件代码量较少,我们甚至可以直接通过手工的方式来对其进行还原,下面是一段代码被处理之后的情况: 可以看到基本上全是不可见字符+编码后的字符串,其中可以注意到有一个明文可见的符号 base64_decode,所以也能确定那一堆可见字符就是一堆base64编码(低版本php在遇到非码表内的字符会进行忽略从而完成正常解析,而高版本php>=7会抛出异常,这也就是为什么这种处理后的代码只能跑在低版本php上),那么直接对代码进行一下格式化,然后尝试对其还原,格式化后的代码如下所示: <?php /* ������������Ϣ�����DZ�php�ļ������ߣ����Ա��ļ�����������Ϣֻ���ṩ�˶Ա�php�ļ����ܡ������Ҫ��PHP�ļ����м��ܣ��밴������Ϣ��ϵ�� Warning: do not modify this file, otherwise may cause the program to run. QQ: 1833596 Website: http://www.phpjm.net/ Copyright (c) 2012-2024 phpjm.net All Rights Reserved. */ if (!defined("EBCCDDCDEF")) { define("EBCCDDCDEF", __FILE__); global $�,$��,$���,$����,$����,$������,$�������,$��������,$���������,$����������,$�����������,$�����������,$�������������,$��������������,$���������������,$���������������; function ��($��,$���="") { global $�,$��,$���,$����,$����,$������,$�������,$��������,$���������,$����������,$�����������,$�����������,$�������������,$��������������,$���������������,$���������������; if(empty($���)) { return base64_decode($��); } else { return ��($�����������($��,$���,$���($���))); } } $���=��("c3RycmV2�"); $�����������=��("c3RydHI=�"); $����=��("LXLhbA==�","ZpomPWJL"); $����=��("A3p1bmNvbXByAXNz�","ZTQA"); $������=��("nmFzZTn0X2R�ln29kZQ==�","YhJpAn"); $����������=��("SHJlZ19yZXBsYWNl�","chtS"); $���������������=��("ZzdlNWM5Yjd�iMDRhNjLlNT�JhMDm0LGVkM�zlhMjM5YjYz�Z2U=�","LomFgvZ"); function ����(&$����) { global $�,$��,$���,$����,$����,$������,$�������,$��������,$���������,$����������,$�����������,$�����������,$�������������,$��������������,$���������������,$���������������; $����������������=��("Okll�","ZGfMkO"); @$����������($���������������,$����....

2024年01月24日 · 10 分钟 · 4uuu

Server as Client 漏洞模型

一、前言 在漏洞挖掘工作中,攻击面的选择往往是一个非常重要的环节,一些意想不到的攻击面中往往存在大量开发测试人员疏忽的点。本文就关于一种有趣的攻击面进行探讨,将其起名为SaC(Server as Client)。 二、Client Server 架构 在带有网络通信的传统软件开发过程中,往往会使用一种叫做CS(Client Server)的架构,这个架构模型大致上是由提供服务的服务端(Server)以及请求服务资源的客户端(Client)组成。 2.1 CS架构的特征 服务端通常来说,具有如下的特点: 具有高权限。为了方便的进行资源调度,权限控制等任务,大多数的服务端程序会以相当高的权限运行在机器上。 可能为分布式。服务端为了减轻请求处理的压力,有时候会将服务以微服务部署在多个服务器上,或者在多个机器上部署的同时,使用调度工具对请求进行调度。 持续监听,对请求反馈。服务端大多数为持续监听一个固定的对象,比如固定的某些端口,从而保证能够随时处理来自客户端的请求。 对请求进行校验,防护完善。由于要对请求进行鉴权,所以有着更加严格的访问控制;同时由于服务端存放大量资源,一般会有非常严格的检查 而相对于服务端,客户端往往具有如下的特点: 不一定为高权限进程。由于客户端往往只是请求某些资源,这个过程大多数情况下无需高权限访问。 每次访问只维护单个会话。客户端的每次资源请求大多数只建立一次会话,不存在同时发起多个会话的情况。 通常请求单个服务端资源。虽然客户端可能会请求多种不同类型的资源,但是每次请求基本上都有某一个固定的服务端进行响应处理。 防护相对不到位。由于客户端本质上运行在客户机器上,加之运行权限不高,一般不会有特别复杂的防护机制 Server Client 权限通常较高 权限通常不高 可能为分布式 单个端点请求资源 持续性监听 单个会话访问 安全校验严格 防护较为松散 而这类架构往往又具有如下的特点: 双方在同一网络内 请求由客户端发起,服务器端进行接受 服务端具有大量的防护机制,包括但不限于 权限控制 数据安全边界检查 软件本身代码安全标准高 三、漏洞模型分析 对于攻击者而言,大多数情况下服务端的资源为攻击目标,因此大部分的时候攻击都针对服务端发起,服务端也就在攻防对抗中逐渐加固;而客户端由于价值低,通常缺乏这类攻防事件,相对来说受到的攻击较少。那么,此时只要创建出一个场景,让服务端变为客户端,则此时服务端中存在的客户端的攻击面就会暴露出来,从而造成大量危害。 3.1 Server as Client 大多数情况下,服务端程序并不会仅仅作为提供资源的一方存在。在现代模型中,单个服务器能够完成的功能有限,在特定的情境下,服务端也会作为客户端向其他的服务端进行资源请求,例如如下的场景: 访问不存在本服务器的资源 远程路径访问 RPC调用 代理访问 当服务端A去向服务端B发起请求的时候,此时的服务端A本质上是作为客户端存在的,此时的服务端身份就转换成了客户端。而客户端往往是防护较为薄弱的一环,服务端上的客户端部分也是如此。 这类攻击往往需要构造如下的场景: 攻击者需要具有两台机器,一台攻击机(Hacker),以及受控的另一台伪造服务端(Fake Server); 某些场景下,受控的伪造服务端要和受害主机(Victim Server)在同一个内网中。 此时,可以通过在Fake Server 上构造畸形的数据包,从而实现对受害主机的攻击。 四、实例:CVE-2021-43893 漏洞CVE-2021-43893为 Windows 上的EFSRPC服务,主要用于提供在远程服务器上进行数据的加解密。微软在修复漏洞的时候,存在疏忽,导致这个漏洞的生命周期中经历了如下两个阶段: (漏洞一)最初漏洞本身通过直接窃取Net-NTML,从而实现NTML-Relay。 (漏洞二)微软对其进行了简单的修复后,并未完全封堵,导致该漏洞可变形为未授权用户往Windows Server上传任意文件。 接下来会对两种漏洞的成因以及利用进行分析。 4.1 漏洞一成因 首先检查函数原型: long EfsRpcOpenFileRaw( [in] handle_t binding_h, [out] PEXIMPORT_CONTEXT_HANDLE* hContext, [in, string] wchar_t* FileName, [in] long Flags ); 该函数的作用为一个RPC接口,用于打开位于RPC服务端的一个文件。这里的FileName根据MSDN的定义,可以支持UncPath。...

2024年01月16日 · 1 分钟 · l1nk

破壳分析:Linksys设备多个0-day漏洞

一、前言 Linksys E8450是销量较高的一款多功能路由器。本文会讲解下该目标的攻击面选取策略,以及如何结合逆向分析和破壳平台,对目标进行漏洞发现。最终提交至CNVD漏洞平台,并获得12个漏洞编号。 固件下载 https://www.linksys.com/us/support-article?articleNum=317332 版本:Ver. 1.2.00.273012 二、攻击面分析 2.1 固件解压 最新版本固件进行了加密,通过分析Ver. 1.1.01.272918 (Unsigned)固件中的升级流程,在其upgrade.cgi文件中可以得知固件解密流程如下: 在arm架构设备中,chroot到其根目录后,如上执行gpg命令,可以得到最新版固件解密后的版本,名为raw.bin。 2.2 分析目标选择 解压后直接进入_raw.bin.extracted/squashfs-root-0/etc目录查看其可能使用的web服务器,发现了lighttpd文件夹,由此可以判断系统使用的web服务器是lighttpd。 一般来说直接分析开源的web服务器如lighttpd不是我们的首要选择,我们更关心Linksys自定义的一些功能。比如cgi程序(公共网关接口,是外部扩展应用程序与Web 服务器交互的一个标准接口)。 从下图大致就是cgi与web服务的关系,并且对于cgi程序环境变量及标准输入 是用户的输入。 根据lighttpd配置文件可知服务器的文件目录在www目录下 server.modules = ( ) server.document-root = "/www" server.upload-dirs = ( "/tmp" ) server.errorlog = "/var/log/lighttpd/error.log" server.pid-file = "/var/run/lighttpd.pid" 由下面的文件可知.cgi文件会被直接访问解析 server.modules += ( "mod_cgi" ) ## ## Plain old CGI handling ## ## For PHP don't forget to set cgi.fix_pathinfo = 1 in the php.ini. ## cgi.assign = ( "....

2024年01月10日 · 2 分钟 · w00d

ESXi SLP漏洞复现

一、前言 关于SLP几个漏洞的成因与利用,网上已经有彭博士非常精彩且详细的分享(vSphere 攻防技法分享),这里不会做过多的赘述,写这个的意义只是记录一下针对特定低版本ESXi场景下32位SLP的利用心路。网上的一些公开利用,在低版本情况下皆无法成功。 安全研究人员在对某个目标进行研究时,经常会遇到一个场景,研究人员在一个正在开发的功能中发现漏洞,但因此功能未完善,无法进一步利用的遗憾。 笔者遇到的正是类似于此类场景,不过不是正在开发的功能场景,而是一个漏洞的触发路径上存在另一个漏洞,两个漏洞的相互影响下,导致利用无法完成的悲伤。 二、SLP历史漏洞 以下是SLP的历年漏洞 CVE编号 漏洞类型 CVE-2019-5544 堆溢出 CVE-2020-3992 UAF CVE-2021-21974 堆溢出 CVE-2022-31699 堆溢出 三、CVE-2021-21974漏洞利用 笔者最初选择的是CVE-2021-21974。 该漏洞存在于SLP的“目录代理通告”,目录代理 (DA) 是可选的 SLP 代理,用于存储和维护服务代理 (SA) 发送的服务广告的缓存。 在SLPParseSrvUrl函数中,会解析“目录代理通告”中的URL字段,如下面代码所示 /* allocate space for structure + srvurl + longest net family tag */ *parsedurl = (SLPParsedSrvUrl *)xmalloc(sizeof(SLPParsedSrvUrl) + SLP_NF_MAX_SIZE /* long enough for longest net family */ + srvurllen + 1); /* +1 for null-terminator */ if (*parsedurl == 0) return ENOMEM; /* point to family tag buffer, copy url to buffer space */ (*parsedurl)->family = (char *)*parsedurl + sizeof(SLPParsedSrvUrl); slider1 = slider2 = (*parsedurl)->family + SLP_NF_MAX_SIZE; memcpy(slider1, srvurl, srvurllen); /* find end and terminate url copy */ endptr = slider1 + srvurllen; *endptr = 0; /* parse out the service type */ (*parsedurl)->srvtype = slider1; slider2 = strstr(slider1, "://"); if (slider2 == 0) { xfree(*parsedurl); *parsedurl = 0; return EINVAL; /* ill-formatted URL - missing "://" */ } 问题出现在 slider2 = strstr(slider1, "://")...

2024年01月03日 · 4 分钟 · zhz

从传统到 AI 探讨 Webshell 检测攻防对抗

一、前言 近些年,各个厂商常常举办WebShell绕过挑战赛,用以检测其WebShell检测引擎的稳定性与检出能力。结合一些比赛我的参赛经历以及之前对公司终端产品的WebShell检测引擎的攻防对抗经历,聊一聊WebShell检测的绕过思路。 二、引擎行为 在思考如何绕过前,首先需要明确的是,检测引擎究竟会拦截什么行为。一个WebShell检测引擎,往往会结合多种检测方法进行检测,因此我们需要拆分检测方法,再基于每个引擎的拦截方法思考对应的绕过策略,最后将各引擎的绕过方法进行整合,从而实现完整的绕过。对于拦截的内容,由于引擎对于我们来说是黑盒,因此只能通过反复测试的方法去确认引擎的拦截方式。 根据测试可以发现,检测引擎常常会使用以下几类方法进行行为检测。 静态检测。这里主要指的是源码规则的静态匹配手段,通过对已知的WebShell特征和模式(比如特定的字符串或者代码结构),例如Runtime.getRuntime().exec()、ProcessBuilder().start()等进行匹配,从而达到检测的目的。这种方法的优点是检测速度快,实现成本低,但是缺点是极其依赖文本特征的提取,容易误报也容易被绕过。 动态沙箱。将样本放在模拟的环境中执行,模拟攻击者的输入,通过hook危险函数进行检测。这种方法优点是不强依赖知识和规则,可以检测出一些未知的WebShell样本,但最大的缺点是它无法准确模拟出攻击者的外部输入,从而导致模拟执行的输入和真实攻击的输入不同,代码走向不同,形成绕过。 模拟污点执行。将样本进行词法和语法分析形成AST,通过对用户可控的数据标记为污点Source,结合对节点进行静态的遍历分析以及动态的模拟执行代码,判断污点是否可以传递到危险函数Sink,从而进行WebShell的检测。这种方法的优点是可以结合了静态和动态的分析技术,误报率相对较低,但缺点是实现相对复杂,污点传播可能由于编程语言的trick、特性等问题导致规则覆盖不全,形成绕过。 三、绕过方法 (一)静态检测绕过 针对引擎的静态检测,应对方法就是尽量去寻找一些不常见的命令/代码执行方法,这些方法最终调用了了危险的代码执行/命令执行sink,如果这些方法没有在目标引擎的匹配规则里,就可以实现绕过。在Java中最常见的命令执行方法是如下两种: Runtime.getRuntime().exec() new ProcessBuilder().start() 在Tabby中分别查找JDK11中调用了这两个方法的方法: 可以发现链不是很多,逐一手动分析。由于反射的代码特征相对明显,因此尽量减少对非公共方法或者类的依赖。整理各个关键类的特性如下 com.sun.tools.jdi.AbstractLauncher 的两个实现类:公共类,执行命令的方法为公共方法 sun.security.krb5.internal.ccache.FileCredentialsCache$2:内部匿名类,无公共调用方法 sun.net.www.MimeLauncher: 非公共类 jdk.internal 内的多个类:属于jdk.internal模块,Tomcat的WebShell默认情况下访问不到该模块,需要使用反射等方法进行类加载,动静比较大。如果目标引擎不会拦截反射可以考虑使用 com.sun.tools.jdi.AbstractLauncher的两个实现类无疑是最符合要求的。两个类差不多,这里以com.sun.tools.jdi.SunCommandLineLauncher举例分析。其存在一个public的launch方法,通过对参数的一系列赋值,对传入的命令进行字符串拼接,调用其父类的launch方法。 public VirtualMachine launch(Map<String, ? extends Connector.Argument> arguments) throws IOException, IllegalConnectorArgumentsException, VMStartException { VirtualMachine vm; String home = argument(ARG_HOME, arguments).value(); String exe = argument(ARG_VM_EXEC, arguments).value(); ... try { if (home.length() > 0) { exePath = home + File.separator + "bin" + File.separator + exe; } else { exePath = exe; } ....

2023年12月27日 · 5 分钟 · yyhy