| 1 | local cmds = require('commands') |
| 2 | local getopt = require('getopt') |
| 3 | local utils = require('utils') |
| 4 | local lib14a = require('read14a') |
| 5 | |
| 6 | example = "script iterates over all possible sectors for a tag and runs hardnested attack against them to collect the keys." |
| 7 | author = "Iceman" |
| 8 | desc = |
| 9 | [[ |
| 10 | This script iterates over all possible sectors for a tag and runs hardnested attack against them to collect the keys. |
| 11 | |
| 12 | Arguments: |
| 13 | -k Known key, 6 bytes (12 hex digits) |
| 14 | Examples : |
| 15 | script hard -b 112233445566 |
| 16 | ]] |
| 17 | |
| 18 | local numBlocks = 64 |
| 19 | local numSectors = 16 |
| 20 | local DEBUG = TRUE |
| 21 | --- |
| 22 | -- A debug printout-function |
| 23 | function dbg(args) |
| 24 | if not DEBUG then return end |
| 25 | |
| 26 | if type(args) == "table" then |
| 27 | local i = 1 |
| 28 | while result[i] do |
| 29 | dbg(result[i]) |
| 30 | i = i+1 |
| 31 | end |
| 32 | else |
| 33 | print("###", args) |
| 34 | end |
| 35 | end |
| 36 | --- |
| 37 | -- This is only meant to be used when errors occur |
| 38 | function oops(err) |
| 39 | print("ERROR: ",err) |
| 40 | return nil,err |
| 41 | end |
| 42 | --- |
| 43 | -- Usage help |
| 44 | function help() |
| 45 | print(desc) |
| 46 | print("Example usage") |
| 47 | print(example) |
| 48 | end |
| 49 | -- |
| 50 | -- Exit message |
| 51 | function ExitMsg(msg) |
| 52 | print( string.rep('--',20) ) |
| 53 | print( string.rep('--',20) ) |
| 54 | print(msg) |
| 55 | print() |
| 56 | end |
| 57 | -- A little helper to place an item first in the list |
| 58 | local function placeFirst(akey, list) |
| 59 | akey = akey:lower() |
| 60 | if list[1] == akey then |
| 61 | -- Already at pole position |
| 62 | return list |
| 63 | end |
| 64 | local result = {akey} |
| 65 | --print(("Putting '%s' first"):format(akey)) |
| 66 | for i,v in ipairs(list) do |
| 67 | if v ~= akey then |
| 68 | result[#result+1] = v |
| 69 | end |
| 70 | end |
| 71 | return result |
| 72 | end |
| 73 | -- A function to display the results |
| 74 | -- TODO: iceman 2016, still screws up output when a key is not found. |
| 75 | local function displayresults(results) |
| 76 | local sector, blockNo, keyA, keyB, succA, succB, _ |
| 77 | |
| 78 | print("|---|----------------|---|----------------|---|") |
| 79 | print("|sec|key A |res|key B |res|") |
| 80 | print("|---|----------------|---|----------------|---|") |
| 81 | |
| 82 | for sector,_ in pairs(results) do |
| 83 | succA, succB, keyA, keyB = unpack(_) |
| 84 | print(("|%03d| %s | %s | %s | %s |"):format(sector, keyA, succA, keyB, succB)) |
| 85 | end |
| 86 | print("|---|----------------|---|----------------|---|") |
| 87 | |
| 88 | end |
| 89 | --- |
| 90 | -- a simple selftest function, |
| 91 | local function selftest() |
| 92 | return nil |
| 93 | end |
| 94 | |
| 95 | --- |
| 96 | -- The main entry point |
| 97 | function main(args) |
| 98 | |
| 99 | local blockno = '00' |
| 100 | local keytype = 0 --A 01==B |
| 101 | local key = 'fc00018778f7' |
| 102 | local trgkey = '' |
| 103 | local numSectors = 16 |
| 104 | |
| 105 | -- Read the parameters |
| 106 | for o, a in getopt.getopt(args, 'hk:') do |
| 107 | if o == "h" then return help() end |
| 108 | if o == "k" then key = a end |
| 109 | end |
| 110 | |
| 111 | -- Turn off Debug |
| 112 | local cmdSetDbgOff = "hf mf dbg 0" |
| 113 | core.console( cmdSetDbgOff) |
| 114 | -- identify tag |
| 115 | result, err = lib14a.read1443a(false) |
| 116 | if not result then |
| 117 | return oops(err) |
| 118 | end |
| 119 | core.clearCommandBuffer() |
| 120 | |
| 121 | -- Show tag info |
| 122 | print((' Found tag %s'):format(result.name)) |
| 123 | |
| 124 | if 0x18 == result.sak then --NXP MIFARE Classic 4k | Plus 4k |
| 125 | -- IFARE Classic 4K offers 4096 bytes split into forty sectors, |
| 126 | -- of which 32 are same size as in the 1K with eight more that are quadruple size sectors. |
| 127 | numSectors = 40 |
| 128 | elseif 0x08 == result.sak then -- NXP MIFARE CLASSIC 1k | Plus 2k |
| 129 | -- 1K offers 1024 bytes of data storage, split into 16 sector |
| 130 | numSectors = 16 |
| 131 | elseif 0x09 == result.sak then -- NXP MIFARE Mini 0.3k |
| 132 | -- MIFARE Classic mini offers 320 bytes split into five sectors. |
| 133 | numSectors = 5 |
| 134 | elseif 0x10 == result.sak then-- "NXP MIFARE Plus 2k" |
| 135 | numSectors = 32 |
| 136 | else |
| 137 | print("I don't know how many sectors there are on this type of card, defaulting to 16") |
| 138 | end |
| 139 | |
| 140 | result = {} |
| 141 | for sector=1,numSectors do |
| 142 | |
| 143 | --[[ |
| 144 | The mifare Classic 1k card has 16 sectors of 4 data blocks each. |
| 145 | The first 32 sectors of a mifare Classic 4k card consists of 4 data blocks and the remaining |
| 146 | 8 sectors consist of 16 data blocks. |
| 147 | --]] |
| 148 | local trgblockno = sector * 4 - 1 |
| 149 | if sector > 32 then |
| 150 | trgblockno = 32 * 4 + (sector-32) * 16 -1 |
| 151 | end |
| 152 | |
| 153 | trgblockno = ("%02d"):format(trgblockno) |
| 154 | |
| 155 | local succA = 1 |
| 156 | local succB = 1 |
| 157 | local errA, keyA = core.hardnested(blockno, keytype, key, trgblockno, '0', trgkey, 0,0,0,0) |
| 158 | keyA = keyA or "" |
| 159 | if errA == nil or errA > 0 then succA = 0 end |
| 160 | |
| 161 | local errB, keyB = core.hardnested(blockno, keytype, key, trgblockno, '1', trgkey, 0,0,0,0) |
| 162 | keyB = keyB or "" |
| 163 | if errB == nil or errB > 0 then succB = 0 end |
| 164 | result[sector] = { succA, succB, utils.ConvertAsciiToHex(keyA), utils.ConvertAsciiToHex(keyB) } |
| 165 | |
| 166 | -- Check if user aborted |
| 167 | if core.ukbhit() then |
| 168 | print("Aborted by user") |
| 169 | break |
| 170 | end |
| 171 | end |
| 172 | displayresults(result) |
| 173 | end |
| 174 | |
| 175 | main(args) |