关于 C++ 迭代器失效特性的研究

一、前言 在C++中,迭代器(Iterator)是一种用于遍历容器中元素的对象(如数组、向量、列表等)。它提供了一种统一的访问容器元素的方式,无论容器的类型和实现细节如何,比如list,vector,map等虽然实现不同,但最终都可以通过迭代器来访问和操作容器中的元素。 迭代器的工作方式类似于指针,它可以指向容器中的特定位置,并允许你在容器中前进或后退。通过使用迭代器,你可以遍历容器中的所有元素,执行读取、修改或删除操作。 二、迭代器的几种常见使用方式 Range-Base-For形式 for(auto& it : its_){ [...] } 直接迭代器形式 auto it = its_.begin() it.next() 循环形式 for(it = its_.begin();it!= its_.end();it++){ [...] } 三、序列式容器 3.1 Vector失效 首先先来介绍一下Vector的结构,Vector 是C++标准程序库中的一个类,可视为会自动扩展容量的数组,以循序(Sequential)的方式维护变量集合。 失效原因:容器发生内存重分配后,或者有删除或者插入操作 失效前提:有删除或者插入操作。 失效情况: 容器内存经过push_back 后重新分配,迭代器的指针还是指向原来的内存,导致迭代器、引用、指针都失效。如果是插入且没有重新分配,只会使后面的元素失效。 如下例,定义了一个Vector,并对其进行循环迭代。 std::vector<int> vec_; vec_.push_back(1); vec_.push_back(2); for(auto & it : vec_){ vec_.push_back(3); std::cout<<it<<std::endl; } 在运行完两个push_back进行元素的添加后,此时Vector的内存: std::vector<int> vec_; vec_.push_back(1); vec_.push_back(2); --------------------- vec_: 0x7fffffffdcc0: 0x0000602000000030[start] 0x0000602000000038[finish] 0x7fffffffdcd0: 0x0000602000000038[end] 0x00007ffff7f953f8 ---------------------- 然后在进行迭代时,对其进行元素添加,超过其容量导致空间重分配。[*]的位置就是Vector的首地址,可以清楚看见此时的地址已经为重新分配后的地址了,原地址已经被释放。然后在运行输出语句后导致UAF。 for(auto & it : vec_){ vec_.push_back(3); ---------------------------- 0x7fffffffdcc0: 0x0000602000000050[*] 0x000060200000005c 0x7fffffffdcd0: 0x0000602000000060 0x00007ffff7f953f8 //原先的内存被释放,重新申请新空间。此时迭代器it 引用的地址 //pwndbg> p &it //$9 = (int *) 0x602000000030 ---------------------------- std::cout<<it<<std::endl; ---------------------------- ==2386378==ERROR: AddressSanitizer: heap-use-after-free on address 0x602000000030 at pc 0x5555556329fe bp 0x7fffffffdc90 sp 0x7fffffffdc88 READ of size 4 at 0x602000000030 thread T0 ----------------------------- } 容器内元素erase后,在其后面的元素都会往前移,导致其后面的元素都失效。(不可利用)...

2024年05月08日 · 3 分钟 · lime

SystemUI As EvilPiP: 针对现代移动设备的劫持攻击

2024年4月19日,奇安信天工实验室安全研究员程为民,出席国际顶级信息安全会议BlackHat ASIA 2024,发表 《SystemUI As EvilPiP: The Hijacking Attacks on Modern Mobile Devices》 议题演讲。议题披露了在SystemUI下隐藏了六年之久的新型攻击面,以及SystemServer中难以修复的设计缺陷。 一、Preface Activity Hijack Attack(AHA)是一项古老的UI攻击技术。大约在十年前,利用这种技术的银行木马与间谍软件开始在Android4.0平台上泛滥,这些劫持软件可以精确监控用户的行为,并以几乎无感知的方式劫持用户正在浏览的内容。由于在早期Android平台上利用这种技术无需任何权限和额外的用户交互,其成为了地下产业最喜爱的攻击手段之一。 但近年来,AHA逐渐失去了它的光彩。由于Google持续发布针对这类技术的缓解方案与限制策略,劫持软件的攻击成本被不断提高。2016年,Google更新了SELinux策略,完全禁止了应用对procfs的访问,并限制了大部分可以泄露应用运行状态的API,自此,无感知与零权限的劫持攻击(基于AHA)成为了历史。2017年,更加严格的LMKD机制与后台执行限制开始杀死处于后台的闲置进程。2019年,Google发布了BAL限制,从后台启动活动的行为被禁止,AHA技术彻底死亡。 在这些安全策略的保护下,AHA不再是低成本的移动端攻击方案。攻击者或许会通过诱导用户开启需要复杂交互才能使用的特权以在高版本设备上实现AHA攻击,但这距离精准劫持还很遥远,更何况手机厂商会在这些特权被授予前警告用户不要轻易相信第三方软件,所以AHA毫无疑问被地下产业抛弃了,甚至在2019年之后,再也没有论文或会议提到这类技术。劫持软件的时代结束了吗? 这份研究将证明Google的安全策略并非不可突破,零权限且无感知的劫持攻击仍有可能出现在高版本Android设备上。 六年前,Android引入了一个新的系统特性。同时也引入了一个潜在的攻击面。本研究将披露攻击面下多个未公开漏洞的细节,任何应用都可以利用漏洞间接攻击SystemUI,并以零权限突破BAL限制。接着,研究将深入SystemServer,同时分析其中潜藏多年的安全问题与设计缺陷,最终利用这些缺陷以侧信道方式来泄露任意一个应用的运行状态,绕过LMKD与后台执行限制,获得长期监控与稳定运行的能力。 在最后,研究将组合这些绕过方案,以武器化一个可以绕过自2014年以来Google发布的所有安全策略的劫持软件。这或许是七年来唯一一个从正面突破安全策略与防御机制,在 Android Q+ 设备上达成零权限且无需额外用户交互的劫持软件(基于AHA)。 二、Introduction 在高版本设备上实现UI劫持攻击之前,有必要知道它在早期Android设备上是怎么运作的。虽然"Preface"章节简要谈到了限制劫持攻击的几种安全策略,同时也提到了绕过策略的可行方案,但如果要理解本研究针对关键组件的分析以及完整利用链的原理,那么通过传统劫持链条来理解安全策略是有必要的。 2.1 Chain Of AHA-based Hijackware 如图为传统劫持软件的大致攻击链条。首先链条将启动Service组件以便进程长时间驻留在后台,接着组件内的代码将不间断获取目标的运行状态,以此来判断其是否来到前台。一旦目标到达前台,也就意味着用户目前正在浏览目标应用,当时机合适时,程序会通过一个带有NEW_TASK标记的Intent对象从后台启动Activity以覆盖用户正在浏览的页面(这一步骤正是AHA),最终达到UI劫持的目的。 可见传统劫持链条相当简单,没有任何一步是多余的,且链条中的所有关键操作在早期Android平台上无需申请任何权限。毫无疑问,简短且有效的攻击链条允许攻击者很好地混淆或隐藏恶意代码,且这种UI覆盖攻击不易被用户察觉。如果Google没有对这类攻击方法采取措施,恐怕直到现在地下产业的开发者仍会采用这种方案攻击用户设备。 在了解攻击链条后,下面将正式进入到关键步骤的技术细节以及安全策略的分析部分。 2.2 Leaking Running State 在API22之前,攻击者可以通过滥用ActivityManager下的接口来泄露第三方应用的运行状态。如下图,getRunningTasks与getRunningAppProcesses函数可以获取到详细的第三方应用信息,其中getRunningTasks接口甚至能够获取到目标任务栈顶的Activity信息,早期的劫持软件正是以此实现高精度的劫持攻击。 在API22之后,这些API全部被Google标记为Deprecated且做了相关限制,目前在API33上调用这些接口将只能返回调用者本身的相关信息。但是在API26之前,攻击者仍可以通过procfs以侧信道方式泄露敏感信息。 如上图,以API19的Android设备为例,以用户u0_a57身份列出/proc目录下的内容,随机选中一个进程并访问其oom_score_adj,显然非特权用户依然有权限浏览第三方进程信息,即使Google对敏感API进行了限制,攻击者仍可以通过procfs获取第三方进程的优先级,以此判断其是否存在于前台。 随后在2017年,Google更新了SELinux策略,彻底禁止了任意应用通过procfs访问第三方应用数据(类似hidepid=2保护)。自此之后劫持软件不得不通过 PACKAGE_USAGE_STATS 权限与 UsageStatsManager 来实现精确劫持,但该权限的开启需要复杂的用户交互,诱导用户开启这种权限并非一件易事,况且许多手机厂商(比如MIUI)会在开启这类权限前强制警告用户可能的安全风险,且警告页面会强制持续10秒。所以在"Preface"章节才会称零权限与无感知的劫持攻击成为了历史。 2.3 Activity Hijack Attack 要实现精准UI劫持,泄露第三方应用的运行状态固然重要,但AHA技术是整个攻击链条的核心,一旦AHA技术不再起作用,整个链条也就无法运行。 在API29之前,AHA仍可以被利用。攻击者会通过调用startActivity启动一个指向Activity且携带NEW_TASK标记的Intent对象以实现AHA攻击。如AOSP框架代码中对于该标记的描述,携带此标记时启动Activity会让系统创建一个新的任务栈(如果这个Activity不包含在任何现有任务栈中),接着这个Activity将立即出现在用户视野内,覆盖屏幕上原本的内容。那么为什么会发生这种情况? 为什么启动新任务栈可以让Activity覆盖屏幕上的内容? 任务栈可以看作装载Activity的容器,任何应用在启动时都将至少创建一个任务栈(假设应用拥有UI)。根据官方文档"Task and back-stack“的描述,应用内启动的Activity都将进入对应的任务栈内,且任务栈可以容纳任意数量的Activity。 在用户视野内,用户将首先看到任务栈中的栈顶活动,而任务栈可以被分为前台任务栈与后台任务栈,后台任务栈不被用户可见,且后台中可以同时存在多个任务栈。前台任务栈为用户可见,但大多数情况下有且仅有一个任务栈存在于前台,用户一次只能与一个前台任务栈进行交互(不考虑分屏或其它情况)。 在了解过任务栈相关的概念后,AHA技术就很好理解了。以API15的AOSP框架代码为例,startActivity函数被调用后,系统将进入ActivityStack#startActivityUncheckedLocked函数,接着代码将判断传入的Intent是否携带NEW_TASK标记,携带标记时系统会将Intent指向的Activity的所在任务栈移动到前台,而由于前台仅允许存在一个任务栈,所以之前存在于前台的第三方应用任务栈将被压入后台,并被新的任务栈顶替。 不难得出结论,AHA的本质事实上就是对前台任务栈的抢占,在合适的时机抢占前台,就能悄无声息地劫持用户的屏幕。事实上早在2013年,由北京航空航天大学与其他相关机构发表的论文《Hijacking Activity Technology Analysis and Research in Android System》(10.1007/978-3-662-43908-1_6)就曾提到过利用这种方法实现AHA攻击。 但自从API29,谷歌开始发布相关策略来阻止这类攻击。下文称该策略为BAL限制(Background Activity Launch restriction)。根据官方文档”Restriction on starting activities“对该限制的描述,任何处于后台的应用都无法启动Activity,除非该应用能够满足一项或多项豁免条件。然而这些条件都十分苛刻,几乎没有任何后台应用可以在不持有危险权限时满足任何一条。如果Activity启动的流程被中断,就不可能创建新任务栈来劫持屏幕内容,所以在API29之后,AHA技术被宣告死亡,目前基于AHA技术的所有相关PoC在API29及以上Android版本都无法正常运行。...

2024年04月24日 · 2 分钟 · mg1937

Palo Alto CVE-2024-3400 漏洞分析

一、前言 全球著名防火墙公司Palo Alto Networks近日在官网公布了一个CVE-2024-3400的漏洞信息,该漏洞存在于部分PAN-OS系统的GlobalProtect功能中,在某些配置打开的情况下,攻击者可以对运行该系统的设备进行未授权RCE,并且拿到系统的root权限,本文以研究学习为目的对漏洞的成因进行详细的分析。 二、影响版本 根据官网提供的信息,我们选取了PAN-OS 11.0.0版本固件作为本文的研究对象。 三、GlobalProtect分析 GlobalProtect是PAN-OS中的VPN组件,可以在管理端配置GlobalProtect Portal门户页面,其页面如下,可供VPN用户进行登录。 对固件进行解包,在/etc/nginx/sslvpn中发现了GlobalProtect Portal的服务器配置文件,查看location.conf文件,可以知道VPN的API请求直接被代理到了内部的20177端口。 通过查看端口占有情况,可知VPN请求由gpsvc程序进行处理。 四、gpsvc逆向分析 gpsvc使用golang语言编写,分析HTTP服务的调用链 net_http__ptr_conn_serve -> net_http_serverHandler_ServeHTTP -> github_com_gorilla_mux__ptr_Router_ServeHTTP -> net_http_HandlerFunc_ServeHTT -> main__ptr_GpTaskMgmt_MainHttpEntry -> main__ptr_GpTask_RunHttp -> main__ptr_GpTask_initHttp 其中main__ptr_GpTask_initHttp函数会解析请求数据包,审计该函数,注意到此处是对Cookie中的SESSID字段的处理。 在此处下断点并追踪对SESSID的处理,最终会来到main__ptr_SessDiskStore_New函数, 分析该函数的代码,其中name就是传入的SESSID,调用net_http__ptr_Request_Cookie从Cookie中获取SESSID的值,然后赋值给session->ID.str。 接下来会将session->ID.str进行简单的路径拼接,得到filename完整路径,然后传递给main_loadSessFile。 我们直接在main_loadSessFile下断点,然后调试,使用如下的测试脚本进行触发: from pwn import * import os import requests ip = '192.168.177.149' port = 20077 payload = 'POST /ssl-vpn/hipreport.esp HTTP/1.1\r\n' payload += 'Host: aa.bb.cc\r\n' payload += 'Cookie: SESSID=/aaaaaaaaaaaaa;\r\n' payload += '\r\n' sh = remote(ip,port) sh.send(payload) sh.interactive() 可以看到SESSID的内容成功拼接到filename,由于是简单的拼接,能否尝试一下/../../这种路径穿越的path?修改测试脚本。 #coding:utf8 from pwn import * import os import requests ip = '192....

2024年04月18日 · 2 分钟 · ha1vk

MikroTik RouterOS CVE-2023-32154 认证前RCE漏洞分析

MikroTik作为网络基础设施供应商,其产品和RouterOS被广泛采用。目前,至少有超过 300 万台设备在线运行 RouterOS。该漏洞是Pwn2Own上orange团队利用的漏洞,达到了认证前RCE。且该漏洞存在了9年未被发现,基本影响了RouterOS6版本和7版本中的大多数设备。 0x00 CVE信息 What this issue affects: The issue affects devices running MikroTik RouterOS versions v6.xx and v7.xx with enabled IPv6 advertisement receiver functionality. You are only affected if one of the below settings is applied: 通过官方描述,得知漏洞存在于ipv6的RA协议中。 Router Advertisement(RA)是IPv6协议中的一种控制消息,用于告知网络中其他设备关于路由器的存在和IPv6地址分配信息。RA消息通常由网络中的路由器定期广播,以便其他设备可以获取路由器的信息并使用IPv6协议与其通信。 在RouterOS中,RA功能是路由器的一个基本功能,它可以通过配置路由器的接口参数来启用。当启用RA功能后,路由器将会在指定的接口上定期广播RA消息,使得其他IPv6设备可以通过接收这些消息来自动配置自己的IPv6地址和路由信息。 0x01 前置知识 IPV6 SLAAC 所谓 IPV6 SLAAC 即 Stateless address autoconfiguration,无状态地址自动配置。这里我们需要了解几个概念。 路由器请求RS(Router Solicitation)报文:很多情况下主机接入网络后希望尽快获取网络前缀进行通信,此时主机可以立刻发送RS报文,网络上的设备将回应RA报文。 路由器通告RA(Router Advertisement)报文:每台设备为了让二层网络上的主机和设备知道自己的存在,定时都会组播发送RA报文,RA报文中会带有网络前缀信息,及其他一些标志位信息。 我们的电脑可以通过路由器发送的RA来接收ipv6前缀从而配置ipv6地址,同样的我们也可以向路由器发送RA来对路由器的ipv6地址和一些其他信息(比如DNS)进行配置。 通过分析这个协议我们可以发现,在这种场景中,路由器其实是一个既充当了客户端,又充当了服务端的功能。其他的客户端可以给路由器发送配置,路由器会将这些配置存储。而路由器本身又会作为客户端存在,把自身的配置向其他设备进行广播。而作为客户端情况存在往往会疏于检查从而存在漏洞。 0x02 漏洞分析 在RouterOS7.9版本中,找到负责RA协议处理的二进制radvd进行bindiff,发现新版本的patch主要在下面这段代码(以下是在7.9版本中未修复的代码) while ( v21 != a2 + 1 ) { v9 = sub_804E434(v8);Mikrotick if ( (_BYTE)v9 ) { v10 = operator<<(&unk_80554C0, "adding DNS server option, address=", v9, v9); v11 = operator<<(v16, v21 + 3, v10, v21 + 3); v12 = operator<<(v11, v20, v17, v18); endl(v12); } v19 = v21[4]; v13 = v21[5]; v14 = v21[6]; *(_DWORD *)(a1 + v3) = v21[3]; *(_DWORD *)(a1 + v3 + 12) = v14; *(_DWORD *)(a1 + v3 + 4) = v19; *(_DWORD *)(a1 + v3 + 8) = v13; v3 += 16; tree_iterator_base::incr((tree_iterator_base *)&v21); } 可以看到这段逻辑循环终止条件是v21这个vector到末尾,并且在循环过程中会把vector中的内容向a1上拷贝。其实再向上追一下可以发现a1是上层函数中的一个栈上的变量且大小是固定的。因此只要这个vector中的内容足够大便可以造成栈溢出。...

2024年04月17日 · 3 分钟 · w00d

JSON 解析不一致性漏洞探究

一、背景 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于机器解析和生成。它基于JavaScript语言标准ECMA-262第3版(1999年12月)的一个子集。随着时间的推移,JSON已经超越了JavaScript,成为许多编程语言支持的标准数据格式之一。 如HTTP 请求走私等攻击一样,json解析器之间以及多阶段请求处理的差异可能引入严重的漏洞,即使是在严格遵守规范的解析器,也不可避免的与规范存在偏差,这是为什么? 关于JSON的规范有多个,各规范定义有一定的差异 ECMAScript Standard:ECMAScript是JavaScript语言的标准化名称,定义了JSON作为JavaScript的一个子集,但实际应用超出了JavaScript; IETF JSON RFC 8259:提供了一个严格和精确的JSON数据交换格式规范,这个标准旨在确保JSON数据的交换在不同的系统间能够保持一致性和可靠性。 规范文档对于一些定义是开放式的描述,例如 IETF JSON RFC 8259 对重复键的描述: An object whose names are all unique is interoperable in the sense that all software implementations receiving that object will agree on the name-value mappings. When the names within an object are not unique, the behavior of software that receives such an object is unpredictable. Many implementations report the last name/value pair only....

2024年04月10日 · 5 分钟 · p0melo

IoT 设备常见 Web Server 漏洞挖掘思路分析

一、前言 在IoT小设备中由于运行资源(CPU、存储、内存等)受限,通常会使用轻量级的 Web server,例如uHTTPd、lighttpd、micro_httpd、mini_httpd、GoAhead、boa等,其中uHTTPd、lighttpd此类Web server通常不会由开发者修改源码新增功能代码,而是纯粹作为一个类似流量转发的框架;而micro_httpd、mini_httpd、GoAhead、boa此类通常是由开发者将一些业务代码集成到Web server中,导致在server代码中产生漏洞的概率增大。 本文由于篇幅原因,仅仅针对IoT小设备(光猫、路由器、摄像头等)中常见的两个开源Web server框架GoAhead和mini_httpd,分别从源码处理数据、漏洞存在点、经典CVE漏洞分析这三个方面,浅析其漏洞挖掘思路。 本文不会完全分析Web server代码实现、架构,而是聚焦于安全研究较为关注、通常由开发者实现的数据包处理部分。文章的大概阐述思路如下: 首先结合源码说明数据包处理特性,主要涉及鉴权、路由处理 简述数据包处理中可能存在的漏洞点 结合经典漏洞进行分析 二、GoAhead篇 GoAhead是一个轻量化、适用于嵌入式设备的Web server,采用C语言编写,代码量不大,具有高度的可移植性和扩展性。GoAhead支持多进程、多线程,能够处理大量的并发连接,支持SSL/TLS加密和基本的身份认证,支持CGI、ASP,满足了绝大部分的Web server业务场景。 GoAhead由Embedthis Software LLC开发,早年间是完全开源的,可以直接在Github上下载到源码。但是在2022年的时候,似乎转为了商业定制,官方在Github删除了代码库,因此在Github上无法下载,但是在Gitee上还有镜像库。下载地址:GoAhead: GoAhead WebServer 在D-Link的主流路由器中,例如DIR系·列,很多使用了GoAhead作为Web Server;除此之外还有Tenda、NETGEAR、BEC等许多厂家都有在其设备中使用GoAhead。 2.1 数据包处理逻辑 GoAhead会对数据包按照优先级进行顺序处理,处理方式是通过注册的回调函数: 优先级为1注册的回调函数:所有数据包都需要首先经过该回调函数进行处理,此处也通常被用来做数据包鉴权、请求路径合法性判断、未授权访问路径定义等等; 优先级为0注册的回调函数:通常用来定义认证后可访问到的接口逻辑实现; 优先级为2注册的回调函数:处理没有匹配到注册路径的数据包,也就是非法路径的数据包。 int websUrlHandlerDefine(char_t *urlPrefix, char_t *webDir, int arg, int (*handler)(webs_t wp, char_t *urlPrefix, char_t *webdir, int arg, char_t *url, char_t *path, char_t *query), int flags) 重要的参数: char_t *urlPrefix:指定URL的前缀,也就是需要处理的URL开头部分 int (*handler):URL对应的回调函数 int flags:URL处理优先级标志,有如下的两个选择: #define WEBS_HANDLER_FIRST 0x1:所有的数据包都会通过该回调函数进行处理 #define WEBS_HANDLER_LAST 0x2:没有回调函数匹配的数据包会通过该回调函数进行处理 如下是一个设备DIR-878,固件版本1.02B02中的GoAhead反编译代码,可以看到GoAhead对于数据包是否已经通过认证,是通过注册一个flags=WEBS_HANDLER_FIRST=1的回调函数websSecurityHandler来进行验证的,这意味着所有的数据包都会通过函数websSecurityHandler进行处理,验证数据包发送者的权限。 websUrlHandlerDefine((int)"/", 0, 0, (int)websSecurityHandler, 1); websUrlHandlerDefine((int)"/HNAP1/", 0, 0, (int)websFormHandler, 0); websUrlHandlerDefine((int)"/cgi-bin", 0, 0, (int)websCgiHandler, 0); websUrlHandlerDefine((int)&unk_497DEC, 0, 0, (int)websDefaultHandler, 2); 例如对一个请求的完整处理过程:使用POST请求访问/HNAP1/,...

2024年04月03日 · 9 分钟 · OneShell

探索 DBus 跨进程消息传递中的安全风险

一、前言 D-Bus (Desktop Bus)是一个用于在 Linux 和 Unix 系统上进行进程间通信的消息总线系统,它提供了一种机制,使得软件组件可以互相交流、传递消息和调用服务。 尽管 D-bus 最初是为桌面环境设计的,但它的通信机制和功能使其在非桌面环境中同样适用,以下是一些非桌面环境中使用 D-Bus 的例子: 服务间通信:D-Bus 可以用作服务之间进行通信的机制,无论是在服务器环境、嵌入式系统还是其他非桌面应用中。它可以帮助不同的服务或守护进程相互交流和协调工作。 系统管理:D-Bus 在系统管理领域也有广泛的应用。例如,系统服务可以使用 D-Bus 在后台进行通信,以便进行配置、监控和控制。这对于系统管理工具、系统监控应用和自动化任务非常有用。 嵌入式系统:D-Bus 在嵌入式系统中也可以发挥作用。它可以用于不同的组件之间进行通信,如硬件驱动程序、系统服务和用户应用程序。通过使用 D-Bus,这些组件可以共享信息、传递事件和协调操作。 IoT(物联网)设备:D-Bus 在物联网设备中的应用也在增加。它可以用于不同设备之间的通信,例如智能家居设备、传感器、控制器等。通过使用 D-Bus,这些设备可以相互通信、共享数据和提供服务。 然而,就像任何其他的通信协议一样,D-Bus通信也存在一些安全风险。本文将介绍 D-Bus 的通信机制,并分析其中的安全问题。 二、D-Bus 通信 2.1 D-Bus 通信背景知识 D-Bus 消息总线:D-Bus 使用消息总线作为通信的中心枢纽,允许不同进程之间消息传递。 总线名称和对象路径:每个 D-Bus 消息都与一个特定的总线名称和对象路径相关联,以确定消息是由哪个进程发送和接收。 接口和方法调用:D-Bus使用接口和方法调用的概念,进程可以调用其它进程公开的方法来进行通信。 D-Bus 通信比较常见,很多系统设置相关的操作都会触发 d-bus 通信。比如:修改用户头像操作,可以通过命令行在发送的 D-Bus 消息实现。 dbus-send --system --print-reply --dest=org.freedesktop.Accounts /org/freedesktop/Accounts/User1000 org.freedesktop.Accounts.User.SetIconFile string:/home/fish/Pictures/1.jpg 2.2 D-Bus 消息介绍 D-Bus 消息由header和body组成,hedaer包含消息的基本信息,包括发送进程的链接名,方法,消息类型等。 消息类型有以下四种: CALL方法调用:发起进程发出的消息; REPLY 方法返回:方法调用返回的结果; ERROR 消息:方法调用返回一个异常; SIGNAL 消息:通知,广播消息。 消息结构如下图所示: 2.3 D-Bus Hello 消息 在 D-Bus 通信中,第一个数据包被称为 “Hello” 消息。它是在客户端应用程序连接到 D-Bus 守护进程时发送的。...

2024年03月27日 · 3 分钟 · dri3dfi5h

Windows hypervisor&内核调试的几种常见/不常见方法

一、前言 本文主要介绍了使用调试器对Windows操作系统的内核层和hypervisor层进行双机调试的几种常见和不常见的方法。本文中使用的windbg调试器和其附带的实用调试工具都可以在windows sdk安装包中选择安装,windows sdk安装包官方的下载地址是: (https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/)。 有需要的读者可以自行下载并安装。 二、串口调试 首先我们先来介绍使用VMware虚拟机的情况下如何使用串口进行双机调试Windows内核及hypervisor。 在VMware的虚拟机设置中,添加"串行端口"设备后,并设置"串行端口"设备"使用命名的管道",这里命名管道的名称可以自己设定,并分别选择"该端是服务器"和"另一端是应用程序"。如下图。 然后我们在被调试机中设置bcdedit参数,这里的目的是在系统启动过程中添加debug参数。如下图。 在被调试机中我们分别使用bcdedit /dbgsettings serial debugport:1 baudrate:115200和 bcdedit /hypervisorsettings serial debugport:1 baudrate:115200命令将Windows内核和hypervisor的调试参数设置为串口调试,串口为com1,波特率为115200。然后再使用bcdedit /debug on和bcdedit /set hypervisordebug on命令分别开启windows内核和hypervisor层的调试。最后设置dbgtransport为kdcom.dll,这里是为了保证被调试机在系统启动过程中使用串口进行调试。 现在被调试机已经整装待发做好了被调试的准备,但调试机还需要一些配置。因为我们需要同时调试windows的内核和hypervisor,而且在被调试机的参数中使用了同一个串口(com1)作为调试串口,所以需要将不同层级的调试数据分发,根据不同层级将调试数据分发到不同的命名管道。我们可以使用& 'C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\vmdemux.exe' -src pipe:pipename=com2命令实现这一过程。 成功运行如上命令后,vmdemux进程会自动生成两个命名管道\\.\pipe\Vm0和\\.\pipe\Vm1。分别使用& 'C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe' -k com:port=\\.\pipe\Vm0,pipe,resets=0,reconnect和& 'C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe' -k com:port=\\.\pipe\Vm1,pipe,resets=0,reconnect命令打开windows hypervisor和内核的调试窗口。如下图。 下面我们介绍在双实体机的情况下如何使用串口进行双机调试。 双实体机进行串口调试需要被调试机的主板上保留9针串口,在10年前的电脑主板上,串口几乎是标准配置,然而随着主板厂商的革新,主板串口也渐渐退出历史舞台。 除了需要主板中保留串口外,还需要拥有一条串口调试线:Null-modem线,或者准备一条2,3交叉线。关于Null-modem调试线的线序如下图,感兴趣的读者可以自己手动做一条。 当使用串口调试线连接好调试端和被调试端,就可以使用 & 'C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\vmdemux.exe' -src com:port=com2命令将不同层级的数据分发到指定的层级,实现windows内核和hypervisor调试。 三、网络调试 Windows内核和hypervisor调试中,可以使用网络进行调试,不需要特殊的调试线连接两台机器,网络调试大大方便了双机调试中的准备过程。 首先,配置被调试机bcdedit配置,这里假设我们的调试机IP地址为192.168.111.1,调试hypervisor的端口为52201,调试windows内核的端口是52202。运行如下图的命令,设置网络调试,最后将dbgtransport设置为kdnet.dll。 在图中可以看到,如果成功设置了网络调试后,会返回一个key,这个key是用来给调试机中的windbg连接被调试机使用的。这里这两个key要先记下来。 重启被调试机后,在调试机端使用& "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg.exe" -k net:port=52202,key=rq644uvs2p16.3ked98d1isrrq.hr7oioflkdt2.37b29ko4f79yj和& "C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\windbg....

2024年03月20日 · 2 分钟 · hongzhenhao

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