首页
社区
课程
招聘
[原创]Tenda 路由器栈溢出复现(CVE-2018-18708)详解
发表于: 2024-7-1 00:25 5417

[原创]Tenda 路由器栈溢出复现(CVE-2018-18708)详解

2024-7-1 00:25
5417

本文,主要是iot新手的第一次漏洞复现,相较于前辈的参考文章多了一些解释,更易食用
文章参考[原创]Tenda 路由器栈溢出详细分析(CVE-2018-18708)-智能设备-看雪-安全社区|安全招聘|kanxue.com

cve-list中的报告

CVE - CVE-2018-18708 (mitre.org)

image-20240531195539381

e7eK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1L8%4N6F1i4K6u0W2N6r3g2F1k6r3q4Q4x3X3g2U0L8$3#2Q4x3X3g2U0L8W2)9J5c8Y4g2H3L8r3!0S2k6r3k6A6L8r3g2Q4x3V1k6m8b7K6p5#2i4K6u0r3g2g2y4Q4y4h3k6m8b7K6p5#2g2U0q4Q4x3X3f1H3b7W2u0Q4y4h3k6h3x3e0g2Q4x3X3f1H3x3#2)9J5k6e0l9#2i4K6u0W2x3e0W2Q4y4h3k6E0N6h3I4@1K9g2)9#2k6W2c8p5x3o6q4Q4x3X3g2*7K9i4l9`.

嵌入式系统通常运行在资源受限的环境中,所以需要高效的存储利用和只读特性

高压缩比

嵌入式设备通常具有有限的存储空间

SquashFS通过高效的压缩算法(如gzip、LZMA、XZ等)

只读文件系统

快速启动

一致性和完整性

固件分发和升级

只读根文件系统

Live系统和恢复系统

嵌入式Linux发行版

首先,准备要打包的文件系统目录,例如/rootfs,然后使用mksquashfs工具创建SquashFS镜像:

将生成的/rootfs.img烧录到嵌入式设备的存储中,例如,通过TFTP或其他方法将其传输到设备上。

在设备启动时,通过启动脚本挂载SquashFS镜像:

readelf 工具查看名为 busybox 的可执行文件的 ELF 文件头信息

readelf 是一个分析 ELF (Executable and Linkable Format) 格式文件的工具

-h 参数表示仅显示 ELF 文件的头部信息

image-20240525152929504

==32 位的小端序 ARM 可执行文件,采用ida反编译==

这个时候要注意命令执行文件夹位置,一定要在前面提取出来的地方运行程序

下载虚拟化套件

复制到当前目录下

解释一下这里运行的指令:

chroot ./: 这个命令改变了新进程的根目录为当前目录 (./)

新的根目录下的文件和目录会对于chroot环境中的程序来说,就如同是在一个全新的系统中

./qemu-arm-static,传递给chroot环境运行的第一个程序

即是运行了 httpd 程序

运行效果:

image-20240527194052092

但是这里我们的程序进入后就没法运行了,要进入ida分析一下问题

网上看到了这个报错

<img src="de0K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4x3i4u0X3k6X3g2Q4x3X3g2G2M7%4y4Q4x3X3c8U0L8W2)9J5k6r3y4Z5k6h3&6Y4k6s2g2Q4x3X3g2S2L8r3W2&6N6h3&6U0M7#2)9J5k6h3y4G2L8g2)9J5c8Y4c8&6M7r3!0J5j5g2)9J5c8X3W2E0j5h3N6W2i4K6u0V1x3U0l9J5y4o6l9#2x3U0M7I4z5e0x3^5x3U0l9#2z5o6N6Q4x3X3g2H3L8X3M7`." alt="image-20240527193820587" style="zoom:150%;" />

表示不存在/proc/sys/kernel/core_pattern这样一个文件夹

接直接再创建一个这样的文件就行了

==(这个不是漏洞文件,只是分析一下固件的一些逻辑)==

地址:

(busybox运行iot固件的关键逻辑)

image-20240527194759229

使用finger恢复没什么用,算了影响不大(就是想试试,八成是没啥用的)

image-20240527195610392

image-20240527200203791

image-20240527204733918

image-20240527201223314

image-20240527204656345

没什么意思,但是整理一下:

密码长度检查

检查密码长度是否大于 5 个字符

密码与用户名相似性检查

使用 sub_5125C 函数检查密码是否与用户名相似

密码与 gecos 字段相似性检查

使用 sub_5125C 函数检查密码是否与 gecos 字段(一般存储用户的个人信息)相似

密码与主机名相似性检查

使用 sub_5125C 函数检查密码是否与主机名相似

密码字符组成检查

通过遍历密码中的每个字符,检查密码是否包含小写字母、大写字母和数字,并将结果存储在 v9

相似字符检查

检查密码中是否有过多的重复字符

密码强度检查

根据 v9 的值计算密码的强度,并检查密码是否足够强

与旧密码相似性检查

使用 sub_5125C 函数检查密码是否与旧密码相似

image-20240527203459989

看看有没有提取时候对他进行混淆,很遗憾没有找到,难道没有?

只找到这个地方对文件中的密码进行检索修改

这里主要就是一个登陆界面的密码检查和修改相关程序

修改后的idb分析文件11cK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3N6A6M7X3q4X3k6X3g2^5K9i4g2Q4x3X3g2D9L8%4k6W2i4K6u0r3N6%4m8Q4x3X3c8U0L8$3&6@1k6h3&6@1i4K6u0r3N6i4m8D9L8$3q4V1M7#2)9J5c8U0t1H3x3U0c8Q4x3V1j5H3y4g2)9J5c8X3u0#2M7%4W2T1L8%4S2Q4x3X3g2*7K9i4l9`.

还是一样的地址

image-20240527211530678

image-20240527211741458

==这里实现了网络连接检查、初始化网络设置、与内核通信、信号处理和主循环的执行==

==这个地方调用了check_network函数,如果检查不过就会跳转到蓝线指向的流程进行,然后再次无条件跳转回去到loc_2E504再次指令流程==

进入一个死循环导致无法进行后续的流程

image-20240527215548890

这里的绕过很简单,我们这里因为如果检查不通过就会给R0返回0 ,然后将R0赋给R3,然后比较如果R3等于0,就会进入死循环

这里我们尝试使用patch bytes将R3直接设置为1,永真就不会进入循环了

image-20240527220937860

要将这里的数据改成MOV R3, #1

image-20240527221005865

注意这里的虚拟环境运行

image-20240527221425567

keystone-engine/keypatch: Multi-architecture assembler for IDA Pro. Powered by Keystone Engine. (github.com)

image-20240527222427644

可以看到这里调用了Cfm函数检查网络的连接状况,跟前面逻辑类似,如果返回的R0为0则跳转到红线的函数

image-20240528191346700

这里跳转后你会直接无条件跳转到函数结束的地方

image-20240528191738652

和上面一样将mov的R0换成#1,变成永真后保存就可以了

image-20240528192439951

image-20240527222818521

image-202405272229245535.替换文件

将原本文件夹内的文件替换为修改后的文件

image-20240528210730058

image-20240528211050592

==ip地址一看不对没法访问,分析调用逻辑==

==字符串定位找到关键调用函数==

image-20240528212409168

==创建并配置一个网络套接字,以在指定的 IP 地址和端口上监听连接请求==

第一次用gdb调试异架构程序顺便详细记录一下

image-20240528215827996

image-20240528222127566

image-20240528222138558

交叉引用这里的inet_ntoa回调函数进行了对于ip的处理

<img src="5bfK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4x3i4u0X3k6X3g2Q4x3X3g2G2M7%4y4Q4x3X3c8U0L8W2)9J5k6r3y4Z5k6h3&6Y4k6s2g2Q4x3X3g2S2L8r3W2&6N6h3&6U0M7#2)9J5k6h3y4G2L8g2)9J5c8Y4c8&6M7r3!0J5j5g2)9J5c8X3W2E0j5h3N6W2i4K6u0V1x3U0l9J5y4o6l9#2x3U0V1I4y4o6x3%4x3K6R3@1y4o6g2Q4x3X3g2H3L8X3M7`." alt="image-20240529143738445" style="zoom:150%;" />

image-20240529144243744

打开一个SSL套接字,用于处理HTTPS请求

这个函数主要是处理SSL的socket,处理http请求,跟ip的生成没有太大的关系

==调用sub_1B84C作用是指定端口上创建一个 SSL 套接字==

成功,则返回一个非负的套接字标识符

失败,则返回 -1 并打印错误信息

==一个网络套接字(Socket)的函数==

解释一下这个地方的意思:

端口号:端口号是一个 16 位的数字,用来标识网络上的特定服务。比如,HTTP 通常使用端口 80,HTTPS 使用端口 443。

套接字:套接字是一个网络编程接口,用于在不同设备之间进行数据传输。它可以看作是通信的终点,包括一个 IP 地址和一个端口号。

创建套接字:在指定端口 a1 上创建一个套接字意味着在这个端口上为应用程序配置一个通道,通过这个通道可以接收和发送数据。这个过程通常包括以下步骤:

==sub_29818 尝试在指定端口 a1 上创建并配置一个套接字,如果成功,服务器就可以通过这个套接字接收客户端的 HTTP 请求==

初始化:清空一个临时字符数组 s,并将全局局域网 IP 地址赋给局部变量 v8

尝试打开套接字:在一个循环中调用 sub_1B84C 函数,尝试在指定的 IP 地址和端口 a1 上创建一个套接字。如果成功(返回值大于等于0),跳出循环

成功处理

失败处理

这个函数调用sub_1B84C函数作用:

==创建一个网络套接字,并在指定的 IP 地址 v8 和端口 a1 上监听 HTTP 请求==

image-20240529151157119

image-20240529152559026

发现主要是四个地方在调用

image-20240529152847965

image-20240529153024165

==确保设备在局域网中的 IP 地址能够被正确地获取并存储在全局变量中,以便后续的网络操作和通信==

在调用 inet_aton 函数时,将全局变量 g_lan_ip 的值作为参数传递给了该函数的第一个参数,即将设备在局域网中的 IP 地址转换为了一个 struct in_addr 结构体中的值 inp

==交叉引用这个函数,发现在主函数初始化等这个g_lan_ip参数,再调用这个函数进行处理g_lan_ip==

image-20240529194504945

image-20240529194818633

image-20240529195729469

image-20240529200003630

image-20240529195933848

来仔细看一下这个地方的inet_addr(a1)的调用

==将提供的IP地址字符串(如果有的话)转换为二进制形式,并存储在 sockaddr 结构体的 sa_data 字段中==

==如果没有提供IP地址字符串,则将 sa_data 的对应部分设置为0==

if (a1): 检查 a1 是否为非空指针

如果 a1 非空,说明它指向一个有效的字符串

*(_DWORD *)&s.sa_data[2] = inet_addr(a1);:如果 a1 非空,使用 inet_addr 函数将 a1 指向的字符串转换为网络字节序的二进制IP地址,并将该值存储到 s.sa_data[2]

具体来说,(_DWORD *)&s.sa_data[2] 是将 sa_data 中从偏移量 2 开始的4个字节解释为一个32位的无符号整数(DWORD),并将转换后的IP地址存储到该位置

else: 如果 a1 是空指针

说明没有提供有效的IP地址字符串

*(_DWORD *)&s.sa_data[2] = 0;:在这种情况下,将 s.sa_data[2] 的值设置为0

也就是说,将 s.sa_data 的从偏移量 2 开始的4个字节都设置为0

这表示使用默认的IP地址(通常为 INADDR_ANY,表示绑定到所有可用接口)

可以看到这里的v8对应前面函数传入的a1

v8就是 g_lan_ip

image-20240529201226181

这里的v17赋值给 g_lan_ip

image-20240529210052623

这里的函数综合处理了s和v17

==推测应该是getIfIp处理的ip生成==

<img src="c48K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4x3i4u0X3k6X3g2Q4x3X3g2G2M7%4y4Q4x3X3c8U0L8W2)9J5k6r3y4Z5k6h3&6Y4k6s2g2Q4x3X3g2S2L8r3W2&6N6h3&6U0M7#2)9J5k6h3y4G2L8g2)9J5c8Y4c8&6M7r3!0J5j5g2)9J5c8X3W2E0j5h3N6W2i4K6u0V1x3U0l9J5y4o6l9#2x3U0V1J5x3e0l9K6x3o6t1^5y4o6g2Q4x3X3g2H3L8X3M7`." alt="image-20240529210302845" style="zoom:150%;" />

image-20240529210433418

image-20240529211515335

找到这里的链接库

image-20240529211313816

路径在截图的上方

image-20240529211939389

image-20240529212218520

这里可以看见传入的两个参数分别是g_lan_ip和v17

可以发现这里的关键是申请ip的函数ioctl

<img src="a21K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4x3i4u0X3k6X3g2Q4x3X3g2G2M7%4y4Q4x3X3c8U0L8W2)9J5k6r3y4Z5k6h3&6Y4k6s2g2Q4x3X3g2S2L8r3W2&6N6h3&6U0M7#2)9J5k6h3y4G2L8g2)9J5c8Y4c8&6M7r3!0J5j5g2)9J5c8X3W2E0j5h3N6W2i4K6u0V1x3U0l9J5y4o6l9#2x3U0V1J5x3e0t1^5x3e0x3$3x3o6y4Q4x3X3g2H3L8X3M7`." alt="image-20240529212813603" style="zoom:200%;" />

ioctl 用于与设备驱动程序进行交互,可以用来获取或设置接口的各种参数

0x8915u 是一个具体的命令,用于获取接口的 IP 地址

SIOCGIFADDR 是一个 ioctl 命令,用于获取指定网络接口的 IP 地址

定义在 <sys/ioctl.h> 中,通常表示为 0x8915u

ifreq 结构定义在 <net/if.h> 中,用于传递网络接口相关信息:

ifr_name 用于指定网络接口名称,ifr_addr 用于存储获取到的 IP 地址

image-20240529215408628

调用getLanIfName函数

image-20240529215428918

image-20240529215720797

进入查看

image-20240529220658022

image-20240529220947224

这里看到传入的参数是0

image-20240529221223834

对应的 v1 = "br0";,所以这里要设置的第三个参数为网卡的名称为br0

==设置一个第三个参数为网卡的名称为br0==

操作:

在ubuntu的浏览器上直接访问ip就可以发现,可以成功连接上路由了

image-20240530142221925

根据官方文档在httpd文件内的sub_C24C0函数存在栈溢出漏洞

image-20240530145956444

image-20240530151433159

==这个地方strcpy不会检查字符串的大小,a1直接没有判断长度拷贝到了a2+32,从而可以实现栈溢出,更改函数的流程==

image-20240530152923860

交叉引用sub_C24C0,得到a2的大小

可以看到==a2的大小是176个字节==

image-20240530153004991

image-20240530165648650

image-20240530165744479

image-20240530165820633

image-20240530165903173

image-20240530170038932

这个函数相当于是在给每个网络相关的处理函数分配名字

image-20240530170443460

image-20240530170554465

image-20240530171109992

image-20240530171202347

image-20240530173430316

image-20240530172439895

image-20240530172530532

image-20240530172600686

==HTTP请求中deviceList的值,并一路传递到sub_c24C0函数的漏洞点==

这里详细解释一下:

这个地方很关键的一个理解

这两个函数共同解释注册表,这个地方就是goform/setMacFilterCfg地址的调用流程

image-20240531213941541

image-20240531213802977

image-20240531171820099

image-20240531172105846

image-20240531173530914

==a1的参数是black或者white就会将相应的值设置为macfilter.mode,并且让a1返回0==

当a2指针指向的字符串不为0时,将循环调用sub_C17A0

image-20240531194644768

这个地方的a2就是前面函数的v17(已经判断过他的生成逻辑,肯定不为空)

image-20240531194859864

没有判定条件,直接执行

image-20240531195122248

抓个damn

image-20240531215938316

结合前面的对于httpd的调用链的拼接和抓包的格式和字符串的溢出长度

image-20240602161144352

可以看见这里开启了NX保护,但是其他保护都没有打开,所以很好泄露

image-20240602182656206

image-20240602182727548

连接成功

image-20240602182914719

image-20240602183022212

<img src="77bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4x3i4u0X3k6X3g2Q4x3X3g2G2M7%4y4Q4x3X3c8U0L8W2)9J5k6r3y4Z5k6h3&6Y4k6s2g2Q4x3X3g2S2L8r3W2&6N6h3&6U0M7#2)9J5k6h3y4G2L8g2)9J5c8Y4c8&6M7r3!0J5j5g2)9J5c8X3W2E0j5h3N6W2i4K6u0V1x3U0l9J5y4o6l9$3x3o6t1I4z5o6x3I4x3e0j5H3y4K6W2Q4x3X3g2H3L8X3M7`." alt="image-20240602183116079" style="zoom:250%;" />

可以发现这个地方已经将连续的A字符串压入栈中,实现溢出,覆盖了pc寄存器和返回地址的数据

image-20240602183524429

==这里很悲伤的是qemu-user模拟不支持vmmap指令打印内存信息==

==qemu是默认关闭ASLR的,每次调试地址是一样的==

我们把断点下再这个地方的main函数puts的调用

image-20240603134246281

image-20240603134456298

这个地方就是puts函数

IDA中查看puts函数的地址为0x35cd4,得到偏移量为:0xff5c1cd4 - 0x35cd4 = 0xff58c000

image-20240603141919355

找到一个能用的就行

image-20240603142841025

找一个简单点的pop链

image-20240603143033860

引发一个缓冲区溢出,控制溢出处函数的返回地址

将这个地址更改为第一个gadget(pop {r3, pc})的地址(当函数返回时,它就会跳转到这个gadget开始执行)

pop {r3, pc}进行的操作:

它会从栈顶取出一个值放入 r3寄存器,然后再取栈顶的下一个值放入 pc寄存器

在栈顶放上 system_addr和第二个gadget的地址

执行这个gadget后,system_addr就被放入 r3寄存器,第二个gadget的地址放入 pc寄存器(程序就会跳转到第二个gadget开始执行)

执行第二个gadget(mov r0, sp ; blx r3)时,会从当前的栈顶(也就是 cmd的位置)取出一个值放入 r0寄存器

然后跳转到 r3寄存器中的地址(也就是 system函数)去执行

image-20240603170349628

sudo apt-get install binwalk
sudo apt-get install binwalk
sudo apt-g0et install binwalk
binwalk -Me US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin
sudo apt-g0et install binwalk
binwalk -Me US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin
cd _US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/
cd _US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/
cd squashfs-root/
cd squashfs-root/
mksquashfs /rootfs /rootfs.img
mksquashfs /rootfs /rootfs.img
mount -t squashfs -o loop /path/to/rootfs.img /mnt/rootfs
mount -t squashfs -o loop /path/to/rootfs.img /mnt/rootfs
readelf -h bin/busybox
readelf -h bin/busybox
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0xbf80
  Start of program headers:          52 (bytes into file)
  Start of section headers:          380400 (bytes into file)
  Flags:                             0x5000002, Version5 EABI, <unknown>
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         7
  Size of section headers:           40 (bytes)
  Number of section headers:         24
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           ARM
  Version:                           0x1
  Entry point address:               0xbf80
  Start of program headers:          52 (bytes into file)
  Start of section headers:          380400 (bytes into file)
  Flags:                             0x5000002, Version5 EABI, <unknown>
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         7
  Size of section headers:           40 (bytes)
  Number of section headers:         24
cd _US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/
cd _US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/
cd squashfs-root/
cd squashfs-root/
sudo apt install qemu-user-static
sudo apt install qemu-user-static
cp $(which qemu-arm-static) ./
cp $(which qemu-arm-static) ./
sudo chroot ./ ./qemu-arm-static ./bin/httpd
sudo chroot ./ ./qemu-arm-static ./bin/httpd
mkdir -p ./proc/sys/kernel
mkdir -p ./proc/sys/kernel
/home/iot/Desktop/iot-cve/_US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/squashfs-root/bin
/home/iot/Desktop/iot-cve/_US_AC15V1.0BR_V15.03.05.19_multi_TD01.bin.extracted/squashfs-root/bin
if ( a2 && (v6 = strlen(a2), v6 > 5) )
if ( a2 && (v6 = strlen(a2), v6 > 5) )
if ( sub_5125C(a2, *a3) )
{
    v21 = "similar to username";
}
if ( sub_5125C(a2, *a3) )
{
    v21 = "similar to username";
}
if ( *v7 && sub_5125C(a2, (int)v7) )
{
    v21 = "similar to gecos";
}
if ( *v7 && sub_5125C(a2, (int)v7) )
{
    v21 = "similar to gecos";
}
v8 = (void *)sub_53750();
v9 = sub_5125C(a2, (int)v8);
free(v8);
if ( v9 )
{
    v21 = "similar to hostname";
}
v8 = (void *)sub_53750();
v9 = sub_5125C(a2, (int)v8);
free(v8);
if ( v9 )
{
    v21 = "similar to hostname";
}
for ( i = 0; i < v6; ++i )
{
    v11 = (unsigned __int8)a2[i];
    if ( (unsigned __int8)(v11 - 97) > 0x19u )
    {
        if ( (unsigned __int8)(v11 - 65) > 0x19u )
        {
            if ( (unsigned __int8)(v11 - 48) > 9u )
                v9 |= 8u;
            else
                v9 |= 4u;
        }
        else
        {
            v9 |= 2u;
        }
    }
    else
    {
        v9 |= 1u;
    }
}
for ( i = 0; i < v6; ++i )
{
    v11 = (unsigned __int8)a2[i];
    if ( (unsigned __int8)(v11 - 97) > 0x19u )
    {
        if ( (unsigned __int8)(v11 - 65) > 0x19u )
        {
            if ( (unsigned __int8)(v11 - 48) > 9u )
                v9 |= 8u;
            else
                v9 |= 4u;
        }
        else
        {
            v9 |= 2u;
        }
    }
    else
    {
        v9 |= 1u;
    }
}
do
{
    v14 = strchr(v12, v11);
    v15 = v14;
    if ( !v14 )
        break;
    v12 = v14 + 1;
    ++v13;
}
while ( v15[1] );
if ( v6 <= 2 * v13 )
{
    v21 = "too many similar characters";
    goto LABEL_38;
}
do
{
    v14 = strchr(v12, v11);
    v15 = v14;
    if ( !v14 )
        break;
    v12 = v14 + 1;
    ++v13;
}
while ( v15[1] );
if ( v6 <= 2 * v13 )
{
    v21 = "too many similar characters";
    goto LABEL_38;
}
v16 = 4;
v17 = 14;
v18 = 1;
do
{
    v19 = (v18 & v9) == 0;
    v18 *= 2;
    if ( !v19 )
        v17 -= 2;
    --v16;
}
while ( v16 );
if ( v6 < v17 )
{
    v21 = "too weak";
}
v16 = 4;
v17 = 14;
v18 = 1;
do
{
    v19 = (v18 & v9) == 0;
    v18 *= 2;
    if ( !v19 )
        v17 -= 2;
    --v16;
}
while ( v16 );
if ( v6 < v17 )
{
    v21 = "too weak";
}
if ( !*a1 || !sub_5125C(a2, (int)a1) )
    return 0;
v21 = "similar to old password";
if ( !*a1 || !sub_5125C(a2, (int)a1) )
    return 0;
v21 = "similar to old password";
int __fastcall sub_F804(int a1, int a2, int a3)
{
  // 声明一些局部变量
  int v4; // r6
  int v5; // r0
  struct passwd *v6; // r4
  char *v7; // r0
  char *v8; // r5
  char *v10; // r6
  int v11; // r7
  size_t v12; // r0
  char *pw_shell; // r0
  int v14[7]; // [sp+4h] [bp-1Ch] BYREF
 
  // 一系列初始化操作
  v14[1] = a3;
  dword_6CCF8 = 3;
  v14[0] = 0;
   
  // 获取系统信息
  openlog((const char *)dword_6DECC, 0, 32);
  
  // 解析环境变量
  dword_6DEF4 = (int)"t+";
  sub_4D454(a2, "t:", v14);
   
  v4 = optind;
  if ( *(_DWORD *)(a2 + 4 * optind) )
  {
    // 关闭文件描述符 0,1
    close(0);
    close(1);
     
    // 从文件中读取数据
    v5 = sub_D728(*(_DWORD *)(a2 + 4 * v4), 2);
     
    // 创建复制文件描述符
    dup(v5);
    // 关闭文件描述符2
    close(2);
    // 创建一个新的文件描述符,复制已存在的文件描述符
    dup(0);
  }
   
  if ( !isatty(0) || !isatty(1) || !isatty(2) )
  {
    // 如果任一文件描述符(012)不是终端设备,则显示错误信息
    dword_6CCF8 = 2;
    sub_D03C((int)"not a tty");
  }
  
  sub_50BA4();
  // 获取root用户的信息
  v6 = getpwuid(0);
   
  if ( !v6 )
    // 如果没有找到root用户,显示错误信息
    sub_D03C((int)"no password entry for root");
     
  while ( 1 )
  {
    // 询问输入root用户的密码,接收输入的密码
    v7 = (char *)sub_4BFDC(
                   0,
                   v14[0],
                   "Give root password for system maintenance\n(or type Control-D for normal startup):");
    v8 = v7;
    
    if ( !v7 || !*v7 )
      break;
       
    // 验证输入的密码是否正确
    v10 = (char *)sub_52C4C(v7, v6->pw_passwd);
    v11 = strcmp(v10, v6->pw_passwd);
    // 释放动态分配的内存
    free(v10);
     
    if ( !v11 )
    {
      v12 = strlen(v8);
      // 清空输入的密码字符串
      memset(v8, 0, v12);
      // 如果密码正确,记录系统维护模式
      sub_4E414("System Maintenance Mode");
       
      // 获取环境变量 SUSHELL 或者 sushell 的值,如果这两个都没有定义,就使用 root 的默认 shell
      pw_shell = getenv("SUSHELL");
      if ( !pw_shell )
      {
        pw_shell = getenv("sushell");
        if ( !pw_shell )
          pw_shell = v6->pw_shell;
      }
       
      // 启动一个新进程运行 shell
      sub_53658(pw_shell, 1, 0, 0);
    }
    else
    {
      // 如果密码错误,等待3秒后再次提示输入密码
      sub_4C188(3);
      // 记录密码错误信息
      sub_4E414("Login incorrect");
    }
  }
  // 正常启动系统
  sub_4E414("Normal startup");
 
  // 返回0表示函数执行完毕
  return 0;
}
int __fastcall sub_F804(int a1, int a2, int a3)
{
  // 声明一些局部变量
  int v4; // r6
  int v5; // r0
  struct passwd *v6; // r4
  char *v7; // r0
  char *v8; // r5
  char *v10; // r6
  int v11; // r7
  size_t v12; // r0
  char *pw_shell; // r0
  int v14[7]; // [sp+4h] [bp-1Ch] BYREF
 
  // 一系列初始化操作
  v14[1] = a3;
  dword_6CCF8 = 3;
  v14[0] = 0;
   
  // 获取系统信息
  openlog((const char *)dword_6DECC, 0, 32);
  
  // 解析环境变量
  dword_6DEF4 = (int)"t+";
  sub_4D454(a2, "t:", v14);
   
  v4 = optind;
  if ( *(_DWORD *)(a2 + 4 * optind) )
  {
    // 关闭文件描述符 0,1
    close(0);
    close(1);
     
    // 从文件中读取数据
    v5 = sub_D728(*(_DWORD *)(a2 + 4 * v4), 2);
     
    // 创建复制文件描述符
    dup(v5);
    // 关闭文件描述符2
    close(2);
    // 创建一个新的文件描述符,复制已存在的文件描述符
    dup(0);
  }
   
  if ( !isatty(0) || !isatty(1) || !isatty(2) )
  {
    // 如果任一文件描述符(012)不是终端设备,则显示错误信息
    dword_6CCF8 = 2;
    sub_D03C((int)"not a tty");
  }
  
  sub_50BA4();
  // 获取root用户的信息
  v6 = getpwuid(0);
   
  if ( !v6 )
    // 如果没有找到root用户,显示错误信息
    sub_D03C((int)"no password entry for root");
     
  while ( 1 )
  {
    // 询问输入root用户的密码,接收输入的密码
    v7 = (char *)sub_4BFDC(
                   0,
                   v14[0],
                   "Give root password for system maintenance\n(or type Control-D for normal startup):");
    v8 = v7;
    
    if ( !v7 || !*v7 )
      break;
       
    // 验证输入的密码是否正确
    v10 = (char *)sub_52C4C(v7, v6->pw_passwd);
    v11 = strcmp(v10, v6->pw_passwd);
    // 释放动态分配的内存
    free(v10);
     
    if ( !v11 )
    {

[招生]科锐逆向工程师培训(2026年7月3日实地,远程教学同时开班, 第56期)!

收藏
免费 0
打赏
分享
最新回复 (5)
雪    币: 1
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
师傅这么有耐心的吗
2024-11-24 12:19
0
雪    币: 1419
活跃值: (1278)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
3
wx_eternity. 师傅这么有耐心的吗[em_055]
当时初学,比较耐心吧
2024-11-24 17:24
0
雪    币: 202
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
师傅,请问libc基地址如何计算的
2025-3-2 21:06
0
雪    币: 1419
活跃值: (1278)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
5
mb_vnvprfge 师傅,请问libc基地址如何计算的
没有ASLR,常规的puts泄露就可以了,可以看看其他详细一点的文章
2025-3-4 14:30
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
太详细了吧
3天前
0
游客
登录 | 注册 方可回帖
返回