米米的博客

做了一点微小的工作

Proxmark3 是一款 RFID 工具,支持多种协议和频段,常用于 IC、ID 卡的研究、破解、仿制和安全测试等场景。Proxmark3 的硬件包括主控板和天线,主控板上有 ARM 架构的处理器和用于调制解调的 FPGA。软件则包含运行在 ARM 处理器上的固件和运行在计算机的客户端软件,两者需要版本匹配才能工作。在淘宝上有许多商家售卖 Proxmark3,且会附送软件。然而,笔者注意到,原版的 Proxmark3 软件是以 GPL 协议开源的,但卖家提供的软件通常只有二进制形式,而没有包含源代码,存在违反开源协议的嫌疑。为了支持开源事业,笔者直接从闲鱼上以较低的价格购买了 Proxmark3 硬件,并尝试自行刷写固件。

现在社区中较为活跃的 Proxmark3 软件是由 RfidResearchGroup 团队维护的,其仓库地址为 https://github.com/RfidResearchGroup/proxmark3。由于 Chris "Iceman" Herrmann 是主要的开发者,因此这一版本通常被称为 Iceman(冰人)版本。

Iceman 软件支持多种不同的 Proxmark3 硬件,包括 RDV4、Easy 等。在编译使用 Proxmark3 之前,需要先确认 Proxmark3 的硬件版本。如果硬件和软件版本不匹配,可能在烧录固件或者在使用时报错,此时重新编译软件并烧录正确的固件即可解决。不同版本的 Proxmark3 外观可参考下图,截取自 Proxmark3 网站。市面上比较常见的是 Proxmark3 Easy 版本。RDV4 版本功能更加强大,但是价格也更高,售价通常超过 1000 元。

不同的Proxmark3硬件

编译 Iceman 版本的 Proxmark3 软件

Iceman 软件可以在 Linux、Windows、macOS 等系统上编译和运行,本文以 Linux 为例。

首先,将仓库克隆到本地:

1
git clone https://github.com/RfidResearchGroup/proxmark3

然后进入目录,执行编译命令:

1
2
3
cd proxmark3
make clean
make all PLATFORM=PM3GENERIC

make all 默认的配置是编译兼容 RDV4 硬件版本的软件,然而现在市面上常见的 Proxmark3 都并不是这一硬件版本。因此,需要指定 PLATFORM 参数,将编译目标设置为 PM3GENERIC,也就是 Generic Proxmark3 platforms。

编译完成后,待烧录的固件将会生成在 recovery 目录下。client 目录下则会出现 proxmark3 可执行文件,这就是客户端软件,是一个命令行工具。仓库根目录下的 pm3 脚本会执行 proxmark3,本文后续的操作也都是基于这个工具进行的。如果要使用图形界面,需要单独下载额外的 GUI 程序。

阅读全文 »

在 macOS 上的「照片」App 中,通过智能相簿功能可以方便地管理照片。在创建智能相簿时,可以设定一系列规则,根据相机型号、拍摄日期等条件筛选照片,然后将这些照片添加到相册中。当相册中有新照片加入时,符合条件的照片也会自动添加到智能相簿中。

智能相簿

但是,目前智能相簿所支持的条件仍然有一定的局限性。例如,笔者想要筛选出来自 iPhone 6S 的照片,即使设置了相机型号规则,也还有大量的手机截屏没有被包含进来。造成问题的原因是手机截屏照片的 EXIF 中并不包含相机型号信息。如果需要筛选这些截屏,使用分辨率作为判断依据是更好的,可惜智能相簿目前还不支持对分辨率进行筛选。类似这样的需求还有很多,有没有办法通过自动化的方式进行处理呢?答案是肯定的,使用 Applescript 编程即可实现。

Applescript 是 macOS 系统自带的脚本语言,可以用于控制系统内置应用程序,实现自动化操作。对于「照片」App,Applescript 可以用于获取照片信息、创建相册、添加照片等操作。下面是一个简单的例子,用于筛选 iPhone 6S 的截屏照片,并将其添加到一个名为「iPhone 6S Screenshot」的相册中。

运行这段脚本的方法是,打开「脚本编辑器」App,将脚本粘贴到编辑器中,然后点击运行按钮。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
tell application "Photos"
log "Start..."

-- 创建一个新相册,如果相册已存在则使用现有相册
if not (exists album "iPhone 6S Screenshot") then
make new album named "iPhone 6S Screenshot"
end if
set targetAlbum to album "iPhone 6S Screenshot"

-- 定义目标分辨率
set targetWidth to 750
set targetHeight to 1334

-- 初始化图片列表
set imageList to {}

-- 获取所有照片
set allPhotos to every media item

-- 循环检查每张照片的分辨率
repeat with aPhoto in allPhotos
if width of aPhoto is targetWidth and height of aPhoto is targetHeight then
-- 打印照片的信息,例如文件名
log "Matching photo: " & filename of aPhoto & ", Date: " & date of aPhoto
-- 将符合分辨率的照片添加到列表
set end of imageList to aPhoto
end if
end repeat

-- 如果列表不为空,将照片添加到相册
if (count of imageList) > 0 then
-- 打印符合条件的照片数量
log "Number of matching photos: " & (count of imageList)
add imageList to targetAlbum
else
log "No matching photos found."
end if
end tell

如果一切顺利,脚本编辑器的输出窗口中会显示符合条件的照片信息。运行完成后,「照片」App 也会弹出提示,让用户确认操作。

这个例子只是一个简单的示例,实际应用中可以根据需求进行扩展,例如添加更多的筛选条件、自动化处理照片等。现有的大模型也可以帮助编写 Applescript 的代码。不过,在通过 Applescript 自动化操作其它 App 时,错误的代码逻辑可能会导致数据丢失,因此需要仔细检查代码并做好备份。

近期,笔者接到了维修一台屏幕完全碎裂的 iPad 的请求。根据机主描述,这台 iPad 在多年前由于从高处跌落,外屏完全损坏,无法开机,因此长期没有使用。但是里面有一些重要的照片,希望能够取出来。

经过检查,这台 iPad 型号为 iPad Mini 1(A1432 版本)。机器状况确实非常不理想,不仅屏幕碎裂,后壳也有明显的变形。只能祈祷主板没有受损,不然就超出笔者的维修能力了。

变形的机身和内屏,外屏由于完全碎裂爆出了很多玻璃渣,已经丢弃

这一代 iPad 的外屏是通过胶水粘合的,正常的拆卸方法是加热后将屏幕揭开,但由于外屏已经完全脱落,这一步骤甚至可以省略。进一步拆卸可以参考 iFixit 发布的指南,并不麻烦,主要就是拧螺丝和拆排线,谨慎操作即可,避免损坏一些脆弱的部件。经过拆卸,这台机器的主板外观还比较完好,变形也比较轻微,看上去还能抢救一下。但是,除了主板外,其它很多硬件都存在不同程度的受损。

拆卸出的主板很完整,但电池已经变形了

相比于单独购买屏幕、电池等配件进行替换,更方便的方法是直接买一台好的 iPad,来一手借尸还魂,把坏机器的主板挪过去。在闲鱼上,同款的 iPad Mini 1 已经是白菜价格,外观成色完美的也就大几十块钱,因此这个方案是经济可行的。

硬件维修的部分还算顺利,经过拆机和更换硬件,这台 iPad Mini 1 终于能够正常开机了。

拆机过程

点亮设备

接下来才是真正有趣的部分:这台 iPad 设置了锁屏密码,但是由于多年没有开机,机主已经忘记了密码。如果要将其中的数据备份出来,就得想办法访问到 iPad 的文件系统了。如果设备在越狱后开启了 SSH 服务,我们可以通过 libimobiledevice 提供的 usbmuxd 转发端口,使用 SSH 连接来访问文件系统。但遗憾的是,经过简单的实验,这台 iPad 似乎并没有越狱,SSH 服务也没有启动,这条路行不通。

由于 iPad Mini 1 是非常古早的设备,想必是有很多现成的 n-day 漏洞可以利用。其中,最著名的就是 2019 年由 iOS 安全研究员 axi0mX 披露的 checkm8 漏洞(这个缩写类似 DotA2 中的 str8,8=eight 是读音的缩写,原意即为 checkmate)。checkm8 漏洞存在于 iOS 设备的 ROM 中,通过 USB 发送恶意的 Payload,可以实现任意代码执行,绕过设备的安全启动限制。这个漏洞影响了搭载 A5 到 A11 芯片的苹果设备,并且苹果无法通过软件更新来修复这个漏洞,非常适合我们的场景。不过,由于 checkm8 漏洞利用需要通过 USB 进行,目前针对 A5 芯片最成熟的方案是使用 Arduino USB Host Shield,这需要我们准备特殊的硬件。

阅读全文 »

CVE-2023-21537 是一个 Windows 消息队列(MSMQ)驱动程序 mqac.sys 中的漏洞。该漏洞于 2023 年 1 月披露,并已被微软修复。漏洞并没有公开的 PoC 程序,漏洞发现者只通过文章 Racing bugs in Windows kernel 透露了部分信息。笔者在其基础上深入分析了相关代码,成功地复现了此漏洞。本文就是对漏洞研究的总结。

漏洞复现环境搭建

由于此漏洞已经在较新的系统中被修复,复现漏洞需要在旧版本的系统中进行。笔者在 Hyper-V 虚拟机中安装了 Windows 10 21H1 版本,内部版本号为 19043.928。此外,由于消息队列是 Windows 的可选功能,需要在控制面板中手动启用。方法是:打开控制面板,选择「程序」,点击「启用或关闭 Windows 功能」,并开启「Microsoft 消息队列(MSMQ)服务器」。如下图所示。

启用消息队列

之后,打开计算机管理,在侧栏中选择「服务和应用程序」,点开「消息队列」下面的「专用队列」,并右键新建专用队列。队列名称需要记下来,之后的 PoC 程序里面需要用到。完成后,情况如下图所示。

新建专用队列

为了方便调试 PoC 程序,在虚拟机中还可以开启内核调试,这样就可以通过 Host 上的 WinDbg 给虚拟机的 Windows 内核下断点进行调试了。方法是开启管理员权限的 Powershell,执行如下命令

1
2
bcdedit /debug on
bcdedit /dbgsettings net hostip:192.168.1.2 port:50001 key:1.2.3.4

hostip 换成 Host 的 ip 地址,portkey 可以修改。完成后,重启虚拟机,之后在 WinDbg 里面用这些参数就可以对虚拟机的内核进行 debug 了。

漏洞分析

根据漏洞发现者的文章,该漏洞的成因是 mqac.sys 中的 ACSendMessage 函数会两次读取一个来自用户的输入参数,第一次该参数用于控制数组长度,第二次则是在释放堆内存时,根据该长度进行释放。然而,这段逻辑并未考虑参数会被用户修改的可能,因而构成一个 Double fetch 漏洞,可能导致错误的内存被释放。

mqac.sys 提供了 IoControl 调用的处理函数,名称为 ACDeviceControl,该函数将会解析用户传入的参数,并调用不同的派发函数。通过逆向分析 ACDeviceControl 函数,发现当 IoControl 调用号为 0x19658107 且输出缓冲区的总长度为 0x2C0 时,它会进一步调用 ACSendMessage 这一派发函数。IDA 反编译得到的关键代码如下(省略了部分上下文):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
__int64 __fastcall ACDeviceControl(struct _DEVICE_OBJECT *DeviceObject, struct _IRP *Irp)
{
struct _IO_STACK_LOCATION *CurrentStackLocation Irp->Tail.Overlay.CurrentStackLocation;
unsigned int LowPart = CurrentStackLocation->Parameters.Read.ByteOffset.LowPart;
__int64 Length = CurrentStackLocation->Parameters.Read.Length;
const struct CQueueBase *FsContext = CurrentStackLocation->FileObject->FsContext;
NTSTATUS Information;
// ...
switch ( LowPart )
{
case 0x19658107u:
if ( (_DWORD)Length == 704 )
{
Information = ACSendMessage(DeviceObject, Irp, Options, FsContext, (struct CACSendParameters *)UserBuffer);
goto LABEL_246;
}
// ...
}
// ...
}

ACSendMessage 函数首先将用户态缓冲区复制到内核态栈上的缓冲区,之后,将执行核心的业务逻辑,调用 CQueue::PutNewPacket 来发送用户请求的数据,完成后再调用 ACFreeDeepCopyQueueFormat 进行堆内存的释放。此处便存在漏洞:进行内存释放操作传入的第二个参数直接读取自用户态缓冲区中。下面是 IDA 反编译得到的关键结果,参数 UserBuffer 就是指向用户缓冲区的指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
__int64 __fastcall ACSendMessage(
struct _DEVICE_OBJECT *DeviceObject,
struct _IRP *Irp,
unsigned int Options,
const struct CQueueBase *FsContext,
struct CACSendParameters *UserBuffer)
{
const struct BOID *v15;
PVOID Contents[36];
memset(Contents, 0, 0x118ui64);
_DWORD *DeviceExtension = DeviceObject->DeviceExtension;
int v9 = CQueueBase::Validate(FsContext);
if ( v9 >= 0 )
{
if ( DeviceExtension[246] )
{
ACDeepProbeSendParams(DeviceObject, UserBuffer, (struct ACSendParametersPointerContents *)Contents);
v15 = (const struct BOID *)*((_QWORD *)UserBuffer + 45);
}
// ...
if ( Contents[23] )
ACFreeDeepCopyQueueFormat((char *)Contents[23], *((_DWORD *)UserBuffer + 148));
// ...
}
// ...
}

这样问题看上去就很明确了:我们首先获得指向 MQAC 虚拟设备的句柄,然后向其发送号码为 0x19658107 的 IoControl 调用,并通过调整参数使 ACFreeDeepCopyQueueFormat 函数能够执行。这又要满足 Contents[23] 不为零的约束条件。观察之后发现 Contents 先被初始化为了全 0,然后传入了 ACDeepProbeSendParams 这个函数里面,所以还要设法让这个函数帮我们修改 Contents[23],才能触发漏洞。

阅读全文 »

内网穿透是一种反向代理技术,允许你从任何位置访问一台处于复杂网络环境中的机器。它的应用场景有很多,例如,你在家中使用树莓派作为 Home Assistant 家庭中枢,但家庭网络的网关没有公网 IP,那么在离开家时就无法控制家中的智能设备。而在配置内网穿透后,就可以随时随地通过代理服务器的公网 IP 连接到树莓派,从而访问 Home Assistant 的服务。

内网穿透的原理是,利用一个具有固定公网 IP 的主机做跳板,让两个客户端建立 P2P 连接;如果不成功,就通过跳板主机中转,将所有流量代理到内网中的主机上。为不同需求所设计的内网穿透解决方案有很多,例如 frp 和 Cloudflare Tunnel。frp 允许你将内网中主机的端口映射到公网的主机上,因此使用起来非常直接。而 frp 的缺点是主机和端口都需要单独配置,如果要用多台设备组网,会有些繁琐;此外,这些端口都会直接暴露在公网上,安全性较差。另一个方案 Cloudflare Tunnel,可以直接用 Cloudflare 的节点进行中转,是非常不错的选择,但缺点和 frp 类似,在多台设备需要互相访问时很麻烦。

经过实验,如果需要将不同网络环境中的多台设备都配置内网穿透,并能够互相访问,那么使用 ZeroTier 搭建虚拟局域网是一个不错的选择。下面笔者将会介绍具体的配置方法。

准备

首先,打开 ZeroTier 官网,注册账号,然后根据使用的设备系统下载客户端并安装。ZeroTier 安装完成后,将会创建一个虚拟网卡。

在 ZeroTier 客户端中,输入 Network ID 并选择加入,你的设备就将分配到一个 ZeroTier 虚拟网络的 IP 地址。将需要内网穿透进行联网的设备都安装好 ZeroTier 客户端,并加入同一个 Network ID 的网络,就可以通过分配的 IP 地址互相访问了。就是这么简单!

阅读全文 »
0%