Skip to content

Instantly share code, notes, and snippets.

@halmartin
Last active May 4, 2023 16:03
Embed
What would you like to do?
Fujitsu iRMC S4 PoC
#!/usr/bin/env python3
# https://watchmysys.com/blog/2023/01/fujitsu-irmc-s4-license/
import argparse
import base64
import binascii
import zlib
import hmac
import hashlib
import struct
from Crypto.Cipher import AES
# obtain values from /usr/local/lib/libfts_license.so.1.12.1
# Note that in python, you enter binary hex values as b"\x0d\x0e..."
HMAC_KEY = b""
HMAC_MSG = b"" * 4
AES_IV = b""
def init_lookup_table():
out = []
for i in range(0x100):
lookup_table_entry = i << 0x18
for k in range(8):
if lookup_table_entry >> 31:
inlr = lookup_table_entry << 1
if lookup_table_entry >> 31:
lookup_table_entry = inlr ^ 0x4c11db7
else:
lookup_table_entry = lookup_table_entry << 1
lookup_table_entry &= 0xffffffff
out.append(lookup_table_entry)
return out
def spd_crc32(param_1, x):
tbl = init_lookup_table()
for pbVar1 in param_1:
x = (tbl[(pbVar1 ^ x >> 0x18) & 0xff] ^ x << 8) & 0xffffffff
return x
def simple_generator(ext_features, type, serial):
if ext_features.lower() == "kvm":
features = b"\x01\x00\x00\x00"
elif ext_features.lower() == "kvm_media":
features = b"\x03\x00\x00\x00"
elif ext_features.lower() == "kvm_media_elcm":
# this license type doesn't validate on my TX chassis
# but apparently works on RX chassis
if type.lower() == "tx":
print("License may not be valid for TX chassis with eLCM enabled")
features = b"\x0f\x00\x00\x00"
return license_generator(serial, features, type.lower())
def license_generator(serial, features, type):
# chassis type TX
struct_data = 0xffffff00
if type == "rx":
# chassis type RX
struct_data = 0xffffff05
DATA = b'iRMC' \
+ features \
+ struct.pack('>I', struct_data) + \
struct.pack('<I', spd_crc32(serial.encode("ASCII"), 1))
print(binascii.hexlify(DATA).decode('utf-8'))
aes_key = hmac.new(HMAC_KEY, HMAC_MSG, hashlib.sha1).digest()
# this is AES-128, so only take the first 16 bytes of the aes_key
cipher = AES.new(aes_key[:16], AES.MODE_CBC, iv=AES_IV)
encrypted_license = cipher.encrypt(DATA)
encoded_license = base64.b32encode(encrypted_license)
license = encoded_license.decode("UTF-8").replace("=","")
return '-'.join(license[i:i+4] for i in range(0, len(license), 4))
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-t", "--type", type=str, default="tx", help="License type, valid values are 'TX' or 'RX'")
parser.add_argument("-f", "--features", type=str, default="kvm_media", help="License type, valid values are 'KVM', 'KVM_Media', or 'KVM_Media_eLCM'")
parser.add_argument("serial")
args = parser.parse_args()
if len(HMAC_KEY) != 16 or len(HMAC_MSG) != 16*4 or len(AES_IV) != 16:
print("You are missing the HMAC and AES values")
else:
print(simple_generator(args.features, args.type, args.serial))
@halmartin
Copy link
Author

halmartin commented Mar 17, 2023

For people who are having trouble unpacking the firmware:

FROM ubuntu:16.04

RUN apt-get update && apt-get install -y --no-install-recommends binwalk cramfsprogs bsdmainutils

COPY FTS_D3373TX1320M3TX1330M3iRMCKronos4Firmwa_D3373B10969Fsdr0318_1272411.BIN /tmp/
WORKDIR /tmp/

CMD "/bin/bash"

Then you can run binwalk -e on the firmware update to extract the relevant file.

Using the iRMC 9.69F release.

@halmartin
Copy link
Author

halmartin commented May 4, 2023

If you want to investigate the exported license (using “iRMC S4” -> “Save Configuration” -> “Include License Information”), here is a validation script:

#!/usr/bin/env python3

import argparse
import base64
import binascii
import zlib
import hmac
import hashlib
import struct
from sys import exit
from Crypto.Cipher import AES

# obtain values from /usr/local/lib/libfts_license.so.1.12.1
# Note that in python, you enter binary hex values as b"\x0d\x0e..."
HMAC_KEY = b""
HMAC_MSG = b"" * 4
AES_IV = b""

aes_key = hmac.new(HMAC_KEY, HMAC_MSG, hashlib.sha1).digest()

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("license")
    args = parser.parse_args()
    if len(args.license) != 32:
        print("'%s' is not a valid license" % args.license)
        exit(1)
    try:
        encrypted_license = base64.b32decode(args.license)
    except binascii.Error:
        encrypted_license_fujitsu = args.license.replace("-","")
        padding = 32-len(encrypted_license_fujitsu)
        padded_license = encrypted_license_fujitsu + padding*"="
        encrypted_license = base64.b32decode(padded_license)

    cipher = AES.new(aes_key[:16], mode=AES.MODE_CBC, iv=AES_IV)
    decrypted_license = cipher.decrypt(encrypted_license)
    if decrypted_license[:4].decode("UTF-8") == "iRMC":
        if ((decrypted_license[4] & 0x04) >> 2 == 1):
            print("eLCM: enabled")
        if ((decrypted_license[4] & 0x02) >> 1 == 1):
            print("Media: enabled")
        if ((decrypted_license[4] & 0x01) == 1):
            print("KVM: enabled")
        print("(Probably) valid license: %s" % binascii.hexlify(decrypted_license))
    else:
        print("License is missing 'iRMC' magic")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment