Category Archives: Linux

Fujitsu iRMC S4 on the TX140 S2

Fujitsu servers come with a remote management solution called iRMC S4 (newer models have iRMC S5). iRMC S4 and S5 are like other lights-out remote management solutions from HP (iLO) or Dell (iDRAC) which comprises a baseband management controller firmware along with other software utilities to remotely configure and manage servers. Importantly though, iRMC S4 runs Linux.


Before we get into the hardware of iRMC S4, let us examine the firmware update process. iRMC S4 follows a pretty typical BMC firmware update process: Fujitsu’s support website offers firmware downloads, and the iRMC web management interface allows you to upload the update which is then written to the inactive firmware slot.

As is common for enterprise hardware, there is no rollback protection, so you can downgrade the installed firmware to previous versions. I did not extensively test this functionality though, so there may be limits to how far you can downgrade as the firmware modifies the persistent conf partition (which is not redundant).

Running binwalk against the update file for the TX140 S2, we can immediately see that it is not encrypted:

$ binwalk FTS_TX140S2D3239iRMCKronos4FirmwareUpdatefo_TX140S20960Fsdr0344_1233853.BIN 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
148820        0x24554         U-Boot version string, "U-Boot 1.1.6 (Sep 22 2015 - 17:25:45)"
150076        0x24A3C         CRC32 polynomial table, little endian
184888        0x2D238         CRC32 polynomial table, little endian
589824        0x90000         uImage header, header size: 64 bytes, header CRC: 0x2658385F, created: 2020-04-01 11:24:03, image size: 27389952 bytes, Data Address: 0x0, Entry Point: 0x0, data CRC: 0x6B45479A, OS: Linux, CPU: ARM, image type: RAMDisk Image, compression type: none, image name: ""
589888        0x90040         CramFS filesystem, little endian, size: 27389952, version 2, sorted_dirs, CRC 0x6745F599, edition 0, 15794 blocks, 4707 files
27983936      0x1AB0040       uImage header, header size: 64 bytes, header CRC: 0x27056A57, created: 2019-11-04 16:46:29, image size: 3042664 bytes, Data Address: 0x80808000, Entry Point: 0x80808000, data CRC: 0x222093D7, OS: Linux, CPU: ARM, image type: OS Kernel Image, compression type: none, image name: "Linux-3.14.17-ami"
27984000      0x1AB0080       Linux kernel ARM boot executable zImage (little-endian)
28002116      0x1AB4744       gzip compressed data, maximum compression, from Unix, last modified: 1970-01-01 00:00:00 (null date)
31129600      0x1DB0000       CramFS filesystem, little endian, size: 45056, version 2, sorted_dirs, CRC 0x52551191, edition 0, 31 blocks, 12 files

As far as I have been able to determine, here is the firmware layout of iRMC S4 on the TX140 S2:

00000000:0008ffff uboot1
00090000:01aaf040 root1 # cramfs1
01ab0040:01daffff zImage1
01db0000:01dc0000 platform1 # sdr1
01e24554:01e8ffff uboot2
01e90000:038af040 root2 # cramfs2
038b0040:03baffff zImage2
03bb0000:03bc0000 platform2 # sdr2
03c00000:03ff0000 conf
03ff0000:03ffffff fru

These correspond to the lower and higher firmware slots in iRMC S4, and ensure that the firmware you are updating is not the currently running firmware.


So, could our way into iRMC S4 be as easy as modifying the cramfs from the firmware update?

Unfortunately, no. The update is signed and the signature is checked by /usr/local/bin/flasher against an RSA-1024 public key located on the conf partition prior to overwriting:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCdgO/cGwthsFEZLuohVB5DNvU/
LolrQobsNASL4Sc+uzn8PsULIPiG0v3zhR8zCwlChmF/umVvk1gxKy5cAY0Bj3oo
cUhXwHf4t2ty+2ZY+p975Yg6YonQJSTKVPVfBlk/9PqNRj/Ih5P3zqd80YxAoKhX
i77qhLxjehHLsRSP2QIDAQAB
-----END PUBLIC KEY-----

Attempting to modify and repack cramfs results in the following output to the UART:

[1533 : 1533 INFO]VerifyImage
Signature Verification Failure
[1533 : 1533 CRITICAL][utils.c:1241]Signature verification failed 
[1533 : 1533 CRITICAL][utils.c:1522]Encrypted hash of Image and the actual contents of rom.ima does not match

With our software-only modification route looking grim, it is time to move on into the realm of the evil maid.

On the TX140 S2 the BMC UART (38400n8) has been routed to pads, located just below PCIe slot 2, which are easily soldered to:

BMC UART connections on a Fujitsu TX140 S2 motherboard
BMC UART on the TX140 S2 (D3239) motherboard

To stop the default boot sequence, press Escape within 2 seconds:

U-Boot 1.1.6 (Jun 20 2013 - 09:09:05)

DRAM:  247 MB
Fast clk is set
Found SPI Chip Macronix MX66L51235F 
Flash: 64 MB
Net:   pilot_eth0, pilot_eth1
Hit Esc key to stop autoboot:  0 
------ Boot Options-------
        0. Normal Boot
        1. Diagnostics
        2. Remote Recovery
        3. Management Console
        4. Raw Console
Select Boot Option:

The GPL source code for iRMC S4 was requested in December 2020, and at the time of writing Fujitsu had not provided the source code. Without the source code for u-boot, it is difficult to determine if there are any routes that could lead to easy exploitation.


Getting a root console is relatively straightforward with soldering or a chip jig. If you use a jig, you will need very steady hands as flashrom requires 20-30 minutes to write and verify the 27MB cramfs region.

512MBit MXIC flash of the TX140 S2 iRMC S4 BMC

Lucky me, Fujitsu engineers considered physical modification of the iRMC S4 firmware out of scope, and there is no secure boot or signature verification of the cramfs on flash.

Since we can manipulate cramfs, we can bypass the stock Fujitsu shell and replace /usr/local/bin/remman with a symlink to /bin/sh and SSH as the admin user. This is not particularly useful though, as the admin user is not root sysadmin, and the busybox that Fujitsu ship is lacking the su applet, so there’s no way to easily escalate your privileges from admin to sysadmin once logged in.

~ $ id
uid=1002(admin) gid=501(ipmi) groups=501(ipmi),504(lanoem),510(serialoem),528(iRMCsettings),529(RemoteStorage),530(UserAccounts),531(VideoRedirection),532(CfgConnectionBlade),535(RemoteManager)

The uid 0 account is not called root, but rather sysadmin with the password superuser:

sysadmin:$1$A17c6z5w$5OsdHjBn1pjvN6xXKDckq0:18627:0:99999:7:::

The sysadmin account is not visible in the iRMC web interface and, as far as I can tell, the password cannot be changed (unless you physically modify the contents of cramfs). I believe the account is leftover from the SDK that iRMC S4 appears to be based on.

All my attempts to login as sysadmin via SSH or uart with the default remman shell were unsuccessful, so it doesn’t appear to be a security risk out of the box.

However, once you have replaced /usr/local/bin/remman with a symlink to /bin/sh it is possible to login as the sysadmin user and enjoy root access to your iRMC S4.

Meraki MX80: buildroot firmware

Ten years ago, before ARM was in everything, many embedded systems that did not justify using an x86 used PowerPC (or MIPS). This leads us to today’s subject: the Meraki MX80, an “enterprise security appliance.”

Meraki MX80 marketing image

Meraki MX80

The MX80 is End-of-Life (EOL) and can be purchased quite inexpensively on eBay.

The MX-series differs from other Meraki networking products of the 2010-era that we have previously covered (the MS220) in that it is PowerPC based (APM86290) with 2GB of RAM and 1GB of NAND flash. The factory bootlog of the device can be found in this GitHub gist.


MX80 PCB with uart header highlighted

MX80 UART header

Removing the cover allows you to connect to the UART header J3 (57600n8), with the pinout:

  1. VCC (don’t connect)
  2. Rx
  3. Tx
  4. GND

Ground (pin 4) is closest to the Ethernet ports.

Obtaining a root shell is very easy as u-boot has a 1 second boot delay and accepts input on UART. The default meraki_boot target sets the bootargs from meraki_bootargs which appends extra_bootargs, so just override rdinit with /bin/sh to prevent the Meraki OS from booting.

setenv extra_bootargs rdinit=/bin/sh
run meraki_boot

Once the MX80 has booted, bring up eth0 (port label Internet on the MX80):

$ mount -t proc proc /proc
$ mount -t sysfs sysfs /sys
$ ifconfig lo up
$ ifconfig eth0 up
$ udhcpc eth0

Once you have functional networking, you can dump the contents of NAND to a remote host for further analysis:

$ cat /proc/mtd
dev:    size   erasesize  name
mtd0: 00100000 00020000 "firmware"
mtd1: 00100000 00020000 "environment"
mtd2: 00040000 00020000 "oops-old"
mtd3: 3fdc0000 00020000 "ubi"
mtd4: 40000000 00020000 "all"
mtd5: 0201d800 0001f800 "part1"
mtd6: 0201d800 0001f800 "part2"
mtd7: 0001f800 0001f800 "board-config"
mtd8: 2001f000 0001f800 "storage"
$ dd if=/dev/mtdblock4 bs=1M | gzip -c | nc -l -p 5000

Running binwalk on “part1” shows us the structure of Meraki’s firmware:

$ binwalk part1.bin

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
1024          0x400           device tree image (dtb)
16384         0x4000          device tree image (dtb)
131072        0x20000         uImage header, header size: 64 bytes, header CRC: 0xD84034B5, created: 2020-05-29 01:15:36, image size: 2447726 bytes, Data Address: 0x0, Entry Point: 0x0, data CRC: 0xCE3A4A5E, OS: Linux, CPU: PowerPC, image type: OS Kernel Image, compression type: gzip, image name: "Linux-3.4.113"
131136        0x20040         gzip compressed data, maximum compression, from Unix, last modified: 1970-01-01 00:00:00 (null date)
4194304       0x400000        uImage header, header size: 64 bytes, header CRC: 0xDA83B155, created: 2020-05-29 01:16:17, image size: 16719915 bytes, Data Address: 0x0, Entry Point: 0x0, data CRC: 0x2381914F, OS: Linux, CPU: PowerPC, image type: RAMDisk Image, compression type: lzma, image name: "Simple Ramdisk Image"
4194368       0x400040        LZMA compressed data, properties: 0x5D, dictionary size: 67108864 bytes, uncompressed size: -1 bytes

There is a device tree image at offset 0x400 which seems to be for the MX60 (codename bluestone). There is a second device tree image at offset 0x4000 for the MX80 (codename fullerene).

It is not as simple as creating a binary image with the DTB at offset 0x4000, the kernel at 0x200000, and initrd at 0x40000 because Meraki have modified u-boot to have a custom command meraki which reads a header, verifies the contents of the ubi partition part1 or part2 with SHA1, and then sets environment variables from addresses defined in the header.

The layout of the header is as follows:

Header field Data type (value)
SHA1_MAGIC uint32 (0x8e73ed8a)
HEADER_LEN int32
DATA_LEN int32
SHA1SUM char[20]
MERAKI_EXTRA_MAGIC uint32 (0xa1f0beef)
MERAKI_EXTRA_LEN uint16 (0x0006)
MERAKI_EXTRA_TYPE uint16 (0x0001)
IMAGE_OFFSET uint32 (0x20000)
RAMDISK_OFFSET uint32 (0x400000)
FDT_OFFSETS array uint32 (0x400 or 0x4000)

The FDT used to boot depends on the value of meraki_part_fdt_index in u-boot. For the MX80, the index of the FDT offset is 1, meaning the FDT located at 0x4000 is used to boot. The presence of two FDTs suggests that Meraki are using the same firmware for both the MX60 and MX80. Despite the MX80 being a dual core CPU only one CPU core is usable, there is no SMP support in the kernel provided by Meraki.


To simplify booting, I have written a post-image.sh script which generates the appropriate header and assembles a bootable firmware image as part of the buildroot build process. You can find instructions on how to build the firmware in the meraki-builder GitHub repository.

The 3.4 kernel provided by Meraki doesn’t have any of the features required by OpenWrt (e.g. overlayfs) and buildroot doesn’t have a package manager. If you just want something to boot and run SSH on, then the buildroot image fulfills that need. You will most probably want to customize buildroot to include the packages and configuration that suits your needs. Upstream support in OpenWrt is still a long way away, as the APM86290 does not have support in the mainline kernel.

Meraki MS220: PoE support

The last several posts in this series have focused primarily on getting a custom firmware running on the Meraki MS220-series switches, without much regard for preserving existing features. Since I am now at a point where my custom firmware is functional as a Layer 2-ish switch, my attention has turned to PoE, since many switches in the series have PoE support and that is feature I think switch owners (especially MS220-8P) are interested in.

From my investigation into libpoecore included in the Meraki firmware, PoE on the MS220-8P appears to be managed by Microsemi’s PD690xx series of Power over Ethernet Management chips (datasheet). The PD690xx series communicates over I2C with the management CPU to manage PoE on the switch ports (enable/disable PoE, set 802.3af/at modes, query power consumed by a PoE device).

We can confirm that the PD690xx communicates via I2C by running poe_server from Meraki’s firmware and enabling I2C tracing in the kernel:

# cat /sys/kernel/debug/tracing/trace
# tracer: nop
#
# entries-in-buffer/entries-written: 1358/1358   #P:1
#
#                              _-----=> irqs-off
#                             / _----=> need-resched
#                            | / _---=> hardirq/softirq
#                            || / _--=> preempt-depth
#                            ||| /     delay
#           TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |       |   ||||       |         |
      poe_server-682   [000] ....   560.356000: i2c_write: i2c-1 #0 a=030 f=0000 l=4 [13-32-0f-ff]
      poe_server-682   [000] ....   560.358000: i2c_result: i2c-1 n=1 ret=1
      poe_server-682   [000] ....   560.358000: i2c_write: i2c-1 #0 a=030 f=0000 l=2 [13-32]
      poe_server-682   [000] ....   560.358000: i2c_read: i2c-1 #1 a=030 f=0001 l=2
      poe_server-682   [000] ....   560.359000: i2c_reply: i2c-1 #1 a=030 f=0001 l=2 [0f-ff]
      poe_server-682   [000] ....   560.359000: i2c_result: i2c-1 n=2 ret=2
      poe_server-682   [000] ....   560.359000: i2c_write: i2c-1 #0 a=030 f=0000 l=4 [13-32-0f-ff]
      poe_server-682   [000] ....   560.360000: i2c_result: i2c-1 n=1 ret=1
      poe_server-682   [000] ....   560.360000: i2c_write: i2c-1 #0 a=030 f=0000 l=4 [13-9e-dc-03]
      poe_server-682   [000] ....   560.362000: i2c_result: i2c-1 n=1 ret=1

I2C tracing is extremely helpful, as running strace against poe_server directly will not yield useful output as to what operations it is performing to configure PoE.

While it is good news that we are able to recover the I2C commands via kernel tracing, it’s bad news in the sense that writing a new daemon to duplicate the features of poe_cli is non-trivial.


Thankfully, with the libpoecore from the Meraki firmware dump and free disassembly tools like Ghidra (sorry Hex-Rays, support MIPS in IDA Free ¯\_(ツ)_/¯), understanding some of the logic behind functionality provided by poe_server and poe_cli becomes much easier.

If you disassemble libpoecore, you can find the function hard_init which contains code to set up GPIO outputs. Interesting to note is that while the GPIO pins change depending on which switch ASIC is present, the sequence of GPIO outputs to configure the PD690xx remains constant.

Disassembler view of the function hard_init from the library libpoecore.so

The same GPIO configuration is executed when switch_brain is started (full strace output):

writev(1, [{iov_base="", iov_len=0}, {iov_base="echo 7 > /sys/class/gpio/export\n", iov_len=32}], 2echo 7 > /sys/class/gpio/export) = 32
writev(1, [{iov_base="", iov_len=0}, {iov_base="echo 12 > /sys/class/gpio/export\n", iov_len=33}], 2echo 12 > /sys/class/gpio/export) = 33
writev(1, [{iov_base="", iov_len=0}, {iov_base="echo out > /sys/class/gpio/gpio7/direction\n", iov_len=43}], 2echo out > /sys/class/gpio/gpio7/direction) = 43
writev(1, [{iov_base="", iov_len=0}, {iov_base="echo out > /sys/class/gpio/gpio12/direction\n", iov_len=44}], 2echo out > /sys/class/gpio/gpio12/direction) = 44
writev(1, [{iov_base="", iov_len=0}, {iov_base="echo 1 > /sys/class/gpio/gpio7/value\n", iov_len=37}], 2echo 1 > /sys/class/gpio/gpio7/value) = 37
writev(1, [{iov_base="", iov_len=0}, {iov_base="echo 0 > /sys/class/gpio/gpio12/value\n", iov_len=38}], 2echo 0 > /sys/class/gpio/gpio12/value) = 38
writev(1, [{iov_base="", iov_len=0}, {iov_base="echo 1 > /sys/class/gpio/gpio12/value\n", iov_len=38}], 2echo 1 > /sys/class/gpio/gpio12/value) = 38
writev(1, [{iov_base="", iov_len=0}, {iov_base="echo 0 > /sys/class/gpio/gpio7/value\n", iov_len=37}], 2echo 0 > /sys/class/gpio/gpio7/value) = 37
writev(1, [{iov_base="", iov_len=0}, {iov_base="echo 1 > /sys/class/gpio/gpio12/value\n", iov_len=38}], 2echo 1 > /sys/class/gpio/gpio12/value) = 38

The datasheet for the luton26 ASIC used in the MS220-8P, MS220-24P, and MS22 (VDMS-10393) doesn’t list anything connected to GPIO 7, and GPIO 12 is used for either SFP17_SD or PHY7_LED1 depending on the overlay function chosen. The functionality of these GPIO pins is undefined in the ASIC datasheet, however libpoecore is setting them and manipulating their outputs.

We can implement the logic of hard_init in an init script to set up the GPIO pins in the same way, and the result is that the PD690xx is configured for auto mode. I am not sure how, there is nothing in the PD690xx datasheet which suggests GPIO pins can be used to configure the operating mode, but the switch will automatically negotiate and power a PoE device.

Writing a new daemon to communicate with the PD690xx will ultimately be necessary if fine control over PoE functionality is to be achieved. Without I2C communication to the PD690xx, it is not possible to query the power budget, or limit port power delivery. In the mean time, for those who do not mind unmanaged “plug-and-play” style, PoE can be considered functional.