UNPKG

reavetard

Version:

CoffeeScript modules that provide AP rotation support and a host of data management and general usability enhancements for Reaver-WPS Cracker and its partner in crime, Wash-WPS Scanner

369 lines (339 loc) 16.5 kB
# Reavetard - Reaver WPS (+Wash) extension scripts # cli.coffee :: Module to pimp our command line interface output # Author: Robbie Saunders http://eibbors.com/[/p/reavetard] # ============================================================================== # Module dependencies # ------------------------- charm = require('charm')(process.stdout) # Core helpers used to allow more user-friendly data structures # ---------------------------------------------------------------------- # Write an array of strings or arrays of the format # [ FG, BG, TEXT... ], [ FG, TEXT ], or [ TEXT ] arrwrite = (arr, cd=false) -> for a in arr if Array.isArray a if cd then cdwrite.apply null, a else cwrite.apply null, a else if cd then cdwrite.apply null, [a] else cwrite.apply null, [a] # Used to write and color something in one fell swoop cwrite = -> if arguments.length is 1 charm.write arguments[0] else if arguments.length >= 2 charm.foreground arguments[0] if arguments.length >= 3 charm.background arguments[1] charm.write arguments[2] else charm.write arguments[1] # Same as above, but assumes an extra 'display' argument at arguments[0] cdwrite = (disp, fg, bg, str) -> charm.display 'reset' charm.display disp args = [] if fg? then args.push fg if bg? then args.push bg if str? then args.push str cwrite.apply null, args # Constant strings / printable arrays / widgets? # ---------------------------------------------------------------------- REAVETARD_TITLE = [ ['reset', 'blue', ' eibbors.com/p/reavetard/\n'] ['dim', 'magenta', ' -------------------------------------------------------------\n'] ['bright', 57, ' - ▀█▀▀▀▄ ▄▀▀▀▄ ▀▀▀▀▄ █ █ ▄▀▀▀▄ ▄█▄▄ ▀▀▀▀▄ ▀█▀▀▀▄ ▀█ -\n'] ['bright', 56, ' - █ ▀ █▀▀▀▀ █▀▀▀█ ▀▄ ▄▀ █▀▀▀▀ █ ▄ █▀▀▀█ █ ▀ ▄▀▀▀█ -\n'] ['bright', 55, ' - ▀▀ ▀▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀ ▀▀▀ ▀ ▀▀ ▀▀▀▀▀ -\n'] ['reset', 'blue', ' - Coffee-powered enhancements for WPS PIN Cracker, Reaver -\n'] ['dim', 'magenta', ' -------------------------------------------------------------\n'] ['reset', '\n'] ] REAVETARD_TITLE_S = [ ['bright', 57, ' __ _ _ _ _ _ ___ _ __ _ \n'] ['bright', 56, ' |_| |_| _| | | |_| | _| | | _| '], ['reset', 'blue', 'CoffeeScript goodies for Reaver-WPS\n'] ['bright', 55, ' | \\ |__ |_\\ V |__ | |_\\ | |_|_ '], ['reset', 'blue', 'Courtesy of Robbie Saunders (eibbors.com)\n\n'] ] REVIEW_SELECTORS = [ ['bright', 250, '\n Special Selectors: \n'] [ 'reset', 240, ' ---------------------------------------------------------------------------------'] ['bright', 'white', '\n 0 /'] [ 'reset', 'white', ' Select all'] ['bright', 'black', ', '] ['bright', 'green', ' -1 /'] [ 'reset', 'green', ' Cracked targets'] ['bright', 'black', ', '] ['bright', 'blue', '-2 /'] [ 'reset', 'blue', ' Targets in progress'] ['bright', 'black', ', '] ['bright', 5, '-3 /'] ['reset', 5, ' New targets\n\n'] ['reset', 240, ' Note: Delimit multiple selections with any non-digit character but `-`\n'] ] # TODO: substitute the sloppy hardcoded colors with a reference to one of these (perhaps move to config file) STATION_COLORS = exports.STATION_COLORS = { C:'green', W: 'blue', N: 5 } # Menu of post-selection target actions REAVETARD_TARGET_ACTIONS = [ ['bright', 'magenta', ' (a)ttack : '], ['reset', 'magenta', 'Queue the station(s) for attack and begin cracking\n'] ['bright', 'magenta', ' (d)elete : '], ['reset', 'magenta', 'Delete their database entries and/or session files\n'] ['bright', 'magenta', ' (e)xport : '], ['reset', 'magenta', 'Export reaver.db and session file(s) to a new location\n'] ['bright', 'magenta', ' (j)son : '], ['reset', 'magenta', 'Output JSON object(s) containing all available target data\n'] ['bright', 'magenta', ' (s)essions : '], ['reset', 'magenta', 'Create and/or Customize their session files\n'] ['bright', 'magenta', ' (u)sage : '], ['reset', 'magenta', 'Print reavetard\'s best guess @ proper reaver arguments (usage)\n'] ['bright', 'magenta', ' e(x)it : '], ['reset', 'magenta', 'End process without doing anything else\n'] ] # Packet Sequence Progress Bar settins # REAVER_BAR_STYLES[sequenceDepth|key] = [bgcolor, text, <:in|>:out|<>:both ] REAVER_BAR_STYLES = 1: [54, 'white', ' St ', '>'] 2: [55, 'white', ' Id ', '>'] 3: [56, 'white', ' Id ', '<'] 4: [18, 'white', ' M1 ', '>'] 5: [19, 'white', ' M2 ', '<'] 6: [20, 'white', ' M3 ', '>'] 7: [21, 'white', ' M4 ', '<'] 8: [23, 'white', ' M5 ', '>'] 9: [31, 'white', ' M6 ', '<'] 10: [22, 'white', ' M7 ', '>'] 0: [203, 'black', ' Na ', '<>'] # Nack nack: @[0] timeout: [ 52, 'white', ' TO '] # Timeout Occured failure: ['red', 'white', ' Fail '] # General/Unknown failure fail3: ['red', 'white', ' WFx2 '] # These three represent fail2: ['red', 'white', ' WFx3 '] # the WPS Failure codes fail4: ['red', 'white', ' WFx4 '] # 0x02-0x04 beacon: [ 234, 'white', ' WB '] # Waiting for Beacon associated: [23, 'white', ' Assoc '] # Associated # Exported functions for printing stuff and other such tomfoolery # ---------------------------------------------------------------------- exports.statusBar = (btype, value, handling) -> if btype is 'pinbar' cdwrite('reset', 'blue', '\n |- Trying pin ') cdwrite('bright', 'blue', "#{value}") cdwrite('reset', 'blue', ' - [') charm.push() # Save place for adding additional progress slices cdwrite('reset','blue', ' ] ') if handling cdwrite 'reset', 'blue', '- [' cdwrite 'bright', 28, " #{handling.totalChecked ? 0} " cdwrite 'bright', 52, " #{handling.consecutiveFailures ? 0} " cdwrite 'bright', 166," #{handling.totalFailures} " cdwrite 'reset', 'blue', ']' charm.pop() else if btype is 'procbar' cdwrite('reset', 250, '\n _- New reaver process spawned for ') cdwrite('bright', 250, "#{value}") cdwrite('reset', 250, ' - [') charm.push() cdwrite('reset', 250, ' ]') now = new Date() tstamp = "#{now.getHours()}:#{now.getMinutes()}:#{now.getSeconds()} #{now.getMonth()}-#{now.getDate()}-#{now.getFullYear()}" cdwrite('reset','magenta', " @ #{tstamp}") charm.pop() else if btype is 'error' cdwrite 'bright', 'red', "\n ¯- #{value} " cdwrite 'reset', 'red', "- #{handling}\n" else if btype is 'success' cdwrite 'bright', 'green', "\n ¯-_-¯- #{value}" cdwrite 'reset', 'green', " - #{handling}\n" else bstyle = REAVER_BAR_STYLES[btype] ? [ 'blue','white',"#{value}" ] cdwrite 'reset', bstyle[1], bstyle[0], bstyle[2] cdwrite 'reset', '' return @ # Full blown title exports.title = (full=true)-> if full then arrwrite REAVETARD_TITLE, true else arrwrite REAVETARD_TITLE_S, true return @ # Prints the actions available once one or more stations have been selected post-scan/query/passing exports.targetActions = -> arrwrite REAVETARD_TARGET_ACTIONS, true return @ # Print the key for the extra selector #'s exports.reviewSelectors = () -> arrwrite REVIEW_SELECTORS, true return @ # TODO: Clean this mess up and keep things DRYer # Used to print the snazzy table headers, dividers, and footers, including variable sizes for their essid/psk exports.tableFrame = (type, maxessid, maxpsk, foot=false, line=false) => if line then char = '-' else char = '=' esect = new Array(maxessid + 1).join(char) psect = new Array(maxpsk + 1).join(char) if type is 'C' if line then return charm.write("\n |----+------+----+---+-------------------+#{esect}--+-------+----------+--#{psect}|\n") if foot then return charm.display('bright').write("\n \\=================================================================#{psect}#{esect}/\n\n") charm.display('reset').foreground('green').display('bright') charm.write(" /=================================================================#{psect}#{esect}\\\n") charm.write(" | ## | RSSI | Ch | L | BSSID |") @tableCell 'green', 'bright', 'ESSID', maxessid charm.display('bright').write(' Att. | PIN |') @tableCell 'green', 'bright', 'WPA PSK', maxpsk charm.display('bright').write("\n |=================================================================#{psect}#{esect}|\n") else if type is 'W' if line then return charm.write("\n |----+------+----+---+-------------------+#{esect}--+-------+---------------+------|\n") if foot then return charm.display('bright').write("\n \\==========================================================================#{esect}/\n\n") charm.display('reset').foreground('blue').display('bright') charm.write(" /==========================================================================#{esect}\\\n") charm.write(" | ## | RSSI | Ch | L | BSSID |") @tableCell 'blue', 'bright', 'ESSID', maxessid charm.display('bright').write(' Att. | Progress | PIN |\n') charm.write(" |==========================================================================#{esect}|\n") else if type is 'N' if line then return charm.write("\n |----+------+----+---+-------------------+#{esect}--|\n") if foot then return charm.display('bright').write("\n \\===========================================#{esect}/\n") charm.display('reset').foreground(5).display('bright') charm.display('bright').write(" /===========================================#{esect}\\\n") charm.write(" | ## | RSSI | Ch | L | BSSID |") @tableCell 5, 'bright', 'ESSID', maxessid charm.display('bright').write("\n |===========================================#{esect}|\n") return @ # prints a table cell + closing divider, ie: " 12341234 |" # returns a copy of itself with the same color/display for convenience exports.tableCell = (color, disp, value, size) -> if value is 'undefined' or value is undefined then value = ' ' charm.display('reset').display(disp).foreground(color) if value.length > size then charm.write(" #{value[0..(size-1)]} ") else charm.write(" #{value} ") if value.length < size # prints spaces, padding the cell to the desired size charm.write((new Array(size+1-value.length)).join(' ')) charm.display('reset').foreground(color).write('|') return fn = (v, s) => exports.tableCell.apply null, [color, disp, v, s] # TODO: This goloiath could benefit from some pampering, but seems to do the trick # Print up to 3 tables based on the provided station categories, each station is # given a unique value for their 'tableIndex' property as they're sequentially printed. # These are later used to select/filter out the specific targets desired by the user. exports.targetReviewTable = (stations) -> maxessid = 0 maxpsk = 0 for key,val of stations for s in val if s.essid?.length > maxessid then maxessid = s.essid.length if s.key?.length > maxpsk then maxpsk = s.key.length beginrow = (color, st) => charm.write(' |') @tableCell(color, 'reset', "#{st.tableIndex}", 2)("#{st.rssi}", 4)("#{st.channel}", 2) if st.locked then charm.foreground('red').display('bright').write(' Y ').display('reset').foreground(color).write('|') else @tableCell color, 'reset', 'n', 1 @tableCell(color, 'reset', "#{st.bssid}", 17)("#{st.essid}", maxessid) @title false # print short title ascii art indexed = [] if stations.complete?.length >= 1 @tableFrame 'C', maxessid, maxpsk stations.complete.sort (a,b) -> if a.rssi > b.rssi then 1 else -1 for s in stations.complete s.tableIndex = indexed.length + 1 indexed.push s if indexed.length > 1 then @tableFrame 'C', maxessid, maxpsk, false,true beginrow 'green', s @tableCell('green', 'reset', "#{(Number(s.session.ki1) + Number(s.session.ki2))}", 5)("#{s.session.pin}", 8)("#{s.key}", maxpsk) @tableFrame 'C', maxessid, maxpsk, true if stations.inProgress?.length >= 1 @tableFrame 'W', maxessid, maxpsk isfirst = true stations.inProgress.sort (a,b) -> if a.attempts < b.attempts then 1 else -1 for s in stations.inProgress s.tableIndex = indexed.length + 1 indexed.push s if isfirst then isfirst = false else @tableFrame 'W', maxessid, maxpsk, false, true perc = Math.round(Number(s.attempts/110)) beginrow 'blue', s @tableCell('blue', 'reset', "#{s.attempts}", 5)("#{(new Array(Math.round(perc/10)+1)).join('█')} #{perc}%", 13)(s.session.pin ? ' ', 4) @tableFrame 'W', maxessid, maxpsk, true if stations.noHistory?.length >= 1 @tableFrame 'N', maxessid, maxpsk isfirst = true stations.noHistory.sort (a,b) -> if a.rssi > b.rssi then 1 else -1 for s in stations.noHistory s.tableIndex = indexed.length + 1 indexed.push s if isfirst then isfirst = false else @tableFrame 'N', maxessid, maxpsk, false,true beginrow 5, s @tableFrame 'N', maxessid, maxpsk, true return indexed # Waaahhhh shhhit son, dis here is the real-time survey result 'hit' # status codes: C='Complete', W='Work In Progress', N='New' exports.washHit = (station, status, isdbq = false) -> c = STATION_COLORS[status] if station.locked then c = 'red' @label c, ' st', status if not isdbq or station.rssi? then @label c, 'rssi', station.rssi @label c, 'bssid', station.bssid if not isdbq then @label c, 'ch', station.channel @label c, 'essid', station.essid if status is 'C' @label c, 'pin', station.session?.pin @label c, 'key', station.key @label c, 'checked', (Number(station.session.ki1) + Number(station.session.ki2)) else if status is 'W' @label c, 'progress', "#{Number(station.attempts/110).toFixed(2)}%" @label c, 'phase', station.session.phase @label c, 'checks', (Number(station.session.ki1) + Number(station.session.ki2)) if station.attempts > 10000 or station.session.phase is 1 then @label c, 'pin', "#{station.session.pin}" if station.device? @label c, 'device', station.device.name @label c, 'manuf.', station.device.manufacturer @label c, 'model', "#{station.device.model}/#{station.device.number}" if station.locked then @label c, '!', 'LOCKED' charm.write '\n' return @ # Print progress overview for active target of attack queue exports.attackProgress = (atkq) -> a = atkq.active ? false r = atkq.reaver ? false if a and r charm.push() cdwrite 'bright', 'blue', "\n //#{a.essid}" cdwrite 'reset', 250, ' | ' @label 'magenta', 'rxok', r.status.foundBeacon @label 'blue', 'assoc', r.status.associated @label 'magenta', 'ch', r.status.channel if r.status.locked then cdwrite 'bright', 'red', '¡LOCKED!' cdwrite 'reset', 250, ' | ' @label 'blue', 'phase', r.status.phase @label 'magenta', 'pin', r.status.pin cdwrite 'reset', 250, ' | ' @label 'blue', 'msdepth', r.metrics.maxSeqDepth @label 'magenta', 'checked', r.metrics.totalChecked @label 'blue', 'failed', r.metrics.totalFailures @label 'magenta', 'inarow', r.metrics.consecutiveFailures charm.pop() # Prints a "label", which is looks like: ltext/RTEXT (caps = bright) exports.label = (fg, ltext, rtext) -> charm.display('reset').foreground(fg).write("#{ltext}/") charm.display('bright').foreground(fg).write("#{rtext} ").display('reset') return @ # Prints labels for an array of keys that correspond to an object's prop's exports.labels = (color, keys, obj) -> for key in keys @label color, key, obj[key] # *See cwrite above* exports.cwrite = (args...) -> cwrite args... return @ # *See cdwrite above* exports.cdwrite = (args...) -> cdwrite args... return @ # Don't think this is necessary with erase exposed below # TODO: Verify and remove or finalize exports.clear = -> charm.erase 'screen' charm.position 0,0 return @ # When extra customization necessary exports.write = charm.write exports.erase = charm.erase exports.foreground = charm.foreground exports.background = charm.background exports.display = charm.display # Just in case, expose a reference to our charmed stdout stream exports._c = charm;