Skip to content

Fix heap out-of-bounds reads on short iCLASS dump files - #3433

Open
munzzyy wants to merge 1 commit into
RfidResearchGroup:masterfrom
munzzyy:fix/iclass-short-dump-oob
Open

Fix heap out-of-bounds reads on short iCLASS dump files#3433
munzzyy wants to merge 1 commit into
RfidResearchGroup:masterfrom
munzzyy:fix/iclass-short-dump-oob

Conversation

@munzzyy

@munzzyy munzzyy commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

hf iclass eload -v, hf iclass decrypt and hf iclass eview -v all cast a buffer loaded from a dump file straight to picopass_hdr_t* and print it, without checking the buffer is actually that big. Same root cause as #3412. pm3_load_dump() sizes the allocation to the real file content, and only clamps when the file is larger than the max; it never pads a short file up. So a truncated dump gives you a short heap block and the print code walks off the end. #3412 only covered view.

decrypt is the worst of the three. It needs no -v, and it probes fixed offsets through block 9 (app_issuer_area, the aa1_encryption flag and block 7 inside iclass_decode_credentials(), then its own block 9 PACS/PIN check) no matter what applimit or decryptedlen / 8 say the real block count is. On a release build that prints garbage heap bytes labelled as CSN, config, debit key and PACS.

Confirmed with make client SANITIZE=1 on an unmodified tree, no device attached:

$ ./client/proxmark3 -c "hf iclass decrypt -f tiny.bin --ns"      # 8-byte file
==ERROR: AddressSanitizer: heap-buffer-overflow
READ of size 1 at 0x... thread T4 (WorkerThread)
    #0 CmdHFiClassDecrypt src/cmdhficlass.c:2387
0x... is located 4 bytes after 8-byte region

eload -f tiny.bin -v crashes the same way inside print_picopass_header via sprint_hex/hex_to_buffer, the identical signature to the #3412 report.

The fix checks the loaded length against what the code is about to read. For eload and eview I only skip the unsafe verbose print, since the upload itself should still work on a legitimately short custom dump. decrypt refuses below 10 blocks, because the credential decode always reaches block 9.

I first tried a 7-block minimum for decrypt and it still crashed one function deeper, in the block 9 check inside iclass_decode_credentials(). That's why the guard is 10 blocks and not 7.

Boundary cases re-checked after the fix, same ASan build:

8-byte  file -> Error, dump file is too small - bytes: 8, expected at least: 80   (no crash)
80-byte file -> full tag memory table, CSN and block decode as normal

One honest gap: I couldn't get a live crash for eview. It needs a connected device, and offline GetFromDevice times out before reaching the vulnerable print. So that third guard is a same-pattern fix from reading the code, not a reproduced crash like the other two.

hf iclass eload -v, hf iclass decrypt and hf iclass eview -v all cast a
loaded dump buffer to picopass_hdr_t* and print it without checking the
buffer is that big. pm3_load_dump() sizes the allocation to the real file
content and only clamps when it's larger than the max, never pads a short
file up, so a truncated dump gives a short heap block and the print code
reads past it. Same root cause as RfidResearchGroup#3412, which only covered view.

decrypt is the worst of the three: no -v needed, and it probes fixed
offsets through block 9 - app_issuer_area, the aa1_encryption flag and
block 7 inside iclass_decode_credentials(), then its own block 9 PACS/PIN
check - regardless of what applimit or decryptedlen/8 claim the real block
count is.

Check the loaded length against what's about to be read. eload and eview
skip only the unsafe verbose print so a legitimately short custom dump
still uploads; decrypt refuses below 10 blocks because the credential
decode always reaches block 9.

Signed-off-by: Cole Munz <colemunz@gmail.com>
@github-actions

Copy link
Copy Markdown

You are welcome to add an entry to the CHANGELOG.md as well

@munzzyy

munzzyy commented Jul 27, 2026

Copy link
Copy Markdown
Contributor Author

The failing proxspace check here isn't from this change. It dies compiling a bundled dependency:

client/deps/jansson/load.c:193:35: error: writing 1 byte into a region of size 0 [-Werror=stringop-overflow=]
make[2]: *** [deps/CMakeFiles/pm3rrg_rdv4_jansson.dir/.../load.c.obj] Error 1

src/cmdhficlass.c — the only file I touched — compiles clean in that same run, both times it's built. The same "Windows Build and Test" job is already failing on master (two runs on 2026-07-25), and PR #3427 from another contributor fails it the same way, so it looks like the MSYS2/ucrt64 GCC in ProxSpace got new enough to flag that jansson write under -Werror=stringop-overflow.

Every other check on this PR is green, including Analyze (cpp) and CodeQL. Happy to rebase once Windows is unblocked if that helps.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant