Add ST+ACL check to hf mfp wrbl

Sector trailers are now checked for errors.
Writes should not go through if for whatever reason ACLs provided to write are invalid.

Reminder that ACLs are checked on the 4-byte method of MFP with the encrypted-only exchange byte.

Signed-off-by: team-orangeBlue <63470411+team-orangeBlue@users.noreply.github.com>
This commit is contained in:
team-orangeBlue
2026-04-11 00:14:36 +03:00
committed by GitHub
parent f8f6216958
commit 394aec40e2
+50
View File
@@ -1180,6 +1180,43 @@ static int CmdHFMFPRdsc(const char *Cmd) {
return PM3_SUCCESS;
}
static int mfp_analyse_st_block(uint8_t blockno, uint8_t *block, bool force) {
if (mfIsSectorTrailer(blockno) == false) {
return PM3_SUCCESS;
}
PrintAndLogEx(INFO, "Sector trailer (ST) write detected");
// ensure access right isn't messed up.
if (mfValidateAccessConditions(block + 6) == false || ((block[5] >> 4) != ((~block[5]) & 0xF))) {
PrintAndLogEx(WARNING, "Invalid Access Conditions detected, replacing with default values");
memcpy(block + 5, "\x0F\xFF\x07\x80\x69", 5);
}
bool ro_detected = false;
uint8_t bar = mfNumBlocksPerSector(mfSectorNum(blockno));
for (uint8_t foo = 0; foo < bar; foo++) {
if (mfReadOnlyAccessConditions(foo, &block[6])) {
PrintAndLogEx(WARNING, "Strict ReadOnly Access Conditions on block " _YELLOW_("%u") " detected", blockno - bar + 1 + foo);
ro_detected = true;
}
}
if (ro_detected) {
if (force) {
PrintAndLogEx(WARNING, " --force override, continuing...");
} else {
PrintAndLogEx(INFO, "Exiting, please run `" _YELLOW_("hf mf acl -d %s") "` to understand", sprint_hex_inrow(&block[6], 3));
PrintAndLogEx(INFO, "Use `" _YELLOW_("--force") "` to override and write this data");
return PM3_EINVARG;
}
} else {
PrintAndLogEx(SUCCESS, "ST checks ( " _GREEN_("ok") " )");
}
return PM3_SUCCESS;
}
static int CmdHFMFPWrbl(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mfp wrbl",
@@ -1197,6 +1234,7 @@ static int CmdHFMFPWrbl(const char *Cmd) {
arg_lit0(NULL, "nmr", "Do not expect MAC in response"),
arg_str1("d", "data", "<hex>", "Data, 16 hex bytes"),
arg_str0("k", "key", "<hex>", "Key, 16 hex bytes"),
arg_lit0(NULL, "force", "Override warnings"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
@@ -1214,6 +1252,8 @@ static int CmdHFMFPWrbl(const char *Cmd) {
uint8_t key[250] = {0};
int keylen = 0;
CLIGetHexWithReturn(ctx, 7, key, &keylen);
bool force = arg_get_lit(ctx, 8);
CLIParserFree(ctx);
uint8_t keyn[2] = {0};
@@ -1239,6 +1279,16 @@ static int CmdHFMFPWrbl(const char *Cmd) {
PrintAndLogEx(ERR, "<data> must be 16 bytes. Got %d", datainlen);
return PM3_EINVARG;
}
// Necessary checks before doing any actual computing + tag interaction
// Block 0 detection
if (blockNum == 0) {
PrintAndLogEx(FAILED, "Cannot write block 0 on Mifare Plus");
return PM3_EINVARG;
}
// ACL validity check
if (mfp_analyse_st_block(blockNum, datain, force) != PM3_SUCCESS) {
return PM3_EINVARG;
}
uint8_t sectorNum = mfSectorNum(blockNum & 0xff);
uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0);