From a2118778539556e86b4c89a68b0013473ba4a3d2 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Tue, 4 Jul 2017 20:23:47 +0200 Subject: [PATCH] CHG: 'script run didump' - got some remake. unfinished. CHG: some default keys --- client/cmdhftopaz.c | 6 +- client/default_keys.dic | 101 ++++++++++++ client/loclass/fileutils.c | 4 +- client/lualibs/mf_default_keys.lua | 58 +++++++ client/mifarehost.c | 6 +- client/scripts/didump.lua | 247 +++++++++++++++++++++-------- 6 files changed, 347 insertions(+), 75 deletions(-) diff --git a/client/cmdhftopaz.c b/client/cmdhftopaz.c index 0ae721086..0985340b1 100644 --- a/client/cmdhftopaz.c +++ b/client/cmdhftopaz.c @@ -565,16 +565,12 @@ static command_t CommandTable[] = }; int CmdHFTopaz(const char *Cmd) { - // flush clearCommandBuffer(); - - // parse CmdsParse(CommandTable, Cmd); return 0; } -static int CmdHelp(const char *Cmd) -{ +static int CmdHelp(const char *Cmd) { CmdsHelp(CommandTable); return 0; } diff --git a/client/default_keys.dic b/client/default_keys.dic index 3ed597a4c..5c646c178 100644 --- a/client/default_keys.dic +++ b/client/default_keys.dic @@ -4,6 +4,7 @@ ffffffffffff,//Defaultkey(firstkeyusedbyprogramifnouserdefinedkey) 000000000000,//Blankkey a0a1a2a3a4a5,//NFCForumMADkey b0b1b2b3b4b5, +c0c1c2c3c4c5, aabbccddeeff, 4d3a99c351dd, 1a982c7e459a, @@ -351,3 +352,103 @@ a6cac2886412, aa0720018738, e64a986a5d94, bf1f4424af76, + +# +# Intratone Cogelec +# Data from http://bouzdeck.com/rfid/32-cloning-a-mifare-classic-1k-tag.html +484558414354, +a22ae129c013, +49fae4e3849f, +38fcf33072e0, +8ad5517b4b18, +509359f131b1, +6c78928e1317, +aa0720018738, +a6cac2886412, +62d0c424ed8e, +e64a986a5d94, +8fa1d601d0a2, +89347350bd36, +66d2b7dc39ef, +6bc1e1ae547d, +22729a9bd40f, +# +# Data from https://dfir.lu/blog/cloning-a-mifare-classic-1k-tag.html +925b158f796f, +fad63ecb5891, +bba840ba1c57, +cc6b3b3cd263, +6245e47352e6, +6c78928e1317, +8ed41e8b8056, +2dd39a54e1f3, +6d4c5b3658d2, +1877ed29435a, +52264716efde, +961c0db4a7ed, +703140fd6d86, +157c9a513fa5, +e2a5dc8e066f, +# +# Data from a oyster card +374bf468607f, +bfc8e353af63, +15cafd6159f6, +62efd80ab715, +987a7f7f1a35, +c4104fa3c526, +4c961f23e6be, +67546972bc69, +f4cd5d4c13ff, +94414c1a07dc, +16551d52fd20, +9cb290282f7d, +77a84170b574, +ed646c83a4f3, +e703589db50b, +513c85d06cde, +95093f0b2e22, +543b01b27a95, +c6d375b99972, +ee4cc572b40e, +5106ca7e4a69, +c96bd1ce607f, +167a1be102e0, +a8d0d850a606, +a2abb693ce34, +7b296c40c486, +91f93a5564c9, +e10623e7a016, +b725f9cbf183, +# +# Data from FDi tag +8829da9daf76, +# +# Data from GitHub issue +0A7932DC7E65, +11428B5BCE06, +11428B5BCE07, +11428B5BCE08, +11428B5BCE09, +11428B5BCE0A, +11428B5BCE0F, +18971D893494, +25D60050BF6E, +3FA7217EC575, +44F0B5FBE344, +7B296F353C6B, +8553263F4FF0, +8E5D33A6ED51, +9F42971E8322, +C620318EF179, +D4FE03CE5B06, +D4FE03CE5B07, +D4FE03CE5B08, +D4FE03CE5B09, +D4FE03CE5B0A, +D4FE03CE5B0F, +E241E8AFCBAF, +# +# Data from forum post +123F8888F322, +050908080008, \ No newline at end of file diff --git a/client/loclass/fileutils.c b/client/loclass/fileutils.c index fd18b9642..2ed19ac78 100644 --- a/client/loclass/fileutils.c +++ b/client/loclass/fileutils.c @@ -84,10 +84,8 @@ int saveFile(const char *preferredName, const char *suffix, const void* data, si return 1; } fwrite(data, 1, datalen, f); - if (f) { + if (f) fclose(f); - f = NULL; - } prnlog("Saved data to '%s'", fileName); free(fileName); return 0; diff --git a/client/lualibs/mf_default_keys.lua b/client/lualibs/mf_default_keys.lua index 9a496b19c..6f7eb46eb 100644 --- a/client/lualibs/mf_default_keys.lua +++ b/client/lualibs/mf_default_keys.lua @@ -7,6 +7,7 @@ local _keys = { '000000000000', -- Blank key 'a0a1a2a3a4a5', -- NFCForum MAD key 'b0b1b2b3b4b5', + 'c0c1c2c3c4c5', 'aabbccddeeff', '4d3a99c351dd', '1a982c7e459a', @@ -454,6 +455,63 @@ local _keys = { --[[ --]] 'a56c2df9a26d', + --[[ + Intratone Cogelec + Data from http://bouzdeck.com/rfid/32-cloning-a-mifare-classic-1k-tag.html + --]] + '484558414354', + 'a22ae129c013', + '49fae4e3849f', + '38fcf33072e0', + '8ad5517b4b18', + '509359f131b1', + '6c78928e1317', + 'aa0720018738', + 'a6cac2886412', + '62d0c424ed8e', + 'e64a986a5d94', + '8fa1d601d0a2', + '89347350bd36', + '66d2b7dc39ef', + '6bc1e1ae547d', + '22729a9bd40f', +--[[ + Data from a oyster card +--]] + '374bf468607f', + 'bfc8e353af63', + '15cafd6159f6', + '62efd80ab715', + '987a7f7f1a35', + 'c4104fa3c526', + '4c961f23e6be', + '67546972bc69', + 'f4cd5d4c13ff', + '94414c1a07dc', + '16551d52fd20', + '9cb290282f7d', + '77a84170b574', + 'ed646c83a4f3', + 'e703589db50b', + '513c85d06cde', + '95093f0b2e22', + '543b01b27a95', + 'c6d375b99972', + 'ee4cc572b40e', + '5106ca7e4a69', + 'c96bd1ce607f', + '167a1be102e0', + 'a8d0d850a606', + 'a2abb693ce34', + '7b296c40c486', + '91f93a5564c9', + 'e10623e7a016', + 'b725f9cbf183', +--[[ + Data from forum post +--]] + "123F8888F322", + "050908080008", } --- diff --git a/client/mifarehost.c b/client/mifarehost.c index cde91ae7f..bfc86604c 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -685,11 +685,11 @@ bool detect_classic_prng(){ uint8_t cmd[] = {MIFARE_AUTH_KEYA, 0x00}; uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC; - UsbCommand cAuth = {CMD_READER_ISO_14443a, {flags, sizeof(cmd), 0}}; - memcpy(cAuth.d.asBytes, cmd, sizeof(cmd)); + UsbCommand c = {CMD_READER_ISO_14443a, {flags, sizeof(cmd), 0}}; + memcpy(c.d.asBytes, cmd, sizeof(cmd)); clearCommandBuffer(); - SendCommand(&cAuth); + SendCommand(&c); WaitForResponse(CMD_ACK, &resp); WaitForResponse(CMD_ACK, &respA); diff --git a/client/scripts/didump.lua b/client/scripts/didump.lua index cb458071d..6e4a10fa4 100644 --- a/client/scripts/didump.lua +++ b/client/scripts/didump.lua @@ -1,19 +1,25 @@ +--- +-- requirements local cmds = require('commands') local getopt = require('getopt') local utils = require('utils') local lib14a = require('read14a') +local json = require('dkjson') +local toys = require('default_toys_di') example =[[ script run didump script run didump -t + script run didump -r ]] author = "Iceman" usage = "script run didump -h -t" desc = [[ This is a script to dump and decrypt the data of a specific type of Mifare Mini token. - +The dump is decrypted. If a raw dump is wanted, use the -r parameter Arguments: -h : this help + -r : raw -t : selftest ]] @@ -21,40 +27,57 @@ local band=bit32.band local bor=bit32.bor local bnot=bit32.bnot local bxor=bit32.bxor -local lshift=bit32.lshift -local rshift=bit32.rshift +local lsh=bit32.lshift +local rsh=bit32.rshift local FOO = 'AF62D2EC0491968CC52A1A7165F865FE' local BAR = '286329204469736E65792032303133' local MIS = '0A14FD0507FF4BCD026BA83F0A3B89A9' -local RANDOM = FOO..BAR local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M%S"); local TIMEOUT = 2000 -local DEBUG = false +local DEBUG = true local numBlocks = 20 local numSectors = 5 local CHECKSUM_OFFSET = 12; -- +1??? + --- -- A debug printout-function -function dbg(args) - if DEBUG then +local function dbg(args) + if not DEBUG then return end + if type(args) == "table" then + local i = 1 + while args[i] do + print("###", args[i]) + i = i+1 + end + else print("###", args) end end --- -- This is only meant to be used when errors occur -function oops(err) +local function oops(err) print("ERROR: ",err) core.clearCommandBuffer() + return false end --- -- Usage help -function help() +local function help() print(desc) print("Example usage") print(example) end --- +-- +local function print_info(tagdata) + --got table with data. + local h = tagdata[2]:sub(1,8) + local t = tostring( tonumber( h, 16 ) ) + local item = toys.Find(t) + print( ("Modelid : %s , %s, v.%s.0"):format(t, item[3], item[2])) +end +--- -- Get checksum, -- called: data is string (32 hex digits) -- returns: number @@ -333,7 +356,7 @@ _tbl[255] = { 0x2D02EF8D } for i,item in pairs(data) do local tmp = band(ret, 0xFF) local index = band( bxor(tmp, item), 0xFF) - ret = bxor(rshift(ret,8), _tbl[index][1]) + ret = bxor(rsh(ret,8), _tbl[index][1]) end return ret end @@ -375,10 +398,111 @@ local function keygen(uid) ) end +--- encode 'table' into a json formatted string +-- +local function convert_to_json( obj ) + if type(obj) == "table" then + return json.encode (obj, { indent = true }) + end + return oops('[fail] input object must be a lua-TABLE') +end +-- +-- Save +local function save_json(filename, data) + jsondata = convert_to_json(data) + filename = filename or 'dumpdata.json' + local f = io.open(filename, "w") + if not f then return oops(string.format("Could not write to file %s", tostring(filename))) end + f:write(jsondata) + io.close(f) + return filename +end +--- loads a json formatted text file with +-- +-- @param filename the file containing the json-dump (defaults to dumpdata.json) +local function load_json(filename) + filename = filename or 'dumpdata.json' + local f = io.open(filename, "rb") + if not f then return oops(string.format("Could not read file %s", tostring(filename))) end + + -- Read file + local t = f:read("*all") + io.close(f) + + local obj, pos, err = json.decode(t, 1, nil) + if err then return oops(string.format("importing json file failed. %s", err)) end + + dbg(string.format('loaded file %s', input)) + return obj + +-- local len, hex = bin.unpack( ("H%d"):format(#t), t) +end +--- +-- Generate encryption key +local function create_key(uid) + local key = '' + local sha = utils.Sha1Hex( FOO..BAR..uid ) + sha = utils.ConvertBytesToHex( utils.ConvertAsciiToBytes(sha:sub(1,16)) ) + key = utils.SwapEndiannessStr( sha:sub(1,8) , 32 ) + key = key..utils.SwapEndiannessStr( sha:sub(9,16), 32 ) + key = key..utils.SwapEndiannessStr( sha:sub(17,24), 32 ) + key = key..utils.SwapEndiannessStr( sha:sub(25,32), 32 ) + return key +end +--- reads all blocks from tag +-- +local function readtag(mfkey, aeskey ) + + local tagdata = {} + + for blockNo = 0, numBlocks-1 do + + if core.ukbhit() then + print("[fail] aborted by user") + return nil + end + + -- read block from tag. + cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = blockNo ,arg2 = 0,arg3 = 0, data = mfkey} + local err = core.SendCommand(cmd:getBytes()) + if err then return oops(err) end + local blockdata, err = waitCmd() + if err then return oops(err) end + + -- rules: + -- the following blocks is NOT encrypted + -- block 0 (manufacturing) and 18 + -- block with all zeros + -- sector trailor + if blockNo == 0 or blockNo == 18 then + + elseif blockNo%4 ~= 3 then + + if not string.find(blockdata, '^0+$') then + if aeskey then + local decrypted, err = core.aes128_decrypt_ecb(aeskey, blockdata) + if err then dbg(err) end + blockdata = utils.ConvertAsciiToHex(decrypted) + end + end + else + -- Sectorblocks, not encrypted, but we add our known key to it since it is normally zeros. + blockdata = mfkey..blockdata:sub(13,20)..mfkey + --dbg(blockdata:sub(13,20)) + end + table.insert(tagdata, blockdata) + end + return tagdata +end +--- +-- simple selftest of functionality local function selftest() local testdata = '000F42430D0A14000001D11F'..'5D738517' local chksum = getChecksum(testdata) local calc = calculateChecksum( utils.ConvertHexToBytes(testdata:sub(1,24))) + print ( testdata:sub(1,24) ) + print ( ('%x - %x'):format(chksum, calc)) + local isValid = false local validStr = "FAIL" if calc == chksum then @@ -399,6 +523,15 @@ local function selftest() print ('TEST KEY :: '..testkey) print ('VALID KEY :: 29564af75805') end +local function setdevicedebug( status ) + local c = 'hf mf dbg ' + if status then + c = c..'1' + else + c = c..'0' + end + core.console(c) +end --- -- The main entry point -- -d decrypt @@ -406,74 +539,60 @@ end -- -v validate function main(args) - local cmd, result, err, blockNo, keyA + local cmd, tag, err, blockNo, mfkey + local shall_validate = false + local shall_dec = false + local shall_enc = false local blocks = {} - local magic = '' + local aeskey = '' + local input = '' -- Read the parameters - for o, a in getopt.getopt(args, 'ht') do + for o, a in getopt.getopt(args, 'htdevi:') do if o == "h" then help() return end if o == "t" then return selftest() end + if o == "d" then shall_dec = true end + if o == "e" then shall_enc = true end + if o == "v" then shall_validate = true end + if o == "i" then input = load_json(a) end end -- Turn off Debug - local cmdSetDbgOff = "hf mf dbg 0" - core.console( cmdSetDbgOff) + setdevicedebug(false) -- GET TAG UID - - result, err = lib14a.read1443a(false) - if not result then - return oops(err) - end + tag, err = lib14a.read1443a(false) + if not tag then return oops(err) end core.clearCommandBuffer() - local keyA = keygen(result.uid) - - -- Show tag info - print((' Found tag %s'):format(result.name)) - - local longrandom = RANDOM..result.uid - local res = utils.Sha1Hex(longrandom) - res = utils.ConvertBytesToHex(utils.ConvertAsciiToBytes(res:sub(1,16))) - magic = utils.SwapEndiannessStr(res:sub(1,8) , 32) - magic = magic..utils.SwapEndiannessStr( res:sub(9,16),32) - magic = magic..utils.SwapEndiannessStr( res:sub(17,24),32) - magic = magic..utils.SwapEndiannessStr( res:sub(25,32),32) - print('Reading card data') - print('Raw','Decrypted') - for blockNo = 0, numBlocks-1, 1 do - - if core.ukbhit() then - print("aborted by user") - break + -- simple tag check + if 0x09 ~= tag.sak then + if 0x4400 ~= tag.atqa then + return oops(('[fail] found tag %s :: looking for Mifare Mini 0.3k'):format(tag.name)) end - - cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = blockNo ,arg2 = 0,arg3 = 0, data = keyA} - local err = core.SendCommand(cmd:getBytes()) - if err then return oops(err) end - local blockdata, err = waitCmd() - if err then return oops(err) end - - if blockNo%4 ~= 3 then - - -- blocks with zero not encrypted. - if string.find(blockdata, '^0+$') then - print(blockdata, blockdata) - else - local aes = core.aes128_decrypt_ecb(magic, blockdata) - local bytes = utils.ConvertAsciiToBytes(aes) - local hex = utils.ConvertBytesToHex(bytes) - print(blockdata , hex) end - elseif blockNo == 0 then - print(blockdata,blockdata) - else - -- Sectorblocks, not encrypted - local sectortrailer = keyA..blockdata:sub(13,20)..keyA - print(sectortrailer, sectortrailer, blockdata:sub(13,20)) - end - end + dbg ('[ok] found '..tag.name) + + -- tag key + mfkey = keygen(tag.uid) + dbg('[ok] using mf keyA : '.. mfkey) + + -- AES key + aeskey = create_key(tag.uid) + dbg('[ok] using AES key : '.. aeskey) + + -- read tag data, complete, enc/dec + tagdata = readtag(mfkey, aeskey) + dbg('[ok] read card data') + + -- show information? + print_info(tagdata) + + -- save + res = save_json(nil, tagdata) + if not res then return oops('[fail] saving json file') end + + dbg('[ok] read card data') end main(args)