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

366 lines (346 loc) 15.1 kB
# Reavetard - Reaver WPS (+Wash) extension scripts # reaver.coffee :: Module to spawn reaver processes and provide basic output # parsing via status update events # Author: Robbie Saunders http://eibbors.com/[/p/reavetard] # ============================================================================== # Module dependencies # ------------------------- events = require 'events' {spawn, exec} = require 'child_process' {REAVER_DEFAULT_ARGS, CONSECUTIVE_FAILURE_LIMIT, DEFAULT_INTERFACE} = require './config' cli = require './cli' # Arguments supported by reaver in the format: # key: [shortFlag, fullFlag, description, includesValue] REAVER_ARGS = interface: ['-i', '--interface', 'Name of the monitor-mode interface to use', true] bssid: ['-b', '--bssid', 'BSSID of the target AP', true] mac: ['-m', '--mac', 'MAC of the host system', true] essid: ['-e', '--essid', 'ESSID of the target AP', true] channel: ['-c', '--channel', 'Set the 802.11 channel for the interface (implies -f)', true] outFile: ['-o', '--out-file', 'Send output to a log file [stdout]', true] session: ['-s', '--session', 'Restore a previous session file', true] exec: ['-C', '--exec', 'Execute the supplied command upon successful pin recovery', true] daemonize: ['-D', '--daemonize', 'Daemonize reaver', false] auto: ['-a', '--auto', 'Auto detect the best advanced options for the target AP', false] fixed: ['-f', '--fixed', 'Disable channel hopping', false] use5ghz: ['-5', '--5ghz', 'Use 5GHz 802.11 channels', false] veryVerbose: ['-vv', '--verbose', 'Display non-critical warnings (-vv for more)', false] verbose: ['-v', '--verbose', 'Display non-critical warnings (-vv for more)', false] quiet: ['-q', '--quiet', 'Only display critical messages', false] help: ['-h', '--help', 'Show help', false] pin: ['-p', '--pin', 'Use the specified 4 or 8 digit WPS pin', true] delay: ['-d', '--delay', 'Set the delay between pin attempts [1]', true] lockDelay: ['-l', '--lock-delay', 'Set the time to wait if the AP locks WPS pin attempts [60]', true] maxAttempts: ['-g', '--max-attempts', 'Quit after num pin attempts', true] failWait: ['-x', '--fail-wait', 'Set the time to sleep after 10 unexpected failures [0]', true] recurringDelay: ['-r', '--recurring-delay', 'Sleep for y seconds every x pin attempts', true] timeout: ['-t', '--timeout', 'Set the receive timeout period [5]', true] m57Timeout: ['-T', '--m57-timeout', 'Set the M5/M7 timeout period [0.20]', true] noAssociate: ['-A', '--no-associate', 'Do not associate with the AP (association must be done by another application)', false] noNacks: ['-N', '--no-nacks', 'Do not send NACK messages when out of order packets are received', false] dhSmall: ['-S', '--dh-small', 'Use small DH keys to improve crack speed', false] ignoreLocks: ['-L', '--ignore-locks', 'Ignore locked state reported by the target AP', false] eapTerminate: ['-E', '--eap-terminate', 'Terminate each WPS session with an EAP FAIL packet', false] nack: ['-n', '--nack', 'Target AP always sends a NACK [Auto]', false] win7: ['-w', '--win7', 'Mimic a Windows 7 registrar [False]', false] REAVER_PATTERNS = sending: /Sending (.*)/ received: /Received (.*)/ tryingPin: /Trying pin (\d+)/ timedOut: /WARNING: Receive timeout occurred/ wpsFailure: /WPS transaction failed \(code: (\w+)\)/ newChannel: /Switching (\w+) to channel (\d+)/ waitingBeacon: /Waiting for beacon from (\S+)/ associated: /Associated with (\S+)/ rateLimit: /WARNING: Detected AP rate limiting, waiting (\d+) seconds before re-checking/ associateFailure: /WARNING: Failed to associate with (\S+) \(ESSID: (.*)\)/ consecutiveFailures: /WARNING: (\d+) failed connections in a row/ progress: /(\S+)% complete @ (\S+) (\S+) \((\d+) seconds\/pin\)/ crackedTime: /Pin cracked in (\d+) (\w+)/ crackedPIN: /WPS PIN: '(\d+)'/ crackedPSK: /WPA PSK: '(.*)'/ crackedSSID: /AP SSID: '(.*)'/ version: /Reaver v(\S+) WiFi Protected Setup Attack Tool/ # nothingDone: /Nothing done, nothing to save/ # copyright: /copyright # Used to track how far into an attempt we get upon failure, which comes in handy # when trying to decide whether to move onto the next station and troubleshooting REAVER_PACKET_SEQ = 'EAPOL START request': 1 'identity request': 2 'identity response': 3 'M1 message': 4 'M2 message': 5 'M3 message': 6 'M4 message': 7 'M5 message': 8 'M6 message': 9 'M7 message': 10 'WSC NACK': 0 # Reversed version of above REAVER_PACKET_SEQ_S = 1: ' St '#> 2: ' Id '#> 3: ' Id '#< 4: ' M1 '#> 5: ' M2 '#< 6: ' M3 '#> 7: ' M4 '#< 8: ' M5 '#> 9: ' M6 '#< 10: ' M7 '#> 0: 'NACK' ### Child process wrapper for spawned reaver processes ### class Reaver extends events.EventEmitter constructor: (options) -> for own key, value of options @[key] = value @interface ?= DEFAULT_INTERFACE ? 'mon0' @bssid ?= null @proc = null @status = foundBeacon: false associated: false locked: false channel: 0 pin: null phase: 0 sequenceDepth: -1 alreadyFailed: false @metrics = maxSeqDepth: 0 consecutiveFailures: 0 timeOuts: 0 totalFailures: 0 totalChecked: 0 timeToCrack: null secondsPerPin: null startedAt: new Date() # Setting a duration will switch to exec vs. real time parsing (spawn) start: (args) => # create an arguments array, if not provided by caller if not args? args = [] for key, value of REAVER_ARGS when @[key]? [flag, option, desc, inclVal] = value if @[key] or inclVal then args.push flag if inclVal then args.push @[key] # kill the existing process, then spawn a new one and bind to the data event @stop() @proc = spawn 'reaver', args @proc.stdout.on 'data', @process @proc.stderr.on 'data', @process stop: () => if @proc @proc.kill() @emit 'exit', true # parse and emit any discovered stations process: (data) => msgs = data.toString('ascii').split '\n' for msg in msgs when msg.length > 1 matched = false for key, pattern of REAVER_PATTERNS res = pattern.exec msg if res matched = key break switch matched when false matched = "unhandled" res = [msg] when 'waitingBeacon' @status.foundBeacon = false when 'newChannel' @status.channel = res[2] @status.associated = false when 'associated' @status.foundBeacon = true @status.associated = true when 'tryingPin' @status.alreadyFailed = false @status.sequenceDepth = 0 if @status.pin isnt res[1] @metrics.totalChecked++ @metrics.consecutiveFailures = 0 if @status.pin isnt null if @status.phase is 0 and @status.pin[0..3] is res[1][0..3] @status.phase = 1 @emit 'completed', 1, { pin: res[1][0..3] } @status.pin = res[1] when 'sending', 'received' if REAVER_PACKET_SEQ[res[1]] > @status.sequenceDepth @status.sequenceDepth = REAVER_PACKET_SEQ[res[1]] @status.sequenceNew = true @metrics.maxSeqDepth = Math.max(@metrics.maxSeqDepth, @status.sequenceDepth) else @status.sequenceNew = false when 'timedOut', 'wpsFailure' if matched is 'timedOut' then @metrics.timeOuts++ if not @status.alreadyFailed @metrics.consecutiveFailures++ @metrics.totalFailures++ @status.alreadyFailed = true when 'rateLimit', 'associateFailure' @status.associated = false @status.locked = (matched is 'rateLimit') @status.sequenceDepth = -1 when 'consecutiveFailures' @metrics.consecutiveFailures when 'progress' @metrics.secondsPerPin = res[4] ? null when 'crackedPSK' @emit 'completed', 2, 'psk', res[1] when 'crackedPIN' @emit 'completed', 2, 'pin', res[1] when 'crackedTime' @status.phase = 2 @metrics.timeToCrack = res[1..2].join(' ') @emit 'completed', 2, 'time', @metrics.timeToCrack when 'crackedSSID' @emit 'completed', 2, 'ssid', res[1] @emit 'status', matched, @status, @metrics, res # Moved from reavetard.coffee since almost all of the code was heavily # related to the Reaver class above. class ReaverQueueManager extends events.EventEmitter constructor: (stations, @interface) -> @priority = [] # cracking sessions that are working @secondary = [] # sessions with non-fatal issues @finished = [] # complete and/or completely fucked targets for station in stations station.priority = true @priority.push station @active = null @interface ?= config.DEFAULT_INTERFACE @solitaire = stations.length <= 1 # Will stop the active process and start a passed station # Calls @next if station is not provided start: (station) => if @active or @reaver then @stop('skipped') if station then @active = station else @active = @next() if @active # next may return false so we gotta check # if this is the only station we have, then set solitaire flag if @priority.length is 0 and @secondary.length is 0 then @solitaire = true @active.reaverArgs ?= REAVER_DEFAULT_ARGS(@active, @interface) @reaver = new Reaver(@active.reaverArgs) # Setup our event listeners for the update/completed events @reaver.on 'status', @handleUpdates @reaver.on 'completed', @handleCompleted @reaver.start() cli.statusBar 'procbar', "#{@active.essid ? @active.bssid}" # Returns the next queued station or emits end:empty event and returns false next: () => if @priority.length > 0 @priority.shift() else if @secondary.length > 0 @secondary.shift() else @emit 'end', 'empty', @finished false # Stops the active process and stashes it in one of the 3 arrays based # on the reason argument. Any key/value pairs of details argument are # passed on to station before being pushed to its destination. stop: (reason, results) => # Always stop the running process and copy over results if @reaver if @active @active.results ?= {} @active.results.snapshots ?= { status: [], metrics: [] } @active.results.snapshots.status.push @reaver.status ? 'empty' @reaver.metrics.stoppedAt = new Date() @active.results.snapshots.metrics.push @reaver.metrics ? 'empty' @reaver.stop() delete @reaver if @active? and results (@active[k] = v) for k,v of results switch reason when 'paused' if @active.priority then @priority.unshift @active else @secondary.unshift @active when 'killed' # @finished.push @active @finished = @finished.concat(@priority, @secondary) @emit 'end', 'killed', @finished when 'skipped', 'wait' # Skipped or ran into temporary issue if not @solitaire if @active.priority then @priority.push @active else @secondary.push @active when 'cracked' @active.success = true @finished.push @active when 'fatal' # There was a deal breaker, like no device communication @active.success = false @finished.push @active else # Some unknown issue? Push to second string queue # Set priority to false? (currently will put back into priority queue after one revolution) @secondary.push @active # Always clear the active station reference @active = null if reason isnt 'killed' then @emit 'stopped', reason # Used to verify some condition by calling fn after period (milliseconds) # has elapsed, then calling success or failure based on fn's return value # If isInterval, then each function will be passed the interval object, # in order to facilitate clearing / etc. verify: (fn, period, success, failure, isInterval=false) => self = @ if typeof(fn) isnt 'function' if fn then result = success else result = failure if isInterval iv = setInterval(( => result.apply(null, [self, iv])), period) else setTimeout((=> result.apply(null, [self, false])), period) else result = (interval=false) => res = fn.apply(null, [self, interval]) if res then success.apply(null, [self, interval]) else failure.apply(null, [self, interval]) if isInterval iv = setInterval(( => result.apply(null, [iv])), period) else setTimeout( result, period ) # Basically extends the Reaver class' input handlers to include printing # compact status bars to stdout and stop/rotate when necessary or advantageous handleUpdates: (update, status, metrics, data) => switch update when 'waitingBeacon' cli.statusBar 'beacon' when 'associated' @active.essid ?= data[1] cli.statusBar 'associated' when 'tryingPin' cli.statusBar 'pinbar', "#{status.pin}", metrics when 'sending', 'received' if status.sequenceNew cli.statusBar status.sequenceDepth else if status.sequenceDepth is 0 cli.statusbar 'nack' when 'timedOut' cli.statusBar('timeout') @emit 'failed', status, metrics when 'wpsFailure' cli.statusBar "fail#{data[1][3]}" @emit 'failed', status, metrics when 'rateLimit' if @solitaire then cli.statusBar 'error', 'LOCKING DETECTED', 'waiting until resolved... (solitaire=true)' else cli.statusBar 'error', 'LOCKING DETECTED', 'rotating target networks...' @stop('wait', { priority: false, error: 'locking' }) when 'associateFailure' @active.essid ?= data[1] cli.statusBar 'error', 'CANNOT ASSOCIATE', 'aborting current target & rotating...' @stop('fatal', { error: 'associateFailure' }) if metrics.consecutiveFailures >= CONSECUTIVE_FAILURE_LIMIT cli.statusBar 'error', 'FAILURE LIMIT TRIGGERED', 'rotating target networks...' @stop('failures', { priority: false, error: 'consecutiveFailures' }) handleCompleted: (phase, key, value) => if @active? @active.results ?= {} @active.results[key] = value if phase is 1 cli.statusBar 'success', 'Phase 1 completed!', "First 1/2 of PIN: #{value}" if phase is 2 if key is 'ssid' # sent last, meaning we can start the next one cli.statusBar 'success', 'Phase 2 completed!',"Results: #{JSON.stringify(@active.results)}" @stop('cracked') module.exports.Reaver = Reaver module.exports.ReaverQueueManager = ReaverQueueManager