小米

小米路由4A分析

• 0x01:Get Shell
图片 路由器暴露串口,焊接之后上电,查看启动信息。
Erasing SPI Flash...
raspi_erase: offs:20000 len:10000
.
Writing to SPI Flash...
.
done
## Booting image at bc160000 ...
Image Name: MIPS OpenWrt Linux-3.10.14
Image Type: MIPS Linux Kernel Image (lzma compressed)
Data Size: 1425061 Bytes = 1.4 MB
Load Address: 80000000
Entry Point: 80000000
Verifying Checksum ... OK
Uncompressing Kernel Image ... - init -
[ 5.750000] ra2880stop()...Done
[ 5.760000] Free TX/RX Ring Memory!
init started: BusyBox v1.19.4 (2019-04-01 03:43:26 UTC)
Please press Enter to activate this console. rcS S boot: INFO: rc script run time limit to 65 seconds.
[ 6.120000] MIWIFI crash syslog initialize skiped! Code=1
[ 16.250000] ipaccount: ifname [eth0.1] event[4]
[ 16.250000] ipaccount: ifname [br-lan] event[4]
[ 16.250000] ipaccount: ifname [eth0.2] event[4]
看到Booting image是0xBC160000,基本可以判断是联发科mt762x的soc。 内核启动后不能输入,应该是没有设相应内核参数。
为了get shell,我们来修改一下启动脚本: 把spi flash焊下来,读取内容(这里用的RT809H编程器,他的对应软件选择20170928版本,最新的2019版本不知道为什么有问题)。
binwalk一下。 å›¾ç‰‡
uImage header长度为64字节,应该没有修改过uboot。正常header的格式如下: 
图片 
主要关注点就是ih_hcrc与 ih_dcrc两处crc校验。
ih_hcrc位header的crc,校验前会把ih_hcrc填充位0再计算crc32。
图片 

ih_dcrc是从zimage起长度为ih_size数据的crc32 å›¾ç‰‡ 理论上这个ih_size只是检查kernel,不会到后面的文件系统部分,验证也确实如此。 
图片 
图片 但我印象里好像360的路由也校验到了文件系统部分。
这里就不需要管uboot校验的问题了,直接修改文件系统。 binwalk -Me解压flash镜像,进入squashfs目录,openwrt通常会去找rc,d中 S开头的文件,执行他的start函数,在/etc/rc.d中发现了S40telnet脚本:
#!/bin/sh /etc/rc.common# Copyright (C) 2006-2011 OpenWrt.org
START=40
start() {    service_start /usr/sbin/telnetd -l /bin/login.sh    return 0}
stop() {    service_stop /usr/sbin/telnetd}login.sh:#!/bin/sh# Copyright (C) 2006-2011 OpenWrt.org
if ( ! grep -qs '^root:[!x]\?:' /etc/shadow || \     ! grep -qs '^root:[!x]\?:' /etc/passwd ) && \   [ -z "$FAILSAFE" ]then    ft_mode=`cat /proc/xiaoqiang/ft_mode`    if [ "$ft_mode" = "1" ]; then        exec /bin/ash --login    else        busybox login    fifi
这个脚本貌似首先会正则判断一下root密码,然后检查/proc/xiaoqiang/ft_mode的值,来确定是否要密码来登陆,比较有趣,等拿shell了详细分析一下。
为了理解路由器的启动流程,在rcS中加一些打印日志。 
图片

并尝试在这里直接开telnet: 
图片
制作文件系统,命令如下:
mksquashfs ./squashfs-root root.squashfs44 -nopad -noappend -root-owned -comp xz -b 256k -p '/dev d 755 0 0' -p '/dev/console c 600 0 0 5 1' -processors 1
这里要注意-comp xz,表示以xz格式压缩文件系统,这里要与之前Binwalk看到的squashfs的压缩方式一致。 
图片
然后将新生成的文件系统放在0x2C0000处覆盖原有文件系统,并且保持0xDC0000 开始处是Jffs2文件系统。(也就是说把新生成的覆盖到0x2C0000-0xDC0000处,其余都保持不变)。
然后通过编程器刷入flash,再焊到路由器上。
这里顺便总结一下编程器与flash读取方法: 最稳定的方法还是吹下来直接拿编程器读,用过CH341A和RT809H编程器,RT809H不知道什么原因总是识别不了,CH341A每次校验会有问题。 还有一些不用吹flash直接读的方法(夹子,脚夹),有时也不太稳定: 
图片 å›¾ç‰‡
Arduino或者树莓派也可以读写spi flash,而且比较稳定,按照下面的几张图把树莓派和flash连起来。 
图片
图片  å›¾ç‰‡
然后通过flashrom读写flash(https://github.com/flashrom/flashrom)
sudo raspi-config ------>  Interfacing options ----------> spi enable    sudo apt install git libpci-dev libusb-1.0 libusb-dev    git clone https://github.com/flashrom/flashrom.gitcd flashrommake && sudo make install
//, spispeed可以设置大一些flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=512 -r flash.dat -c GD25B128B/GD25Q128B -V
//写,-c是指定设备flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=512 -w flash.dat -c GD25B128B/GD25Q128B -V
刷写后,启动路由器,诡异的是启动脚本的打印日志并没有任何变化。 想一想这里是dump了整个flash的内容,之前binwalk的结果,在Uboot与内核之间有很多的zlib或者jffs2格式的东西。
//不确定 我估计有可能即使修改了flash里的根文件系统,但是flash里其他的地方没有修改,有可能内核认为已经挂在了文件系统,而不从初始的squashfs重新挂在。
这里我按reset键5秒,发现成功修改文件系统。 按reset的串口打印:
[   78.020000] [sched_delayed] sched: RT throttling activated[   78.460000] press reset button over 5s[   78.460000] : sending a SIGUSR2 to process 4347pid 4347 recv sig SIGUSR2!gpio btn reset![   78.960000] press reset button over 5s[   78.960000] : sending a SIGUSR2 to process 4347[   79.470000] led=44, on=1, off=4000, blinks,=1, reset=1, time=4000[   79.480000] led=11, on=1, off=4000, blinks,=1, reset=1, time=4000[   79.490000] led=11, on=1, off=4000, blinks,=1, reset=1, time=4000[   79.500000] led=44, on=4000, off=1, blinks,=1, reset=1, time=4000pid 4347 recv sig SIGUSR2!gpio btn reset![   80.060000] led=44, on=1, off=4000, blinks,=1, reset=1, time=4000[   80.080000] led=11, on=1, off=4000, blinks,=1, reset=1, time=4000[   80.090000] led=11, on=1, off=4000, blinks,=1, reset=1, time=4000[   80.100000] led=44, on=4000, off=1, blinks,=1, reset=1, time=4000router monitor is not running, exit./sbin/wifi: CALLER: /bin/sh/etc/rc.common/etc/rc.d/K01shutdownshutdown[   84.220000] ipaccount: ifname [eth0.2] event[9][   84.220000] ipaccount: ifname [eth0.2] event[2][   84.240000] ipaccount: ifname [eth0.2] event[6][   84.250000] ipaccount: ifname [eth0.2] event[17]/sbin/wifi: ...mt7612.type=mt7612.../sbin/wifi: run1 eval type disable_mt7612/sbin/wifi: run2 eval disable_mt7612 'mt7612'[   84.820000] ipaccount: ifname [wl0] event[9][   84.860000] br-lan: port 2(wl0) entered disabled state[   84.860000] ipaccount: ifname [wl0] event[2]/sbin/wifi: ...mt7628.type=mt7628.../sbin/wifi: run1 eval type disable_mt7628/sbin/wifi: run2 eval disable_mt7628 'mt7628'[   85.020000] ipaccount: ifname [wl1] event[9][   85.020000] wifi_disassoc2 MAC 30-b4-9e-af-0d-5a,  ReasonCode 4[   85.030000] wifi_log: [Class 2 - SEND DISASSOC] - if="wl1", sta_mac="30:b4:9e:af:0d:5a", bssid="ec:41:18:da:a9:fd", action="send-disassoc", info="mlme kick out sta", reason="4", Aid="1"[   85.050000] br-lan: port 3(wl1) entered disabled state[   85.050000] ipaccount: ifname [wl1] event[2][   85.160000] ipaccount: ifname [wl2] event[9][   85.170000] MtAsicSetPreTbtt(): bss_idx=0, PreTBTT timeout = 0x0[   85.170000] MtAsicSetPiggyBack(783): Not support for HIF_MT yet![   86.010000] tx_kickout_fail_count = 0[   86.010000] tx_timeout_fail_count = 0[   86.010000] rx_receive_fail_count = 0[   86.020000] alloc_cmd_msg = 250[   86.020000] free_cmd_msg = 250[   86.030000] ipaccount: ifname [wl2] event[2]program name :'/usr/bin/longloopd'longloopd: is not runninglongloopd: is not running!/sbin/wifi: CALLER: /bin/sh/etc/rc.common/etc/rc.d/K90networkshutdown/sbin/wifi: ...mt7612.type=mt7612.../sbin/wifi: run1 eval type disable_mt7612/sbin/wifi: run2 eval disable_mt7612 'mt7612'/sbin/wifi: ...mt7628.type=mt7628.../sbin/wifi: run1 eval type disable_mt7628/sbin/wifi: run2 eval disable_mt7628 'mt7628'[   88.840000] ipaccount: ifname [ifb0] event[9][   88.840000] ipaccount: ifname [ifb0] event[2][   88.880000] ipaccount: ifname [br-lan] event[9][   88.880000] br-lan: port 1(eth0.1) entered disabled state[   88.890000] ipaccount: ifname [br-lan] event[2][   88.910000] ipaccount: ifname [br-lan] event[8][   88.910000] device eth0.1 left promiscuous mode[   88.910000] device eth0 left promiscuous mode[   88.920000] br-lan: port 1(eth0.1) entered disabled state[   89.020000] ipaccount: ifname [eth0.1] event[9][   89.030000] ipaccount: ifname [eth0.1] event[2][   89.030000] IPv6: ADDRCONF(NETDEV_UP): eth0.1: link is not ready[   89.040000] ipaccount: ifname [eth0.1] event[6][   89.060000] ipaccount: ifname [eth0.1] event[17][   89.060000] ipaccount: ifname [eth0] event[9][   89.060000] ra2880stop()...Done[   89.070000] Free TX/RX Ring Memory![   89.070000] ipaccount: ifname [eth0] event[2][   89.090000] device wl0 left promiscuous mode[   89.100000] br-lan: port 2(wl0) entered disabled state[   89.110000] device wl1 left promiscuous mode[   89.110000] br-lan: port 3(wl1) entered disabled state[   89.120000] ipaccount: ifname [br-lan] event[11][   89.120000] ipaccount: ifname [br-lan] event[6][   89.140000] ipaccount: ifname [br-lan] event[17][   89.150000] ipaccount: ifname [lo] event[9][   89.180000] ipaccount: ifname [lo] event[2]program name :'/usr/bin/longloopd'longloopd: is not runninglongloopd: is not running!/etc/rc.common: line 92: swapoff: not foundThe system is going down NOW!Sent SIGTERM to all processesApr  1 11:59:41 wrsst[4265]: wrsst got signal(15), exit.
Sent SIGKILL to all processesRequesting system reboot[   91.840000] Stopped WatchDog Timer.[   91.840000] MT7612E cleanup[   91.840000] ipaccount: ifname [apclii0] event[6][   91.850000] ipaccount: ifname [apclii0] event[17][   91.850000] RtmpOSNetDevDetach(): RtmpOSNetDeviceDetach(), dev->name=wl0![   91.860000] ipaccount: ifname [wl0] event[6][   91.860000] ipaccount: landev_uninit clear ifname [wl0][   91.870000] ipaccount: ifname [wl0] event[17][   91.880000] Restarting system.[   91.880000]
[   91.880000] before restart close watchdog[   91.880000] Stopped WatchDog Timer.
[03080D08][03080D08][88880000][24244848][00242448]DU Setting Cal Done
重启后的启动脚本日志: 刷新overlayfs和nvram 
图片
进入rcS: 
图片
开始遍历调用rc.d中S开头的启动脚本 
图片

启动后发现没有telnet依然没有开启,qemu跑一下路由里的busybox telnetd 
图片

看来busybox是小米修改过了的,大概分析一下:  
图片
图片
函数表起始:
图片 å›¾ç‰‡

Let’s Look inside:) 整体看下来没看出啥异常,上调试器看看 
图片 
这里理论上是会走到telnetd处理函数: 
图片

F7进去发现到了函数0x41739C,怀疑小米做了类似hook的操作来修改busybox 
图片

静态看了一下基本就是对/proc/xiaoqiang这个目录的文件进行操作,这里ft_mode 我怀疑是 factory_mode,如果值为0就直接退出了。等拿了shell再看下。 
图片

为了getshell,先上传个mipel版本的busybox,修改S40telnet脚本: 
图片
重新刷写,上电,reset,发现成功开启telnet。 但是需要密码,试了下貌似不是弱口令,尝试修改脚本如下: 
图片
图片

重新刷,成功拿shell: 
图片
netstat -anp和nmap结果  
图片
图片

评论