4

一次服务器BMC固件逆向经历

 3 years ago
source link: http://maskray.me/blog/2015-06-30-bmc-firmware-reverse-enginnering
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一次服务器BMC固件逆向经历

访问BMC默认开启的Web管理界面http://$ip,开启ssh服务,之后即可以执行ssh root@$ip,得到一个受限的管理界面,功能很少,没有系统shell。

在服务器上执行ipmitool fru可以查找到设备型号,使用厂商提供的flash备份工具导出flash,比较慢,每秒200多KB。在BMC网页界面的BMC System Audit Log中看到BMC的Linux系统日志:

kernel: Kernel command line: root=/dev/ramdisk ro ip=none ramdisk_blocksize=4096 console=ttyS4,38400 rootfstype=cramfs bigphysarea=6144 imagebooted=1
kernel: Memory: 201216KB available (3080K code, 261K data, 128K init)
kernel: Ractrends Flash mapping: 0x2000000 at 0x20000000
kernel: Creating 6 MTD partitions on "Ractrends":
kernel: 0x00000000-0x02000000 : "fullpart"
kernel: 0x00050000-0x000c0000 : "conf"
kernel: 0x000d0000-0x00140000 : "bkupconf"
kernel: 0x00150000-0x00f10000 : "root"
kernel: 0x010c0000-0x01410000 : "www"
kernel: 0x01600000-0x01ff0000 : "lmedia"

从中得到flash的分区信息,包含conf bkupconf root www lmedia等分区。用binwalk查看导出的flash可以看到u-boot和一个存储内核的u-boot legacy image。

根据Linux系统日志中描述的conf偏移提取conf,binwalk可以确定文件系统为[JFFS2],而报告的很多zlib压缩块则是JFFS2的数据。本机模拟一个mtdblock设备并挂载:

modprobe mtdram total_size=32768 erase_size=64
modprobe mtdblock
dd if=conf of=/dev/mtdblock0
mount -t jffs2 /dev/mtdblock0 /mnt

发现很多文件是通常Linux系统/etc下的配置文件。如果调大conf结束偏移量,还能看到更多文件,不清楚模拟出来的mtdblock有哪些差异。

修改登录shell

猜测conf分区会被作为/etc使用,因此修改flash中conf分区处的passwd,把root的shell改成/bin/shumount /mnt后再次执行上述挂载操作,在dmesg中能看到内核输出的JFFS2 checksum错误,其中包含正确的校验和。在flash中搜索错误的校验和,修改成正确的。用flash备份工具写入修改后的flash,稍等片刻等待BMC自动重启,再次执行ssh即看到shell换成了busybox!实际测试没有这么顺利,修改了几次都发现没效果,原因是系统检测到JFFS2文件系统checksum错误后自动还原分区。

查看挂载点:

BusyBox v1.13.2 (2015-01-08 17:43:56 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.
# mount
rootfs on / type rootfs (rw)
/dev/root on / type cramfs (ro)
/dev/proc on /proc type proc (rw)
sys on /sys type sysfs (rw)
/dev/shm on /var type tmpfs (rw)
/dev/ram2 on /usr/local/www type cramfs (ro)
/dev/mtdblock1 on /conf type jffs2 (rw)
/dev/mtdblock2 on /bkupconf type jffs2 (rw)
/dev/mtdblock5 on /usr/local/lmedia type jffs2 (rw)
devpts on /dev/pts type devpts (rw,gid=5,mode=620)
tmpfs on /usr/local/www/tmp type tmpfs (rw)
/dev/mtdblock5 on /usr/local/www/blackbox type jffs2 (rw)
/dev/shm on /usr/local/tmplmedia type tmpfs (rw)
# cat /etc/fstab
/dev/mtdblock1 /conf jffs2 defaults 0 0
/dev/mtdblock2 /bkupconf jffs2 defaults 0 0
/dev/root / auto defaults,rw 0 0
/dev/mtdblock5 /usr/local/lmedia jffs2 defaults 0 0
proc /proc proc defaults 0 0
sys /sys sysfs defaults
devpts /dev/pts devpts gid=5,mode=620

注意/dev/mtdblock{0..5},它们是同一块SPI flash,kernel中应该编码了划分方案,但尚不清楚这个信息是怎么存储的。

导出initrd

内核启动选项为root=/dev/ramdisk ro ip=none ramdisk_blocksize=4096 console=ttyS4,38400 rootfstype=cramfs bigphysarea=6144 imagebooted=1/dev/ramdisk即为u-boot提供的initrd,使用只读文件系统[cramfs],启动后不会通过switch_root等方式更换根文件系统。可以用ssh root@$ip cat /dev/ramdisk把initrd复制到本地,挂载后发现/etc下有很多指向/conf的软链接,比如/etc/passwd -> /conf/passwd。这就解释了为什么之前修改/conf/passwd可以改变系统的登录shell。

但是以initrd的数据在flash中按图索骥却查不到。暂时搁置。

值得注意的是负责挂载网页管理界面程序所用分区的启动脚本/etc/init.d/init-sp.sh,它把flash的分区/dev/mtdblock4(www)挂载到/usr/local/www

dd if=/dev/$wwwmtdblock of=/dev/ram2
/usr/local/bin/dencryption /dev/ram2 /dev/$wwwmtd
mount -t cramfs /dev/ram2 /usr/local/www

这里用到了程序/usr/local/bin/dencryption,它从第二个参数中获取长度,使用ECB模式的Tiny Encryption Algorithm原地解密第一个参数(此处为/dev/ram2)。程序中编码了密钥,而在flash的u-boot bootloader区域也能找到该密钥,发现initrd也是被同样方式解密后挂载的,而执行这一解密过程的只能是u-boot。猜测开发人员修改u-boot,使用了Tiny Encryption Algorithm解密initrd传递给kernel。

提取flash中0x150000到0xf10000处内容,用dencryption解密算法得到u-boot legacy image,tail -c+65即可得到initrd,和/dev/ramdisk一致。

交叉编译工具链

# uname -a
Linux 6C92BF0BA1B6 2.6.28.10-ami #1 Thu Jan 8 17:50:26 CST 2015 armv5tejl unknown
# head -n1 /proc/meminfo
MemTotal: 215668 kB

安装crosstool-ng,执行ct-ng menuconfig配置交叉编译工具链:

Paths and misc options --->
[*] Debug crosstool-NG
[ ] Pause between every steps
[*] Save intermediate steps
[*] gzip saved states
[*] Interactive shell on failed commands
Target options --->
Target Architecture (arm) --->
() Suffix to the arch-part
*** Generic target options ***
[ ] Build a multilib toolchain (READ HELP!!!)
[*] Use the MMU
Endianness: (Little endian) --->
Bitness: (32-bit) --->
*** Target optimisations ***
(armv5te) Architecture level
(arm926ej-s) Emit assembly for CPU
() Tune for CPU
() Use specific FPU
Floating point: (software (no FPU)) --->
() Target CFLAGS
() Target LDFLAGS
*** arm other options ***
Default instruction set mode (arm) --->
[ ] Use Thumb-interworking (READ HELP)
-*- Use EABI

gcc、glibc、binutils等有复杂的版本依赖关系,近年gcc make sed又都升级了主版本号,如果选择与BMC系统相近的glibc-2.11,./configure时会报错。方便起见glibc选择新版本,这样某些程序会因为依赖高版本符号而无法在旧glibc系统上执行,暂且不管。为了调试方便把strace和ltrace也选上。

ct-ng build。失败后可以用ct-ng list-stepsct-ng libc+等,避免前功尽弃。我在tmpfs下编译,完成后占用了近7GiB。

内核启动参数bigphysarea=6144用于分配设置连续的物理内存,/proc/bigphysarea/proc/iomem可以看到地址的使用情况。

很多服务如kvm、虚拟磁盘等都用到了service location protocol,读取SLP配置文件,有待了解。

/usr/local/bin/IPMIMain负责处理system/LAN等interface的IPMI请求。Web管理界面和原来的shell都经过自行实现的libipmi.so,请求最终发往IPMIMain监听的unix socket。很多符号名和https://www.virustotal.com/en/file/e72785167675ab46c8abd7bc29e66488d78bc5c9ac494c8ca4abcfd5ae94e0e0/analysis/相同。

默认开启snmpd,但Web管理界面不显示。可以用snmpwalk -u $username -l authPriv -a MD5 -x DES -A $password -X $passsword $ip获取MIB树信息。

IPMI SDR数据取自/dev/{adc0,gpio0,i2c2,netmon,pwmtach0}等。

调试IPMIMain时需要关闭/etc/init.d中的watchdogapp以避免服务不可用导致系统自动重启。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK