作者 | 海康威视
一、前言
二、尝试Firmadyne自动模拟
2.1 提取文件系统
2.2 识别固件架构
2.3 修复文件系统&镜像打包
2.4 获取网络配置
三、修复启动过程
3.1 错误的init进程
3.2 缺失的nvram键值
3.3 绕过系统reset逻辑
3.4 顺利进入系统
四、完成仿真
4.1 更新网络配置
4.2 修复web访问权限
4.3 仿真成功
五、总结
一、前言
二、尝试Firmadyne自动模拟
2.1 提取文件系统
# ./sources/extractor/extractor.py -b Cisco -np -nk "./Cisco_RV110W_FW_1.2.2.5.bin" images./Cisco_RV110W_FW_1.2.2.5.bin>> MD5: 10ca3292c5aeb5b4c77ddb98c0b6d663>> Tag: Cisco_RV110W_FW_1.2.2.5.bin_10ca3292c5aeb5b4c77ddb98c0b6d663>> Temp: /tmp/tmpe2qv2gq5>> Status: Kernel: True, Rootfs: False, Do_Kernel: False, Do_Rootfs: True>> Recursing into archive ...>>>> Squashfs filesystem, little endian, non-standard signature, version 3.0, size: 9188808 bytes, 1074 inodes, blocksize: 65536 bytes, created: 2019-07-24 08:12:22>>>> Found Linux filesystem in /tmp/tmpe2qv2gq5/_Cisco_RV110W_FW_1.2.2.5.bin.extracted/squashfs-root!>> Skipping: completed!>> Cleaning up /tmp/tmpe2qv2gq5...
2.2 识别固件架构
# ./scripts/getArch.sh images/99.tar.gz
./bin/busybox: mipsel
2.3 修复文件系统&镜像打包
# ./scripts/makeImage.sh 99 mipsel
----Running----
./scratch/99/
./scratch/99/image.raw
./scratch/99/image/
./binaries/console.mipsel
./binaries/libnvram.so.mipsel
----Copying Filesystem Tarball----
----Creating QEMU Image----
Formatting './scratch/99/image.raw', fmt=raw size=1073741824
----Creating Partition Table----
……
----Mounting QEMU Image---
-----Creating Filesystem----
mke2fs 1.45.6 (20-Mar-2020)
……
----Making QEMU Image Mountpoint---
-----Mounting QEMU Image Partition 1----
----Extracting Filesystem Tarball----
----Creating FIRMADYNE Directories----
----Patching Filesystem (chroot)----
Creating /etc/TZ!
Creating /etc/hosts!
Creating /etc/passwd!
Warning: Recreating device nodes!
Removing /etc/scripts/sys_resetbutton!-
---Setting up FIRMADYNE----
----Unmounting QEMU Image----
loop deleted : /dev/loop0
2.4 获取网络配置
# ./scripts/inferNetwork.sh 99 mipsel
Running firmware 99: terminating after 60 secs...
terminating on signal 2 from pid 1244313 (timeout) :
Inferring network...
Interfaces: []
tree scratch/99
scratch/99
image
image.raw
qemu.initial.serial.log
└── run.sh
1 directory, 3 files
三、修复启动过程
3.1 错误的init进程
# cat scratch/99/qemu.final.serial.log | tail
nvram_set_default_image: Copying overrides from defaults folder!
sem_get: Key: 410c001c
cp: cannot stat '/firmadyne/libnvram.override/*': No such file or directory
sem_get: Key: 410c001c
sem_get: Key: 410c001c
sem_get: Key: 410c001c
nvram_get_buf: = "-08 1 1"
[ 2.932000] Kernel panic - not syncing: Attempted to kill init!
QEMU: Terminated
# file /mnt/sbin/init
/mnt/sbin/init: symbolic link to rc
# file /mnt/sbin/* | grep 'to rc'
/mnt/sbin/blink_diag_led: symbolic link to rc
……
/mnt/sbin/client6: symbolic link to rc
……
/mnt/sbin/preinit: symbolic link to rc
……
/mnt/sbin/snmpdc: symbolic link to rc
……
/mnt/sbin/wl_nvram: symbolic link to rc
……
/mnt/sbin/yutest: symbolic link to rc

# binwalk -e Cisco_RV110W_FW_1.2.2.5.bin
……
# strings _Cisco_RV110W_FW_1.2.2.5.bin.extracted/3C | grep -P 'init='
……root=/dev/mtdblock2 console=ttyS0,115200 init=/sbin/preinit
……
# cat scratch/99/run.sh | grep 'init=/sbin/preinit'
-drive if=ide,format=raw,file=${IMAGE} -append "root=${QEMU_ROOTFS} console=ttyS
0 nandsim.parts=64,64,64,64,64,64,64,64,64,64 rdinit=/firmadyne/preInit.sh rw debug
ignore_loglevel print-fatal-signals=1 user_debug=31 firmadyne.syscall=0 init=/sbin/preinit"
3.2 缺失的nvram键值
[7fe0b074 7fe0afda 7fe0af88 7fe0af58 00000001 7fe0af68 004b0000 00471ce0 ]
[ ] ...
[ ] Call Trace:
[ ]
[ ]
[00c08821 00e0b021 00808021 ] Code:
[8d020000 00051840 00621821 94620000 30420020 ]
[1: potentially unexpected fatal signal 11. ] preinit/
[ ]
[0 ] Cpu
[0 : 00000000 1000a400 00000000 00000001 ] $
[4 : 00000000 00000000 0000000a 00000001 ] $
[8 : 2adaf004 00000003 00000001 8ffc5480 ] $
[12 : 8ffc5480 2adaf004 00010000 8ffc515c ] $
[16 : 00000000 0000000a 7fe0b074 7fe0afda ] $
[20 : 00000001 00000000 00000001 004b0000 ] $
[24 : 00000002 2ad64ac0 ] $
[28 : 2adb7530 7fe0ae98 00000001 0046f968 ] $
[00000001 ] Hi :
[00000001 ] Lo :
[2ad64b08 0x2ad64b08 ] epc :
[ ] Not tainted
[0046f968 0x46f968 ] ra :
[0000a413 USER EXL IE ] Status:
[10800008 ] Cause :
[00000000 ] BadVA :
[00019300 (MIPS 24Kc) ] PrId :
[ ] Kernel panic - not syncing: Attempted to kill init!
QEMU: Terminated
# cat scratch/99/qemu.final.serial.log | grep -i 'unable to open key' | tail
nvram_get_buf: Unable to open key: /firmadyne/libnvram/boot_hw_model!
nvram_get_buf: Unable to open key: /firmadyne/libnvram/wl0_radio!
nvram_get_buf: Unable to open key: /firmadyne/libnvram/wl0_vifs!
nvram_get_buf: Unable to open key: /firmadyne/libnvram/wl0_radio!
nvram_get_buf: Unable to open key: /firmadyne/libnvram/wl0_bss_enabled!
nvram_get_buf: Unable to open key: /firmadyne/libnvram/wl0_vlan_id!
# cat scratch/99/qemu.final.serial.log | grep -i 'unable to open key' | sort | uniq | wc -l
283
# kpartx -a -s -v scratch/99/image.raw
add map loop0p1 (253:0): 0 2095104 linear 7:0 2048
# mount /dev/mapper/loop0p1 /mnt
# ls /mnt
bin/ data/ dev/ etc/ firmadyne/ lib/ lost+found/ mnt/ proc/ sbin/ sys/ tmp/ usr/ var@ www/
# tree /mnt/firmadyne
/mnt/firmadyne
├── console
├── libnvram
├── libnvram.override
├── libnvram.so
├── preInit.sh
└── ttyS1
# cat scratch/99/qemu.final.serial.log | grep 'Unable to'| sort | uniq | awk 'BEGIN{F
S="!|/"}{print $4}' | xargs -I arg touch /mnt/firmadyne/libnvram.override/arg
3.3 绕过系统reset逻辑
at scratch/99/qemu.final.serial.log | tail
……
[1 (preinit)]: magic1:fee1dead, magic2:28121969, cmd:1234567 ] firmadyne: sys_reboot[PID:
[ ] firmadyne: sys_reboot: removed CAP_SYS_BOOT, starting init...
QEMU: Terminated
nvram_get_buf: resetbutton_disable
……
Reset Button pushed!
count[0] RESET_WAIT_COUNT[100]
Reset Button pushed!
count[1] RESET_WAIT_COUNT[100]
Reset Button pushed!
count[2] RESET_WAIT_COUNT[100]
……
Reset Button pushed!
count[98] RESET_WAIT_COUNT[100]
Reset Button pushed!
count[99] RESET_WAIT_COUNT[100]
Reset Button pushed!
count[100] RESET_WAIT_COUNT[100]
resetbutton: factory default.
Receiving restore commond from resetbutton ...
printf "1" | tee /mnt/firmadyne/libnvram.override/resetbutton_disable1
3.4 顺利进入系统
# cat config.h | grep DEBUG
#define DEBUG 0
# CC=/usr/bin/mipsel-linux-gnu-gcc make 2>/dev/null
/usr/bin/mipsel-linux-gnu-gcc -c -O2 -fPIC -Wall nvram.c -o nvram.o
/usr/bin/mipsel-linux-gnu-gcc -shared -nostdlib nvram.o -o libnvram.so
# cp libnvram.so /mnt/firmadyne/libnvram.so
# ./scratch/99/run.sh
……
Hit enter to continue...
BusyBox v1.7.2 (2019-04-22 16:08:01 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.
# netstat -ln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:81 0.0.0.0:* LISTEN
tcp 0 0 192.168.1.1:23 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:444 0.0.0.0:* LISTEN
tcp 0 0 :::80 :::* LISTEN
tcp 0 0 :::81 :::* LISTEN
tcp 0 0 :::443 :::* LISTEN
tcp 0 0 :::444 :::* LISTEN
udp 0 0 0.0.0.0:53518 0.0.0.0:*
udp 0 0 127.0.0.1:53 0.0.0.0:*
udp 0 0 192.168.1.1:53 0.0.0.0:*
udp 0 0 0.0.0.0:67 0.0.0.0:*
udp 0 0 0.0.0.0:69 0.0.0.0:*
udp 0 0 127.0.0.1:38032 0.0.0.0:*
udp 0 0 0.0.0.0:5353 0.0.0.0:*
udp 0 0 :::42626 :::*
raw 0 0 0.0.0.0:255 0.0.0.0:* 7
Active UNIX domain sockets (only servers)
Proto RefCnt Flags Type State I-Node Path
四、完成仿真
4.1. 更新网络配置
# cat scripts/run.mipsel.sh | grep init=/sbin/preinit
qemu-system-mipsel -m 256 -M malta -kernel ${KERNEL} -drive if=ide,format=raw,file=
64,64,64,64,64,64,64,64,64 rdinit=/firmadyne/preInit.sh rw debug ignore_loglevel pri
nt-fatal-signals=1 init=/sbin/preinit" -serial file:${WORK_DIR}/qemu.initial.serial.
log -serial unix:/tmp/qemu.${IID}.S1,server,nowait -monitor unix:/tmp/qemu.${IID},se
rver,nowait -display none -netdev socket,id=s0,listen=:2000 -device e1000,netdev=s0
-netdev socket,id=s1,listen=:2001 -device e1000,netdev=s1 -netdev socket,id=s2,liste
n=:2002 -device e1000,netdev=s2 -netdev socket,id=s3,listen=:2003 -device e1000,netd
ev=s3
# ./scripts/inferNetwork.sh 99 mipsel
Running firmware 99: terminating after 60 secs...
qemu-system-mipsel: terminating on signal 2 from pid 1271009 (timeout)
Inferring network...
Interfaces: [('br0', '192.168.1.1')]
Done!
# cat scratch/99/run.sh | grep 'init=/sbin/preinit'
-drive if=ide,format=raw,file=${IMAGE} -append "root=${QEMU_ROOTFS} console=ttyS
0 nandsim.parts=64,64,64,64,64,64,64,64,64,64 rdinit=/firmadyne/preInit.sh rw debug ignore_loglevel print-fatal-signals=1 user_debug
=31 firmadyne.syscall=0 init=/sbin/p
reinit"
4.2 修复web访问权限
# telnet 192.168.1.1
Trying 192.168.1.1...
Connected to 192.168.1.1.Escape character is '^]'.
RV110W login: admin
Password:
BusyBox v1.7.2 (2019-04-22 16:08:01 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.
#
printf "10" | tee /mnt/firmadyne/libnvram.override/wl_ap_mgmt_vlan_id
printf "10" | tee /mnt/firmadyne/libnvram.override/wl_vlan_id
nvram.h:
36 char *wl_nvram_get(const char *key);
nvram.c
336 char *wl_nvram_get(const char *key) {
337 return nvram_get(key);
338 }
# cat config.h | grep DEBUG
#define DEBUG 0
# CC=/usr/bin/mipsel-linux-gnu-gcc make 2>/dev/null
/usr/bin/mipsel-linux-gnu-gcc -c -O2 -fPIC -Wall nvram.c -o nvram.o
/usr/bin/mipsel-linux-gnu-gcc -shared -nostdlib nvram.o -o libnvram.so
# cp libnvram.so /mnt/firmadyne/libnvram.so
4.3 仿真成功
五、总结
本文始发于微信公众号(关键基础设施安全应急响应中心):原创 | Firmadyne仿真修复案例
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论