导出文件
访问BMC默认开启的Web管理界面http://$ip
,开启ssh
服务,之后即可以执行ssh root@$ip
,得到一个受限的管理界面,功能很少,没有系统shell。
在服务器上执行ipmitool fru
可以查找到设备型号,使用厂商提供的flash备份工具导出flash,比较慢,每秒200多KB。在BMC网页界面的BMC System Audit Log
中看到BMC的Linux系统日志:
1 | kernel: Kernel command line: root=/dev/ramdisk ro ip=none ramdisk_blocksize=4096 console=ttyS4,38400 rootfstype=cramfs bigphysarea=6144 imagebooted=1 |
从中得到flash的分区信息,包含conf
bkupconf
root
www
lmedia
等分区。用binwalk查看导出的flash可以看到u-boot和一个存储内核的u-boot
legacy image。
根据Linux系统日志中描述的conf
偏移提取conf
,binwalk可以确定文件系统为[JFFS2],而报告的很多zlib压缩块则是JFFS2的数据。本机模拟一个mtdblock设备并挂载:
1 | modprobe mtdram total_size=32768 erase_size=64 |
发现很多文件是通常Linux系统/etc
下的配置文件。如果调大conf
结束偏移量,还能看到更多文件,不清楚模拟出来的mtdblock有哪些差异。
修改登录shell
猜测conf
分区会被作为/etc
使用,因此修改flash中conf
分区处的passwd
,把root
的shell改成/bin/sh
,umount /mnt
后再次执行上述挂载操作,在dmesg
中能看到内核输出的JFFS2
checksum错误,其中包含正确的校验和。在flash中搜索错误的校验和,修改成正确的。用flash备份工具写入修改后的flash,稍等片刻等待BMC自动重启,再次执行ssh即看到shell换成了busybox!实际测试没有这么顺利,修改了几次都发现没效果,原因是系统检测到JFFS2文件系统checksum错误后自动还原分区。
查看挂载点:
1 | BusyBox v1.13.2 (2015-01-08 17:43:56 CST) built-in shell (ash) |
注意/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
:
1 | dd if=/dev/$wwwmtdblock of=/dev/ram2 |
这里用到了程序/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
一致。
交叉编译工具链
1 | # uname -a |
安装crosstool-ng,执行ct-ng menuconfig
配置交叉编译工具链:
1 | Paths and misc options ---> |
gcc、glibc、binutils等有复杂的版本依赖关系,近年gcc make
sed又都升级了主版本号,如果选择与BMC系统相近的glibc-2.11,./configure
时会报错。方便起见glibc选择新版本,这样某些程序会因为依赖高版本符号而无法在旧glibc系统上执行,暂且不管。为了调试方便把strace和ltrace也选上。
ct-ng build
。失败后可以用ct-ng list-steps
和ct-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以避免服务不可用导致系统自动重启。