redballoonsecurity / ofrak

OFRAK: unpack, modify, and repack binaries.
https://ofrak.com
Other
1.85k stars 126 forks source link

OpenWRT TRX unpacker fails to unpack when rootfs_offset is null #153

Closed RLS-Rylan closed 1 year ago

RLS-Rylan commented 1 year ago

What is the problem? (Here is where you provide a complete Traceback.) Many TRX images seem to zero out the rootfs_offset field in the TRX header, and store data in a UBIFS filesystem at kernel_offset within the image. When this happens, the OpenWrtTrxKernel is empty, and the OpenWrtTrxRootfs object contains all data from offset 0 to the end of the file.

Please provide some information about your environment. redballoonsecurity/ofrak/binaryninja

If you've discovered it, what is the root cause of the problem?

How often does the issue happen? This seems to affect all targets except bcrm47xx/mips74k across all versions of OpenWRT

What are the steps to reproduce the issue? Running the following function on a (bcm53xx target)[https://downloads.openwrt.org/releases/19.07.0/targets/bcm53xx/generic/openwrt-19.07.0-bcm53xx-buffalo-wxr-1900dhp-squashfs.trx] and a (bcrm47xx)[https://downloads.openwrt.org/releases/21.02.0-rc1/targets/bcm47xx/mips74k/openwrt-21.02.0-rc1-bcm47xx-mips74k-asus_rt-n10u-b-squashfs.trx] will produce the output below

async def unpack_image(resource):
    resource.add_tag(OpenWrtTrx)
    await resource.save()
    await resource.unpack()

    trx_view = await resource.view_as(OpenWrtTrx)
    trx_header = await trx_view.get_header()
    print(f'loader_offset: {hex(trx_header.trx_loader_offset)}')
    print(f'kernel_offset: {hex(trx_header.trx_kernel_offset)}')
    print(f'rootfs_offset: {hex(trx_header.trx_rootfs_offset)}')

    kernel = await resource.get_only_descendant_as_view(
        OpenWrtTrxKernel, r_filter=ResourceFilter(tags=(OpenWrtTrxKernel,))
    )
    print(f'kernel data length: {await kernel.resource.get_data_length()}')

    print(await resource.summarize_tree())
openwrt-19.07.0-bcm53xx-buffalo-wxr-1900dhp-squashfs.trx
loader  offset: 0x1c
kernel_offset: 0x400000
rootfs_offset: 0x0
kernel data length: 0
┌f3ae23f495724d2da8dde702461de851: [caption=(File, OpenWrtTrx), attributes=(Magic), global_offset=(0x0-0x640000), parent_offset=(0x0-0x0), data_hash=46cdc765]
├────d712a6fd9582414b887b5561b657de70: [caption=(OpenWrtTrxHeader), attributes=(OpenWrtTrxHeaderAutoAttributes), global_offset=(0x0-0x1c), parent_offset=(0x0-0x1c), data_hex=48445230000064003c0f556b000001001c0000000000400000000000]
├────6a81c1d4de354793a6647fa639a3fb86: [caption=(OpenWrtTrxLzmaLoader), attributes=(), global_offset=(0x0-0x3fffe4), parent_offset=(0x0-0x0), data_hash=0d248e14]
├────fe9b61633a8e4deeb7ab2655b3a6fbbd: [caption=(OpenWrtTrxKernel), attributes=(), global_offset=(0x0-0x0), parent_offset=(0x0-0x0), data_ascii=""]
└────539a7426ad7846fd8f58ac3ff323bc47: [caption=(OpenWrtTrxRootfs), attributes=(), global_offset=(0x0-0x640000), parent_offset=(0x0-0x0), data_hash=46cdc765]

openwrt-21.02.0-rc1-bcm47xx-mips74k-asus_rt-n10u-squashfs.trx
loader  offset: 0x1c
kernel_offset: 0x948
rootfs_offset: 0x1d1000
kernel data length: 1902264
┌dadda0afd664423aa8adff0c92ebb3db: [caption=(File, OpenWrtTrx), attributes=(Magic), global_offset=(0x0-0x531000), parent_offset=(0x0-0x0), data_hash=812040c8]
├────2c37c975cd13402dadc6a0d1b083c775: [caption=(OpenWrtTrxHeader), attributes=(OpenWrtTrxHeaderAutoAttributes), global_offset=(0x0-0x1c), parent_offset=(0x0-0x1c), data_hex=4844523000105300e1851726000001001c0000004809000000101d00]
├────99ac34f3e7ab4db9b7aaff3f02de0878: [caption=(OpenWrtTrxLzmaLoader), attributes=(), global_offset=(0x0-0x92c), parent_offset=(0x0-0x0), data_hash=3bac3c22]
├────8edc7a6eb7864c668a217b7111fa0327: [caption=(OpenWrtTrxKernel), attributes=(), global_offset=(0x0-0x1d06b8), parent_offset=(0x0-0x0), data_hash=b4c54f04]
└────35106e38c1ee40779b06f147807c39b9: [caption=(OpenWrtTrxRootfs), attributes=(), global_offset=(0x0-0x360000), parent_offset=(0x0-0x0), data_hash=8b281388]

It took 1.363 seconds to run the OFRAK script

How would you implement this fix?

Are there any (reasonable) alternative approaches?

Are you interested in implementing it yourself?

EdwardLarson commented 1 year ago

Thanks for reporting this and providing an example. Have you found documentation on this null-rootfs_offset behavior? It would help clarify the following questions:

  1. I assume the desired behavior is that the OpenWrtTrxRootfs should be empty (or nonexistant) and the OpenWrtTrxKernel should include all the data starting from trx_kernel_offset to the end (or trx_binheader_offset if it's version 2)?
  2. Is this just on Version 1, or 2, or both?

I can see where in the Packer and Unpacker this change is needed, should be simple enough once the desired behavior is clear.

RLS-Rylan commented 1 year ago

Unfortunately the only documentation I've come across so far is: 1) The OpenWRT header wiki page that the diagrams in the ofrak TRX unpacker appear to be from 2) trx.c from OpenWRT's firmware utils constructs TRX headers, but doesn't mention anything about the rootfs being null

Although I'm not seeing any documentation, I think your assumptions are accurate - ie. OpenWrtTrxRootfs should be empty/nonexistant and OpenWrtTrxKernel should include all data from trx_kernel_offset to the end of the file or trx_binheader_offset (if present).

It looks like all the TRX files I was able to pull from the OpenWRT website are TRX v1, so I have not yet tested this on TRX v2 firmware.

whyitfor commented 1 year ago

216 makes core changes to OpenWRT TRX files that should address this issue.