Verified Commit 1bfc0a59 authored by Ave's avatar Ave 🤔

Further documentation and bugfixes wrt reading block before replacing

parent b4bf1595
[package]
name = "flashuid-rs"
version = "0.1.0"
authors = ["Ave Ozkal <ave@ave.zone>"]
authors = ["Ave Ozkal <rustfoss@ave.zone>"]
edition = "2018"
[dependencies]
......
......@@ -4,4 +4,14 @@ Tool to flash UID section on UID modifiable Mifare Classic fobs with an ACR122U.
This is a Rust port of https://github.com/general-programming/flashuid, made to be slightly cleaner, and significantly more verification to prevent flashing improper data.
Keep in mind that I'm not great with Rust, so expect some issues here and there with the code.
\ No newline at end of file
Keep in mind that I'm not great with Rust, so expect some issues here and there with the code. Only tested on Linux.
## Screenshot
![](https://elixi.re/i/2tuysepo.png)
## Shoutouts
Thanks a lot to linuxgemini for his continued help with my endless NFC hacking questions.
A bit of the code here is based on pcsc-rust's example, so huge shoutouts to the developer of that as well.
......@@ -49,7 +49,8 @@ fn send_verify_apdu(apdu: &[u8], expected: &[u8], card: &pcsc::Card) -> std::vec
}
fn verify_uid(uid: &[u8]) {
assert!(uid.len() > 6, "UID must be at least 6 bytes long (currently {} bytes long).", uid.len());
assert!(uid.len() >= 6, "UID must be at least 6 bytes long (currently {} bytes long).", uid.len());
assert!(uid.len() <= 16, "UID must be at most 16 bytes long (currently {} bytes long).", uid.len());
assert!(uid.len() < 6 || uid[5] == 0x08 || uid[5] == 0x88, "UID's 6th byte should be 0x08 or 0x88 (currently 0x{:02X?}).", uid[5]);
assert!(uid.len() < 7 || uid[6] == 0x04, "UID's 7th byte should be 0x04 or must be omitted altogether (currently 0x{:02X?}).", uid[6]);
assert!(uid.len() < 8 || uid[7] == 0x00, "UID's 8th byte should be 0x00 or must be omitted altogether (currently 0x{:02X?}).", uid[7]);
......@@ -57,6 +58,38 @@ fn verify_uid(uid: &[u8]) {
assert!(bcc == uid[4], "Incorrect BCC (at byte 5, should be 0x{:02X?}, currently 0x{:02X?}).", bcc, uid[4]);
}
fn complete_uid(uid: &[u8], card: &pcsc::Card) -> std::vec::Vec<u8> {
if uid.len() < 16 {
let mut temp_uid = uid.to_vec();
// Fetch block 0x00
// On API Docs: "5.3. Read Binary Blocks"
let mut first_block = send_apdu(b"\xFF\xB0\x00\x00\x10", &card);
// Crop to 16 bytes (drop success bytes at end, a block is 16 bytes)
first_block.truncate(16);
// Delete UID bytes that are already provided by user
first_block.drain(..temp_uid.len());
// Form block with UID provided by user and data that's already in fob, return that
temp_uid.extend(first_block);
temp_uid
}
else {
uid.to_vec()
}
}
fn auth_witchcraft(card: &pcsc::Card) {
// Commands taken from https://github.com/general-programming/flashuid/blob/master/index.js
// Thanks linuxgemini!
// I'll try to document these when I can
send_verify_apdu(b"\xFF\x00\x00\x00\x08\xD4\x08\x63\x02\x00\x63\x03\x00", b"\xD5\x09\x90\x00", &card);
send_verify_apdu(b"\xFF\x00\x00\x00\x06\xD4\x42\x50\x00\x57\xCD", b"\xD5\x43\x01\x90\x00", &card);
send_verify_apdu(b"\xFF\x00\x00\x00\x05\xD4\x08\x63\x3D\x07", b"\xD5\x09\x90\x00", &card);
send_verify_apdu(b"\xFF\x00\x00\x00\x03\xD4\x42\x40", b"\xD5\x43\x00\x0A\x90\x00", &card);
send_verify_apdu(b"\xFF\x00\x00\x00\x05\xD4\x08\x63\x3D\x00", b"\xD5\x09\x90\x00", &card);
send_verify_apdu(b"\xFF\x00\x00\x00\x03\xD4\x42\x43", b"\xD5\x43\x00\x0A\x90\x00", &card);
send_verify_apdu(b"\xFF\x00\x00\x00\x08\xD4\x08\x63\x02\x80\x63\x03\x80", b"\xD5\x09\x90\x00", &card);
}
fn main() {
// Establish a PC/SC context.
let ctx = match Context::establish(Scope::User) {
......@@ -78,6 +111,7 @@ fn main() {
};
// Use the first reader.
// TODO: Can we ensure that we pick an ACR122U, or at least not a Yubikey?
let reader = match readers.next() {
Some(reader) => reader,
None => {
......@@ -100,13 +134,13 @@ fn main() {
}
};
// Disable buzzer command
// Disable buzzer
// https://stackoverflow.com/a/41550221/3286892
// On API: "6.2. Bi-color LED and Buzzer Control"
// On API Docs: "6.2. Bi-color LED and Buzzer Control"
send_verify_apdu(b"\xFF\x00\x52\x00\x00", b"\x90\x00", &card);
// Get UID command
// On API: Note on "7.3. How to access FeliCa tags (ISO 18092)?"
// Get UID, print it for user
// On API Docs: "4.1. Get Data"
let mut out = send_apdu(b"\xFF\xCA\x00\x00\x00", &card);
out.truncate(4);
println!("Current UID: {}", hex::encode_upper(&out));
......@@ -115,28 +149,25 @@ fn main() {
match args.len() {
2 => {
// Get UID from user, verify it
let arg: String = args[1].parse().expect("Invalid input (??? how)");
let uid: Vec<u8> = Vec::from_hex(arg).expect("Input isn't a valid hex");
let mut uid: Vec<u8> = Vec::from_hex(arg).expect("Input isn't a valid hex");
verify_uid(&uid);
// Commands taken from https://github.com/general-programming/flashuid/blob/master/index.js
// Thanks linuxgemini!
// I'll try to document these when I can
send_verify_apdu(b"\xFF\x00\x00\x00\x08\xD4\x08\x63\x02\x00\x63\x03\x00", b"\xD5\x09\x90\x00", &card);
send_verify_apdu(b"\xFF\x00\x00\x00\x06\xD4\x42\x50\x00\x57\xCD", b"\xD5\x43\x01\x90\x00", &card);
send_verify_apdu(b"\xFF\x00\x00\x00\x05\xD4\x08\x63\x3D\x07", b"\xD5\x09\x90\x00", &card);
send_verify_apdu(b"\xFF\x00\x00\x00\x03\xD4\x42\x40", b"\xD5\x43\x00\x0A\x90\x00", &card);
send_verify_apdu(b"\xFF\x00\x00\x00\x05\xD4\x08\x63\x3D\x00", b"\xD5\x09\x90\x00", &card);
send_verify_apdu(b"\xFF\x00\x00\x00\x03\xD4\x42\x43", b"\xD5\x43\x00\x0A\x90\x00", &card);
send_verify_apdu(b"\xFF\x00\x00\x00\x08\xD4\x08\x63\x02\x80\x63\x03\x80", b"\xD5\x09\x90\x00", &card);
// Run weird commands and bypass auth. Our best guess with linuxgemini is that this is a backdoor.
auth_witchcraft(&card);
let cmd = b"\xFF\x00\x00\x00\x15\xD4\x40\x01\xA0\x00";
// Complete UID by user to a whole block
uid = complete_uid(&uid, &card).to_vec();
let mut full_cmd = cmd.to_vec();
// Form the command to replace block 0x00 (and therefore, UID)
let uid_change_cmd = b"\xFF\x00\x00\x00\x15\xD4\x40\x01\xA0\x00";
let mut full_cmd = uid_change_cmd.to_vec();
full_cmd.extend(&uid);
// Replace UID
send_verify_apdu(&full_cmd, b"\xD5\x41\x00\x90\x00", &card);
println!("Successfully replaced UID with {}. You may need to remove the card from reader and place it again for it to be detected.", hex::encode_upper(&uid));
println!("Successfully replaced UID with {}. You may need to remove the fob from reader and place it again for it to be detected.", hex::encode_upper(&uid));
},
_ => {
println!("No UID specified, therefore no UID was set.");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment