本教程仅供信息参考和教育目的。我认为,任何使用数字信息和计算机的人都应该熟悉道德黑客、信息安全及网络安全的相关知识。我相信,不了解黑客的手法,就无法有效防御黑客攻击。
$ IDA Pro +7.0(由于IDA价格昂贵,也可以使用Radar2)
$ 运行Rompager 4.07的路由器:数量众多,例如TP-Link TD-W8951*、ZyXEL P-660R、Billion BiPAC 51**/52** 等
$ UART转USB
"不幸的饼干"漏洞已存在一段时间,但公开的技术细节分析仍然缺乏。根据Check Point的报告,受影响的软件是来自AllegroSoft的嵌入式Web服务器RomPager。互联网范围的扫描表明,就可用端点数量而言,RomPager很可能是全球最流行的Web服务器软件。RomPager通常嵌入在设备固件中。该特定漏洞于2002年引入代码库。此外,暴露的设备使用RomPager服务的版本低于4.34(特别是4.07版本)。
本教程将使用一台运行Rompager 4.07的ZXHN H108L路由器。
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="ZXHN H108L"
Content-Type: text/html
Transfer-Encoding: chunked
Server: RomPager/4.07 UPnP/1.0
EXT:
现在使用串口,首先必须确定波特率
$ sudo stty -F /dev/ttyUSB0
输出:
speed 115200 baud; line = 0;
-brkint -imaxbel
设备
我使用__minicom__,你也可以用__screen__或__HyperTerminal__,但我更青睐它的稳定性 ;)
$ sudo minicom -s
+-----------------------------------------------------------------------+
| A - 串行设备 : /dev/modem |
| B - 锁文件位置 : /var/lock |
| C - 呼叫程序 : |
| D - 呼出程序 : |
| E - 波特率/校验/数据位 : 115200 8N1 |
| F - 硬件流控制 : No |
| G - 软件流控制 : No |
| |
| 修改哪项设置? |
+-----------------------------------------------------------------------+
| 屏幕和键盘 |
| 保存设置为dfl |
| 保存设置为.. |
| 退出 |
| 退出Minicom |
+--------------------------+
现在可以开始了:
$ sudo minicom -D /dev/ttyUSB0 -b 115200
Bootbase Version: VTC_SPI1.26 | 2012/12/26 16:00:00
RAM: Size = 8192 Kbytes
Found SPI Flash 2MiB GD25Q16 at 0xbfc00000
SPI Flash Quad Enable
Turn off Quad Mode
RAS Version: ZXHN H108LV4.0.0a_ZRQ_MA
System ID: $2.12.162.0(G94.BY.2)3.20.19.0| 2014/03/21 20140321_v006 | 2014
Press any key to enter debug mode within 3 seconds.
..........
显示可用命令:
$ ATHE
======= 调试命令列表 =======
AT 仅回复OK
ATHE 打印帮助
ATBAx 更改波特率。1:38.4k, 2:19.2k, 3:9.6k 4:57.6k 5:115.2k
ATENx,(y) 设置BootExtension调试标志 (y=密码)
ATSE 显示密码生成器的种子
ATTI(h,m,s) 更改系统时间为时:分:秒或显示当前时间
ATDA(y,m,d) 更改系统日期为年/月/日或显示当前日期
ATDS 转储RAS堆栈
ATDT 转储启动模块公共区域
ATDUx,y 从地址x开始转储长度为y的内存内容
ATRBx 显示地址x的8位值
ATRWx 显示地址x的16位值
ATRLx 显示地址x的32位值
ATGO(x) 在地址x运行程序或启动路由器
ATGR 启动路由器
ATGT 运行硬件测试程序
ATRTw,x,y(,z) RAM测试级别w, 从地址x到y (z次迭代)
ATSH 转储ROM中制造商相关数据
ATDOx,y 通过XMODEM从地址x下载长度为y的数据到PC
ATTD 通过XMODEM将路由器配置下载到PC
ATUR 将路由器固件上传到闪存ROM
< 按任意键继续 >
该固件有一组隐藏命令,对我们的动态分析非常有用,问题是我们无法查看寄存器也不能设置断点,但幸运的是利用这些隐藏命令我们可以实现这些功能。为了看到并使用它们,我们需要使用__ATEN__。然而,这个命令需要密码,那么我们现在该怎么办?让我们逆向固件,找出密码生成算法。
(静态分析)先看看固件文件:
$ binwalk ras
binwalk1
$ dd if=ras of=rom1.lzma bs=1 skip=85043 count=66696
$ dd if=ras of=rom2.lzma bs=1 skip=350259 count=3350556
$ lzma -d <rom1.lzma> rom1
$ lzma -d <rom2.lzma> rom2
现在看看提取文件中的签名:
$ binwalk -Y rom1
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 MIPS可执行代码, 32/64位, 大端序, 至少1250条有效指令
strings 是一个很好的工具,可以了解二进制文件的内容。让我们看看能否找到密码算法:
$ strings rom1 | grep -i aten
$ strings rom2 | grep -i aten
显然看不到,因为固件仍然被压缩,这意味着我们必须使用调试模式从路由器中转储出来:
$ ATDO bfc00000, 1f0000
xmodem
想到的第一件事就是寻找调度表(dispatch table)。为什么?只是直觉,另一种可能性是使用if-elif链,但那样会非常长,用于检查用户提供的密码是否有效。下面是我找到的内容:
ROM:800158A0 CmdTable: .word unk_80015C20 # DATA XREF: sub_8000E3D8+4Co
ROM:800158A0 # sub_8000E3D8+90o ...
ROM:800158A4 .word sub_8000E6D0
ROM:800158A8 .byte 0
ROM:800158A9 .byte 0
ROM:800158AA .byte 0
ROM:800158AB .byte 0
ROM:800158AC .word aJustAnswerOk # " just answer OK"
ROM:800158B0 .word aAthe # "ATHE"
ROM:800158B4 .word CMD_SHOW_HELP_ATHE
ROM:800158B8 .byte 0
ROM:800158B9 .byte 0
ROM:800158BA .byte 0
ROM:800158BB .byte 0
ROM:800158BC .word aPrintHelp # " print help"
ROM:800158C0 .word aAtba # "ATBA"
ROM:800158C4 .word CMD_ATBA
...
ROM:800158D0 .word aAten # "ATEN"
ROM:800158D4 .word CMD_ATEN
请记住,MIPS中的字(word)是4字节。现在看看__CMD_ATEN__:
ROM:8000FF84 lw $a0, 8($s0)
ROM:8000FF88 jal sub_80013654
ROM:8000FF8C li $a1, 0
ROM:8000FF90 jal passwd_generator
然后是__passwd_generator__:
ROM:80010A2C passwd_generator: # CODE XREF: CMD_ATEN+50p
ROM:80010A2C lui $t9, 0xA11F
ROM:80010A30 lw $v1, ptrDO_GTABLE
ROM:80010A34 lw $t8, dword_80017050
ROM:80010A38 lw $v1, 0x18($v1) # 获取种子部分,由ATSE命令生成
ROM:80010A3C lbu $t8, 0x6D($t8) # 获取MAC地址最后一个字节
ROM:80010A40 sll $v1, 8 # $v1 = v1 << 8
ROM:80010A44 andi $t7, $t8, 7 # $t7 = t8 & 0x7
ROM:80010A48 li $t8, 0 # $t8 = 0
ROM:80010A4C srl $v1, 8 # $v1 = v1 >> 8
ROM:80010A50 li $t9, 0xA11F5AC6 # $t9 = MAGIC
ROM:80010A54 j loc_80010A68
ROM:80010A58 addu $t9, $v1, $t9 # $t9 = $v1 + $t9
ROM:80010A5C # ---------------------------------------------------------------------------
ROM:80010A5C
ROM:80010A5C loc_80010A5C: # CODE XREF: passwd_generator+40j
ROM:80010A5C sll $t9, 31 # $t9 = $t9 << 31
ROM:80010A60 or $t9, $t6, $t9 # $t9 = $t6 | $t9
ROM:80010A64 addiu $t8, 1 # $t8 = $t8 + 1
ROM:80010A68
ROM:80010A68 loc_80010A68: # CODE XREF: passwd_generator+28j
ROM:80010A68 sltu $t6, $t8, $t7 # $t6 = (t8 < t7? 1:0)
ROM:80010A6C bnez $t6, loc_80010A5C # 如果$t6 != 0则跳转
ROM:80010A70 srl $t6, $t9, 1 # $t6 = $t9 >> 1
ROM:80010A74 jr $ra # 全部完成,退出
ROM:80010A78 xor $v0, $t9, $v1 # $v0 = $t9 ^ $v1
结论如下:
现在编写一个C程序实现相同功能并编译:
U32 passwd_generator(U32 seed, U8 last_mac_octet)
{
U32 b = seed & 0x00FFFFFF;
U32 r = ROR(b + 0xA11F5AC6, last_mac_octet & 7);
return r^b;
}
现在运行 ATSE 获取种子并使用它(例如,我得到 088521A78838):
$ ./gen_pass 088521A78838
现在运行我们刚刚破解的神奇命令 :p
$ ATEN1, A12F5AC6
ATEN1, A12F5AC6
OK
ATHE
======= 调试命令列表 =======
AT 仅回复OK
ATHE 打印帮助
ATBAx 更改波特率。1:38.4k, 2:19.2k, 3:9.6k 4:57.6k 5:115.2k
ATENx,(y) 设置BootExtension调试标志 (y=密码)
ATSE 显示密码生成器的种子
ATTI(h,m,s) 更改系统时间为时:分:秒或显示当前时间
ATDA(y,m,d) 更改系统日期为年/月/日或显示当前日期
ATDS 转储RAS堆栈
ATDT 转储启动模块公共区域
ATDUx,y 从地址x开始转储长度为y的内存内容
ATWBx,y 将8位值y写入地址x
ATWWx,y 将16位值y写入地址x
ATWLx,y 将32位值y写入地址x
ATRBx 显示地址x的8位值
ATRWx 显示地址x的16位值
ATRLx 显示地址x的32位值
ATGO(x) 在地址x运行程序或启动路由器
ATGR 启动路由器
ATGT 运行硬件测试程序
AT%Tx 在启动时启用硬件测试程序
ATBTx 块0写使能 (1=启用, 其他=禁用)
< 按任意键继续 >
ATRTw,x,y(,z) RAM测试级别w, 从地址x到y (z次迭代)
ATWEa(,b,c,d) 将MAC地址、国家代码、EngDbgFlag、FeatureBit写入闪存ROM
ATCUx 将国家代码写入闪存ROM
ATCB 从闪存ROM复制到工作缓冲区
ATCL 清除工作缓冲区
ATSB 将工作缓冲区保存到闪存ROM
ATBU 转储工作缓冲区中的制造商相关数据
ATSH 转储ROM中的制造商相关数据
ATWMx 在工作缓冲区中设置MAC地址的低6位
ATMHx 在工作缓冲区中设置MAC地址的高6位
ATBS 显示BootBase密码生成器的种子
ATLBx 通过xmodem上传BootBase, x为密码
ATSMx 在工作缓冲区中设置MAC地址的6位
ATCOx 在工作缓冲区中设置国家代码
ATFLx 在工作缓冲区中设置EngDebugFlag
ATSTx 在工作缓冲区中设置ROMRAS地址
ATSYx 在工作缓冲区中设置系统类型
ATVDx 在工作缓冲区中设置供应商名称
ATPNx 在工作缓冲区中设置产品名称
ATFEx,y,... 在工作缓冲区中设置功能位
ATMP 检查并转储memMapTab
ATDOx,y 通过XMODEM从地址x下载长度为y的数据到PC
< 按任意键继续 >
ATTD 通过XMODEM将路由器配置下载到PC
ATUPx,y 通过XMODEM从PC上传数据到RAM地址x, 长度y
ATUR 将路由器固件上传到闪存ROM
ATDC 在上传固件期间禁用硬件版本检查
ATLC 将路由器配置文件上传到闪存ROM
ATUXx(,y) 从闪存块x到y通过xmodem上传
ATERx,y 擦除闪存ROM从块x到y
ATWFx,y,z 将数据从地址x复制到闪存地址y, 长度z
ATXSx xmodem选择: x=0: CRC模式(默认); x=1: 校验和模式
ATLD 将配置文件和默认ROM文件上传到闪存
ATBR 重置为默认Romfile
ATCD 将当前ROM文件转换为默认ROM文件并写入闪存
OK
最后,我们获得了全新的命令集,可以让我们转储内存
$ ATMP
ROMIO image start at bfc30000
code version:
code start: 80008000
code length: 1A5D4E
memMapTab: 18 entries, start = bfc44000, checksum = 9770
$RAM Section:
0: BootExt(RAMBOOT), start=80030000, len=18000
1: HTPCode(RAMCODE), start=80048000, len=E0000
2: RasCode(RAMCODE), start=80048000, len=420000
$ROM Section:
3: BootBas(ROMIMG), start=bfc28000, len=4000
4: DbgArea(ROMIMG), start=bfc2c000, len=2000
5: RomDir2(ROMDIR), start=bfc2e000, len=2000
6: BootExt(ROMIMG), start=bfc30030, len=13FD0
7: MemMapT(ROMMAP), start=bfc44000, len=C00
8: HTPCode(ROMBIN), start=bfc44c00, len=8000
(压缩)
Version: HTP_TC V 0.05, start: bfc44c30
Length: 10488, Checksum: CB32
Compressed Length: 41CF, Checksum: D5A5
9: termcap(ROMIMG), start=bfc4cc00, len=400
10: RomDefa(ROMIMG), start=bfc4d000, len=2000
11: LedDefi(ROMIMG), start=bfc4f000, len=400
12: LogoImg(ROMIMG), start=bfc4f400, len=2000
13: LogoImg2(ROMIMG), start=bfc51400, len=2000
14: StrImag(ROMIMG), start=bfc53400, len=32000
15: StrImag2(ROMIMG), start=bfc85400, len=32000
16: Rt11nE2p(ROMIMG), start=bfcb7400, len=400
17: RasCode(ROMBIN), start=bfcb7800, len=246C00
(压缩)
Version: ADSL ATU-R, start: bfcb7830
Length: 321D2C, Checksum: 582D
Compressed Length: 11E54B, Checksum: B30B
$USER Section:
Msecs 128
Heap0 16 300 16
Heap1 32 64 4
Heap2 64 64 4
Heap3 128 160 4
Heap4 192 256 4
Heap5 256 80 4
Heap6 320 20 4
Heap7 384 4 2
Heap8 448 20 2
Heap9 512 34 2
Heap10 1024 52 4
Heap11 2048 20 2
Heap12 3172 4 2
Heap13 4096 2 2
Heap14 0 0
...
我们刚才学到的知识将帮助我们分析之前提取的ROM文件
code start: 80008000
code length: 1A5D4E
现在,在转储一个新ROM文件后,我们可以将__ROM起始地址__和__加载地址__设置为__0x80008000__,使用以下命令:
$ ATDO 80008000, 1A5D4E
我们知道__ATGR__会启动路由器,所以我们可以跟踪它,找到将执行权传递给固件第二部分的指令。
atgr1
ROM:80011E8C loc_80011E8C: # CODE XREF: LoadAndRunImage+D8j
ROM:80011E8C jal sub_80009B24
ROM:80011E90 li $a0, 0x1F4
ROM:80011E94
ROM:80011E94 execute_image: # 执行镜像
ROM:80011E94 jalr $s0 # 跳转并链接到$s0中的地址
ROM:80011E98 nop
现在我们需要问:$s0中存储的是什么地址?显然我们仍然无法设置断点,也无法查看寄存器,所以我们需要通过一些小技巧来泄露它。幸运的是,我们可以读写内存,看看下面的命令:
ATWL 80011E94, ae30001c
ATGR
看起来,这条命令将__0x80011E94__处的指令补丁为__ae30001c__(MIPS指令__sw $s0, 0x1C($s1)的字节表示)。通过覆盖原始的__jalr $s0__为__sw $s0, 0x1C($s1),我们强制程序将__$s0__寄存器的内容存储到内存地址__0x8001FF1C__。这样,第二阶段的固件将不会被执行,而是显示"ERROR"消息,我们仍然可以访问第一阶段的控制台,太棒了!那么让我们看看__0x8001FF1C__中存储了什么内容。
ATRL 8001FF1C
8001FF1C: 80020000
抓住它了!... 使用__0x80020000__,我们可以转储并正确分析固件的第二部分
未完待续 ...