Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion applications/main/nfc/scenes/nfc_scene_mf_ultralight_write.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,19 @@ NfcCommand nfc_scene_mf_ultralight_write_worker_callback(NfcGenericEvent event,
mfu_event->data->poller_mode = MfUltralightPollerModeWrite;
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventCardDetected);
} else if(mfu_event->type == MfUltralightPollerEventTypeAuthRequest) {
mfu_event->data->auth_context.skip_auth = true;
const MfUltralightData* mfu_ref_data =
nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);
MfUltralightConfigPages* config = NULL;
mf_ultralight_get_config_page(mfu_ref_data, &config);
if(config->auth0 <= mfu_ref_data->pages_total - 1) {
Comment on lines +26 to +27

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

There's a potential NULL pointer dereference here. The function mf_ultralight_get_config_page returns a bool indicating success or failure, but its return value is not checked. If it returns false, the config pointer will remain NULL, leading to a crash when config->auth0 is accessed.

You should check the return value of mf_ultralight_get_config_page and also ensure config is not NULL before dereferencing it. The suggested change incorporates these checks into the if condition, ensuring that authentication is skipped if the configuration page cannot be retrieved.

Suggested change
mf_ultralight_get_config_page(mfu_ref_data, &config);
if(config->auth0 <= mfu_ref_data->pages_total - 1) {
if(mf_ultralight_get_config_page(mfu_ref_data, &config) && config &&
(config->auth0 <= mfu_ref_data->pages_total - 1)) {

uint8_t pwd_page_idx = mf_ultralight_get_pwd_page_num(mfu_ref_data->type);
memcpy(
mfu_event->data->auth_context.password.data,
mfu_ref_data->page[pwd_page_idx].data,
sizeof(MfUltralightAuthPassword));
} else {
mfu_event->data->auth_context.skip_auth = true;
}
} else if(mfu_event->type == MfUltralightPollerEventTypeRequestWriteData) {
mfu_event->data->write_data =
nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight);
Expand All @@ -31,6 +43,8 @@ NfcCommand nfc_scene_mf_ultralight_write_worker_callback(NfcGenericEvent event,
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
command = NfcCommandStop;
} else if(mfu_event->type == MfUltralightPollerEventTypeWriteFail) {
FURI_LOG_D("MfUltralightWrite", "Write failed with %d", mfu_event->data->error);
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerFailure);
command = NfcCommandStop;
} else if(mfu_event->type == MfUltralightPollerEventTypeWriteSuccess) {
view_dispatcher_send_custom_event(instance->view_dispatcher, NfcCustomEventPollerSuccess);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ void nfc_scene_mf_ultralight_write_fail_on_enter(void* context) {
AlignLeft,
AlignTop,
FontSecondary,
"Card protected by\npassword, AUTH0\nor lock bits");
"Wrong password or\ncard protected by\nlock bits");

widget_add_button_element(
widget,
Expand Down
19 changes: 8 additions & 11 deletions lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c
Original file line number Diff line number Diff line change
Expand Up @@ -375,8 +375,7 @@ static NfcCommand mf_ultralight_poller_handler_read_tearing_flags(MfUltralightPo
NfcCommand command = NfcCommandContinue;

if(mf_ultralight_support_feature(
instance->feature_set,
MfUltralightFeatureSupportCheckTearingFlag | MfUltralightFeatureSupportSingleCounter)) {
instance->feature_set, MfUltralightFeatureSupportCheckTearingFlag)) {
if(instance->tearing_flag_read == instance->tearing_flag_total) {
instance->state = MfUltralightPollerStateTryDefaultPass;
command = NfcCommandReset;
Expand Down Expand Up @@ -437,7 +436,11 @@ static NfcCommand mf_ultralight_poller_handler_auth(MfUltralightPoller* instance
}
}
}
instance->state = MfUltralightPollerStateReadPages;
if(instance->mode == MfUltralightPollerModeRead) {
instance->state = MfUltralightPollerStateReadPages;
} else {
instance->state = MfUltralightPollerStateRequestWriteData;
Comment on lines +439 to +442

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Populate target pages before write-mode lock checks

After the auth handler now branches directly to MfUltralightPollerStateRequestWriteData in write mode (mf_ultralight_poller.c:438-441), the poller skips ReadPages but mf_ultralight_poller_handler_request_write_data still inspects tag_data->page[2] and the dynamic lock page to decide whether the card is locked. Those page buffers are never populated in write mode anymore, so the lock-bit check runs on uninitialized heap data and can randomly mark a card locked or unlocked, causing writes to fail spuriously or to ignore actual lock bits depending on memory contents. Please keep the read step (or explicitly populate the pages) before performing those checks.

Useful? React with 👍 / 👎.

}

return command;
}
Expand Down Expand Up @@ -678,14 +681,8 @@ static NfcCommand mf_ultralight_poller_handler_read_success(MfUltralightPoller*
FURI_LOG_D(TAG, "Read success");
instance->mfu_event.type = MfUltralightPollerEventTypeReadSuccess;
NfcCommand command = instance->callback(instance->general_event, instance->context);

if(instance->mode == MfUltralightPollerModeRead) {
iso14443_3a_poller_halt(instance->iso14443_3a_poller);
instance->state = MfUltralightPollerStateIdle;
} else {
instance->state = MfUltralightPollerStateRequestWriteData;
}

iso14443_3a_poller_halt(instance->iso14443_3a_poller);
instance->state = MfUltralightPollerStateIdle;
return command;
}

Expand Down
5 changes: 5 additions & 0 deletions lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,11 @@ MfUltralightError mf_ultralight_poller_write_page(
break;
}
if(!bit_buffer_starts_with_byte(instance->rx_buffer, MF_ULTRALIGHT_CMD_ACK)) {
FURI_LOG_D(
TAG,
"Write page %u NAK'd with %u",
page,
bit_buffer_get_byte(instance->rx_buffer, 0));
ret = MfUltralightErrorProtocol;
break;
}
Expand Down