Idea to bypass RSA signature verification


I would like to suggest a idea for people who tries to support any new device and are stuck with RSA validation, specially for flashing firmware without doing SPI/UART connections.

During the last days I was playing with, a simple POC where I play with LD_PRELOAD feature of Linux. It allows to hook any system/library call and replace with your own implementation. This is widely used for things like:

  • Monitor memory usage of an application by hooking the free() and malloc() calls (Valgrind)
  • Hook networks calls to analyze where a app try to connect
  • Etc...

In my case, I was doing it for SSL certificate validation (and yes, it bypass invalid certs), but the interesting part is here: Why not use it to hook Hash/Signature calls and make them accept any stuff as valid?

There should be only two requirements i guess:

  • The OEM writter/flashed must be dynamically linked (most probably)
  • LD_PRELOAD enabled in the system (most probably)
  • Shell access (root access is not even needed)

A simple example can be found here:


1 Like

Indeed, LD_PRELOAD is a more generic way, certainly better then patching out the checks in the factory flashing binaries.

As always, the devil is hidden in the details. It's not only just about bypassing the RSA/signature crypto checks, but also about knowing the structure of the factory firmware image and providing images in the 100% compatible format (including some fake RSA signatures in the image), otherwise you would need to handle Bad Image Structure errors as well:

XW.v6.1.7# fwupdate.real -m /tmp/openwrt-ath79-generic-ubnt_bullet-m2hp-squashfs-factory.bin  -d
Current: XW.ar934x.v6.1.7.32555.180523.1754

New ver: XW.ar934x.v6.0.4-OpenWrt-r8452+9-e95e9fc
Versions: New(393220) 6.0.4, Required(393220) 6.0.4
FW Part: "kernel"(1), MAGIC: 'PART', Base: 0x9F050000, DLen: 0x00100000, PLen: 0x00100000
FW Part: "rootfs"(2), MAGIC: 'PART', Base: 0x9F150000, DLen: 0x00280004, PLen: 0x00660000
Bad Image Structure
Signature check failed

Otherwise you would need to be hooking fread or such as well, which would make it quite complex.

For the record, here is the read-elf output for that fwupdate.real binary:

ELF Header:
  Magic:   7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, big endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           MIPS R3000
  Version:                           0x1
  Entry point address:               0x4084e0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          0 (bytes into file)
  Flags:                             0x50001007, noreorder, pic, cpic, o32, mips32
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         7
  Size of section headers:           0 (bytes)
  Number of section headers:         0
  Section header string table index: 0

There are no sections in this file.

There are no sections in this file.

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x00400034 0x00400034 0x000e0 0x000e0 R E 0x4
  INTERP         0x000114 0x00400114 0x00400114 0x00014 0x00014 R   0x1
      [Requesting program interpreter: /lib/]
  REGINFO        0x000128 0x00400128 0x00400128 0x00018 0x00018 R   0x4
  LOAD           0x000000 0x00400000 0x00400000 0xa6f64 0xa6f64 R E 0x1000
  LOAD           0x0a7000 0x004e7000 0x004e7000 0x07980 0x0d104 RW  0x1000
  DYNAMIC        0x000140 0x00400140 0x00400140 0x07c51 0x07c51 RWE 0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4

Dynamic section at offset 0x140 contains 37 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: []
 0x00000001 (NEEDED)                     Shared library: []
 0x00000001 (NEEDED)                     Shared library: []
 0x00000001 (NEEDED)                     Shared library: []
 0x00000001 (NEEDED)                     Shared library: []
 0x00000001 (NEEDED)                     Shared library: []
 0x00000001 (NEEDED)                     Shared library: []
 0x00000001 (NEEDED)                     Shared library: []
 0x00000001 (NEEDED)                     Shared library: []
 0x00000001 (NEEDED)                     Shared library: []
 0x00000001 (NEEDED)                     Shared library: []
 0x00000001 (NEEDED)                     Shared library: []
 0x00000001 (NEEDED)                     Shared library: []
 0x0000000c (INIT)                       0x408454
 0x0000000d (FINI)                       0x484e60
 0x00000004 (HASH)                       0x400290
 0x00000005 (STRTAB)                     0x404d10
 0x00000006 (SYMTAB)                     0x401800
 0x0000000a (STRSZ)                      12417 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x70000016 (MIPS_RLD_MAP)               0x4edc10
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x4edc20
 0x00000011 (REL)                        0x0
 0x00000012 (RELSZ)                      0 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x70000001 (MIPS_RLD_VERSION)           1
 0x70000005 (MIPS_FLAGS)                 NOTPOT
 0x70000006 (MIPS_BASE_ADDRESS)          0x400000
 0x7000000a (MIPS_LOCAL_GOTNO)           18
 0x70000011 (MIPS_SYMTABNO)              849
 0x70000012 (MIPS_UNREFEXTNO)            37
 0x70000013 (MIPS_GOTSYM)                0xb
 0x6ffffffe (VERNEED)                    0x408434
 0x6fffffff (VERNEEDNUM)                 1
 0x6ffffff0 (VERSYM)                     0x407d92
 0x00000000 (NULL)                       0x0

And you shouldn't forget about signature verification on fit images from u-boot that is at least used on some IPQ boards.