mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-01-10 18:24:27 +08:00
Merge branch 'master' of github.com:RfidResearchGroup/proxmark3
This commit is contained in:
commit
c909a9c576
1 changed files with 424 additions and 0 deletions
424
client/luascripts/ntag_bruteforce.lua
Normal file
424
client/luascripts/ntag_bruteforce.lua
Normal file
|
@ -0,0 +1,424 @@
|
|||
#!/usr/bin/env lua
|
||||
os.execute("clear")
|
||||
core.console('clear')
|
||||
local DEBUG = true
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- PRE REQ:
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- 1. Install lua rocks: apt install luarocks
|
||||
-- 2. Then install lua md5 module: luarocks install md5
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- USAGE:
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- Run me like this (connected via USB): ./pm3 -l ntag_bruteforce.lua
|
||||
-- Run me like this (connected via Blueshark addon): ./client/proxmark3 /dev/rfcomm0 -l ./ntag_bruteforce.lua
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- VER | AUTHOR | DATE | CHANGE
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- 1.0 | Keld Norman, | 30 okt. 2021 | Initial version
|
||||
-- 1.1 | Keld Norman, | 30 okt. 2021 | Added: Press enter to stop the script
|
||||
-- 1.2 | Keld Norman, | 15 Nov. 2021 | Added: Check for correct hex values for bruteforcing
|
||||
-- 1.3 | Keld Norman, | 15 Nov. 2021 | Added: Added a skip ping option
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- TODO:
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- Output file not implemented yet
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- SPEEDTEST
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- BRUTEFORCE ALL HEX COMBINATIONS:
|
||||
--
|
||||
-- With the -t 10 ( lowest possible delay ) and FFFFFFFF attempts or in decimal 4.294.967.295 combinations
|
||||
--
|
||||
-- My test showed that this script can do 255 password attempts in approxemately 170 seconds
|
||||
--
|
||||
-- That is : 255 / 170 = 1,5 attempt/second
|
||||
--
|
||||
-- So .. 4.294.967.295 combinations / 1,5 per second = 2.863.311.530 seconds and it is roughly 90 years
|
||||
--
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- PASSWORD LISTS:
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- Crunch can generate all (14.776.336) combinations of 4 chars with a-z + A-Z + 0-9 like this:
|
||||
--
|
||||
-- crunch 4 4 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" -o keys/4_chars_and_digits.list
|
||||
--
|
||||
-- for LINE in $(cat keys/4_chars_and_digits.list) ; do echo -n ${LINE} |xxd -p -u;done > keys/4_chars_and_digits_hex.list
|
||||
--
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- Required includes
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
local getopt = require('getopt')
|
||||
local ansicolors = require('ansicolors')
|
||||
local md5 = require('md5')
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- Variables
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
local command = ''
|
||||
local timeout = 10 -- do not set this below 10 (if so it will not test the password)
|
||||
local option, argument
|
||||
local use_ping = true
|
||||
local bruteforce = true
|
||||
local ntagformat = false
|
||||
local input_file_valid = false
|
||||
local password_is_ascii = true
|
||||
local pass_text = "Passwords in file is treated as: ASCII"
|
||||
local bruteforce_status_file = 'ntag_status.txt'
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
copyright = ''
|
||||
script = 'Script : ntag_bruteforce.lua'
|
||||
author = 'Author : Keld Norman'
|
||||
version = 'Version : 1.0.0'
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
desc = [[Description : Bruteforces 7 byte UID NTAG protected with a 32 bit password
|
||||
.-.
|
||||
/ \ .-.
|
||||
/ \ / \ .-. .-. _ _
|
||||
+--/-------\-----/-----\-----/---\---/---\---/-\-/-\/\/---
|
||||
/ \ / \ / '-' '-'
|
||||
/ '-' '-'
|
||||
]]
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
example = [[
|
||||
Example of how to run the script with bruteforcing of continuously HEX numbers with 1 secound delay between tests:
|
||||
script run ntag_bruteforce -s 00000000 -e FFFFFFFF -t 1000 -o /var/log/ntag_bruteforce.log
|
||||
Example of how to run the script and bruteforc the card using passwords from the input file with 1s delay between tests
|
||||
script run ntag_bruteforce -i /home/my_4_char_passwords_list.txt -o /var/log/ntag_bruteforce.log ]]
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
usage = [[
|
||||
script run ntag_bruteforce [-s <start_id>] [-e <end_id>] [-t <timeout>] [ -o <output_file> ] [ -p ] [ -h for help ]
|
||||
script run ntag_bruteforce [-i <input_file>] [-t <timeout>] [ -o <output_file> ] [ -n | -x ] [ -p ] [ -h for help ]
|
||||
DESCRIPTION
|
||||
This script will test either an 8 digit hexadecimal code or 4 char stings (will be converted to an 8 digit hex string )
|
||||
against NFC cards of the type NTAG21x protected by a 32 bit password.
|
||||
Read more about NTAGs here: https://www.nxp.com/docs/en/data-sheet/NTAG213_215_216.pdf
|
||||
]]
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
arguments = [[
|
||||
-h This help
|
||||
-i input_file Read 4 char ASCII values to test from this file (will override -s and -e )
|
||||
-o output_file Write output to this file
|
||||
-t 0-99999, pause Timeout (ms) between cards 1000 = 1 second (use the word 'pause' to wait for user input)
|
||||
-p Skip Ping
|
||||
# Either use the continuously test:
|
||||
-s 0-0xFFFFFFFF Start HEX value
|
||||
-e 0-0xFFFFFFFF End HEX value
|
||||
# Or use a list of passwords from a file:
|
||||
-x Passwords in HEX Password file (-i) contains HEX values (4 x 2hex -> 32 bit/line like: 00112233)
|
||||
-n NTAG Tools format Bruteforce with first 8 hex values of a md5 hash of the password
|
||||
The password will be prefixed with hex value 20 (space) if the string/password is < 4 chars
|
||||
]]
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- FUNCTIONS
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- Check availability of file
|
||||
local function file_check(file_name)
|
||||
local exists = io.open(file_name, "r")
|
||||
if not exists then
|
||||
exists = false
|
||||
else
|
||||
exists = true
|
||||
end
|
||||
return exists
|
||||
end
|
||||
-- read lines from a file
|
||||
function read_lines_from(file)
|
||||
print(ansicolors.yellow..'\nPlease wait while loading password file..'..ansicolors.reset)
|
||||
readlines = {}
|
||||
for line in io.lines(file) do
|
||||
readlines[#readlines + 1] = line
|
||||
end
|
||||
print(ansicolors.yellow..'\nLoading password file finished'..ansicolors.reset)
|
||||
return readlines
|
||||
end
|
||||
-- write to file
|
||||
function writeOutputBytes(bytes, outfile)
|
||||
local fileout,err = io.open(outfile,"wb")
|
||||
if err then
|
||||
print("### ERROR - Faild to open output-file "..outfile)
|
||||
return false
|
||||
end
|
||||
for i = 1, #bytes do
|
||||
fileout:write(string.char(tonumber(bytes[i], 16)))
|
||||
end
|
||||
fileout:close()
|
||||
print("\nwrote "..#bytes.." bytes to "..outfile)
|
||||
return true
|
||||
end
|
||||
-- find number of entrys in a table
|
||||
function tablelength(table)
|
||||
local count = 0
|
||||
for _ in pairs(table) do count = count + 1 end
|
||||
return count
|
||||
end
|
||||
-- debug print function
|
||||
local function dbg(args)
|
||||
if not DEBUG then return end
|
||||
if type(args) == 'table' then
|
||||
local i = 1
|
||||
while result[i] do
|
||||
dbg(result[i])
|
||||
i = i+1
|
||||
end
|
||||
else
|
||||
print('###', args)
|
||||
end
|
||||
end
|
||||
-- when errors occur
|
||||
local function oops(err)
|
||||
print(ansicolors.red..'\n### ERROR - '..err..ansicolors.reset)
|
||||
core.clearCommandBuffer()
|
||||
return nil, err
|
||||
end
|
||||
-- Usage help
|
||||
local function help()
|
||||
print(copyright)
|
||||
print(script)
|
||||
print(author)
|
||||
print(version)
|
||||
print(desc)
|
||||
print(ansicolors.cyan..'Usage'..ansicolors.reset)
|
||||
print(usage)
|
||||
print(ansicolors.cyan..'Arguments'..ansicolors.reset)
|
||||
print(arguments)
|
||||
print(ansicolors.cyan..'Example usage'..ansicolors.reset)
|
||||
print(example)
|
||||
end
|
||||
--- Print user message
|
||||
local function msg(msg)
|
||||
print( string.rep('--',20) )
|
||||
print('')
|
||||
print(msg)
|
||||
print('')
|
||||
print( string.rep('--',20) )
|
||||
end
|
||||
-- Convert a string in to a hex string
|
||||
function convert_string_to_hex(str)
|
||||
return (
|
||||
str:gsub('.', function (c)
|
||||
return string.format('%02X', string.byte(c))
|
||||
end
|
||||
)
|
||||
)
|
||||
end
|
||||
-- Check if string is 4 chars ascii (32 bit) = 8 chars hex
|
||||
function check_if_string_is_hex(value)
|
||||
local patt = "%x%x%x%x%x%x%x%x"
|
||||
if string.find(value, patt) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
-- Check if number is a 0x???????? numbe (32 bit hex value)
|
||||
function check_if_number_is_hex(value)
|
||||
local num = tonumber(value) -- would return nil...
|
||||
if not num then
|
||||
return false
|
||||
end
|
||||
if (num < 0x00000000 or num > 0xffffffff) then -- ...and fail here
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- MAIN FUNCTION
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
local function main(args)
|
||||
local i = 0
|
||||
local bytes = {}
|
||||
local start_id = 0x00000000
|
||||
local end_id = 0xFFFFFFFF
|
||||
local infile, outfile
|
||||
-- stop if no args is given
|
||||
if #args == 0 then
|
||||
print(ansicolors.red..'\n### ERROR - Missing parameters'..ansicolors.reset)
|
||||
help()
|
||||
return
|
||||
end
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- Get arguments
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
for option, argument in getopt.getopt(args, ':s:e:t:i:o:pnxh') do
|
||||
-- error in options
|
||||
if optind == '?' then
|
||||
return print('Unrecognized option', args[optind -1])
|
||||
end
|
||||
-- no options
|
||||
if option == '' then
|
||||
return help()
|
||||
end
|
||||
-- start hex value
|
||||
if option == 's' then
|
||||
start_id = argument
|
||||
end
|
||||
-- end hex value
|
||||
if option == 'e' then
|
||||
end_id = argument
|
||||
end
|
||||
-- timeout
|
||||
if option == 't' then
|
||||
timeout = argument
|
||||
end
|
||||
-- input file
|
||||
if option == 'i' then
|
||||
infile = argument
|
||||
if (file_check(infile) == false) then
|
||||
return oops('Input file: '..infile..' not found')
|
||||
else
|
||||
input_file_valid = true
|
||||
end
|
||||
bruteforce = false
|
||||
end
|
||||
-- skip ping
|
||||
if option == 'p' then
|
||||
use_ping = false
|
||||
end
|
||||
-- passwordlist is hex values
|
||||
if option == 'x' then
|
||||
password_is_ascii = false
|
||||
pass_text = "Passwords in file is treated as: HEX"
|
||||
bruteforce = false
|
||||
end
|
||||
-- output file
|
||||
if option == 'o' then
|
||||
outfile = argument
|
||||
if (file_check(argument)) then
|
||||
local answer = utils.confirm('\nThe output-file '..argument..' already exists!\nthis will delete the previous content!\ncontinue?')
|
||||
if (answer == false) then
|
||||
return oops('Quiting')
|
||||
end
|
||||
end
|
||||
end
|
||||
-- bruteforce NTAG Tools encryption
|
||||
if option == 'n' then
|
||||
ntagformat = true
|
||||
bruteforce = false
|
||||
end
|
||||
-- help
|
||||
if option == 'h' then
|
||||
return help()
|
||||
end
|
||||
end
|
||||
-- min timeout is set to 1 sec if it is empty
|
||||
timeout = tonumber(timeout)
|
||||
if timeout < 10 then
|
||||
timeout = 10
|
||||
end
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- BRUTEFORCE
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
-- select bruteforce method
|
||||
if bruteforce then
|
||||
if not check_if_number_is_hex(start_id) then
|
||||
print(ansicolors.red..'\n### ERROR - start_id value '..start_id..' is out of the range of a 32-bit integer (0 to 0xFFFFFFFF) - Did you forget to add 0x ?'..ansicolors.reset)
|
||||
return
|
||||
end
|
||||
if not check_if_number_is_hex(end_id) then
|
||||
print(ansicolors.red..'\n### ERROR - end_id value '..end_id..' is out of the range of a 32-bit integer (0 to 0xFFFFFFFF) - Did you forget to add 0x ?'..ansicolors.reset)
|
||||
return
|
||||
end
|
||||
-----------------------------------------------------
|
||||
-- START BRUTEFORCE WITH CONTINUOUSLY HEX NUMBERS --
|
||||
-----------------------------------------------------
|
||||
command = 'hf mfu i -k %08X'
|
||||
msg('Bruteforcing NTAG Passwords\n\nStart value: '..start_id..'\nStop value : '..end_id..'\nDelay between tests: '..timeout..' ms')
|
||||
for hexvalue = start_id, end_id do
|
||||
if core.kbd_enter_pressed() then -- abort if key is pressed
|
||||
print("aborted by user")
|
||||
break
|
||||
end
|
||||
local cmd = string.format( command, hexvalue )
|
||||
core.console(cmd)
|
||||
print('[=] Tested password '..ansicolors.yellow..ansicolors.bright..string.format("%08X",hexvalue)..ansicolors.reset)
|
||||
print('[=] Ran command: "'..cmd..'"')
|
||||
--core.console('msleep -t'..timeout);
|
||||
if use_ping then
|
||||
print('[=] -------------------------------------------------------------')
|
||||
core.console('hw ping')
|
||||
end
|
||||
print('[=] -------------------------------------------------------------')
|
||||
end
|
||||
-----------------------------------------------------
|
||||
-- END BRUTEFORCE WITH CONTINUOUSLY HEX NUMBERS --
|
||||
-----------------------------------------------------
|
||||
else
|
||||
if not input_file_valid then
|
||||
return oops('Can not bruteforce without a password list file ( -i password_list_file.txt ) ')
|
||||
end
|
||||
-----------------------------------------------------
|
||||
-- START BRUTEFORCE WITH PASSWORDS FROM A FILE --
|
||||
-----------------------------------------------------
|
||||
local password
|
||||
local counter = 1
|
||||
local skip_to_next = 0
|
||||
local passwords_left_to_try
|
||||
local lines = read_lines_from(infile)
|
||||
local count_lines = tablelength(lines)
|
||||
msg('Bruteforcing NTAG Passwords\n\nUsing passwords from file: '..infile..'\nTesting '..count_lines..' passwords\nDelay between tests: '..timeout..' ms\n\n'..pass_text)
|
||||
while lines[counter] do
|
||||
if core.kbd_enter_pressed() then -- abort if key is pressed
|
||||
print("aborted by user")
|
||||
break
|
||||
end
|
||||
password = lines[counter]
|
||||
if ntagformat then -- "NFC Tools" uses md5 of the password and caps it to 8 chars
|
||||
local md5message = md5.sumhexa(password)
|
||||
-- print ('[=] Password is: "'..password..'" md5: '..md5message)
|
||||
password = string.sub(md5message,1,8)
|
||||
else
|
||||
if password_is_ascii then
|
||||
------------
|
||||
-- ASCII
|
||||
------------
|
||||
if string.len(password) > 4 then
|
||||
print('[!] Skipping password to long: '..password)
|
||||
skip_to_next = 1
|
||||
else
|
||||
password = convert_string_to_hex(password)
|
||||
end
|
||||
else
|
||||
------------
|
||||
-- HEX
|
||||
------------
|
||||
if string.len(password) ~= 8 then
|
||||
print('[!] WARNING - Skipping password not 8 chars (32 bit HEX): '..password)
|
||||
skip_to_next = 1
|
||||
else
|
||||
if not check_if_string_is_hex(password) then
|
||||
print('[!] WARNING - Skipping password not a valid hex string: '..password)
|
||||
skip_to_next = 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if skip_to_next == 0 then
|
||||
command = 'hf mfu i -k %4s'
|
||||
local cmd = string.format( command, password )
|
||||
core.console(cmd)
|
||||
if lines[counter] ~= password then -- show hex value (if not the password was a hex value already)
|
||||
print('[=] Tested password: "'..ansicolors.yellow..ansicolors.bright..lines[counter]..ansicolors.reset..'" (Hex: '..password..')')
|
||||
else
|
||||
print('[=] Tested password: "'..ansicolors.yellow..ansicolors.bright..lines[counter]..ansicolors.reset..'"')
|
||||
end
|
||||
passwords_left_to_try = count_lines - counter
|
||||
print('[+] Passwords left to try: '..ansicolors.green..ansicolors.bright..passwords_left_to_try..ansicolors.reset..' of '..ansicolors.green..ansicolors.bright..count_lines..ansicolors.reset)
|
||||
print('[=] Ran command: "'..cmd..'"')
|
||||
core.console('msleep -t'..timeout);
|
||||
if use_ping then
|
||||
print('[=] -------------------------------------------------------------')
|
||||
core.console('hw ping')
|
||||
end
|
||||
print('[=] -------------------------------------------------------------')
|
||||
end
|
||||
counter = counter+1
|
||||
skip_to_next = 0
|
||||
end
|
||||
-----------------------------------------------------
|
||||
-- END BRUTEFORCE WITH PASSWORDS FROM A FILE --
|
||||
-----------------------------------------------------
|
||||
end
|
||||
end
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
main(args)
|
||||
-------------------------------------------------------------------------------------------------------------
|
Loading…
Reference in a new issue