Extracts info from Quake3 game server and similar games. Displays server port, players, and game options
Reporter | Title | Published | Views | Family All 199 |
---|---|---|---|---|
Nmap | smtp-enum-users NSE Script | 13 Mar 201004:03 | – | nmap |
Nmap | dns-brute NSE Script | 5 Mar 201121:16 | – | nmap |
Nmap | mikrotik-routeros-brute NSE Script | 30 Jul 201403:48 | – | nmap |
Nmap | http-slowloris NSE Script | 16 Jul 201219:27 | – | nmap |
Nmap | freelancer-info NSE Script | 20 Nov 201304:31 | – | nmap |
Nmap | irc-info NSE Script | 6 Nov 200802:52 | – | nmap |
Nmap | ajp-methods NSE Script | 7 May 201218:49 | – | nmap |
Nmap | telnet-encryption NSE Script | 28 Dec 201100:57 | – | nmap |
Nmap | http-unsafe-output-escaping NSE Script | 15 Dec 201106:19 | – | nmap |
Nmap | hostmap-robtex NSE Script | 9 Apr 201206:35 | – | nmap |
local comm = require "comm"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local stringaux = require "stringaux"
local table = require "table"
description = [[
Extracts information from a Quake3 game server and other games which use the same protocol.
]]
---
-- @usage
-- nmap -sU -sV -Pn --script quake3-info.nse -p <port> <target>
--
-- @output
-- PORT STATE SERVICE VERSION
-- 27960/udp open quake3 Quake 3 dedicated server
-- | quake3-info:
-- | PLAYERS:
-- | 1. cyberix (frags: 0/20, ping: 4)
-- | BASIC OPTIONS:
-- | capturelimit: 8
-- | dmflags: 0
-- | elimflags: 0
-- | fraglimit: 20
-- | gamename: baseoa
-- | mapname: oa_dm1
-- | protocol: 71
-- | timelimit: 0
-- | version: ioq3 1.36+svn1933-1/Ubuntu linux-x86_64 Apr 4 2011
-- | videoflags: 7
-- | voteflags: 767
-- | OTHER OPTIONS:
-- | bot_minplayers: 0
-- | elimination_roundtime: 120
-- | g_allowVote: 1
-- | g_altExcellent: 0
-- | g_delagHitscan: 0
-- | g_doWarmup: 0
-- | g_enableBreath: 0
-- | g_enableDust: 0
-- | g_gametype: 0
-- | g_instantgib: 0
-- | g_lms_mode: 0
-- | g_maxGameClients: 0
-- | g_needpass: 0
-- | g_obeliskRespawnDelay: 10
-- | g_rockets: 0
-- | g_voteGametypes: /0/1/3/4/5/6/7/8/9/10/11/12/
-- | g_voteMaxFraglimit: 0
-- | g_voteMaxTimelimit: 0
-- | g_voteMinFraglimit: 0
-- | g_voteMinTimelimit: 0
-- | sv_allowDownload: 0
-- | sv_floodProtect: 1
-- | sv_hostname: noname
-- | sv_maxPing: 0
-- | sv_maxRate: 0
-- | sv_maxclients: 8
-- | sv_minPing: 0
-- | sv_minRate: 0
-- |_ sv_privateClients: 0
author = "Toni Ruottu"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default", "discovery", "safe", "version"}
local function range(first, last)
local list = {}
for i = first, last do
table.insert(list, i)
end
return list
end
portrule = shortport.version_port_or_service(range(27960, 27970), {'quake3'}, 'udp')
local function parsefields(data)
local fields = {}
local parts = stringaux.strsplit("\\", data)
local nullprefix = table.remove(parts, 1)
if nullprefix ~= "" then
stdnse.debug2("unrecognized field format, skipping options")
return {}
end
for i = 1, #parts, 2 do
local key = parts[i]
local value = parts[i + 1]
fields[key] = value
end
return fields
end
local function parsename(data)
local parts = stringaux.strsplit('"', data)
if #parts ~= 3 then
return nil
end
local e1 = parts[1]
local name = parts[2]
local e2 = parts[3]
local extra = e1 .. e2
if extra ~= "" then
return nil
end
return name
end
local function parseplayer(data)
local parts = stringaux.strsplit(" ", data)
if #parts < 3 then
stdnse.debug2("player info line is missing elements, skipping a player")
return nil
end
if #parts > 3 then
stdnse.debug2("player info line has unknown elements, skipping a player")
return nil
end
local player = {}
player.frags = parts[1]
player.ping = parts[2]
player.name = parsename(parts[3])
if player.name == nil then
stdnse.debug2("invalid player name serialization, skipping a player")
return nil
end
return player
end
local function parseplayers(data)
local players = {}
for _, p in ipairs(data) do
local player = parseplayer(p)
if player then
table.insert(players, player)
end
end
return players
end
local function is_leader(a, b)
local collide = a.name == b.name
local even = a.frags == b.frags
local leads = a.frags > b.frags
local alphab = a.name > b.name
local faster = a.ping > b.ping
return leads or (even and alphab) or (even and collide and faster)
end
local function formatplayers(players, fraglimit)
table.sort(players, is_leader)
local printable = {}
for i, player in ipairs(players) do
local name = player.name
local ping = player.ping
local frags = player.frags
if fraglimit then
frags = string.format("%s/%s", frags, fraglimit)
end
table.insert(printable, string.format("%d. %s (frags: %s, ping: %s)", i, name, frags, ping))
end
printable["name"] = "PLAYERS:"
return printable
end
local function formatfields(fields, title)
local printable = {}
for key, value in pairs(fields) do
local kv = string.format("%s: %s", key, value)
table.insert(printable, kv)
end
table.sort(printable)
printable["name"] = title
return printable
end
local function assorted(fields)
local basic = {}
local other = {}
for key, value in pairs(fields) do
if string.find(key, "_") == nil then
basic[key] = value
else
other[key] = value
end
end
return basic, other
end
action = function(host, port)
local GETSTATUS = "\xff\xff\xff\xffgetstatus\n"
local STATUSRESP = "\xff\xff\xff\xffstatusResponse"
local status, data = comm.exchange(host, port, GETSTATUS, {["proto"] = "udp"})
if not status then
return
end
local parts = stringaux.strsplit("\n", data)
local header = table.remove(parts, 1)
if header ~= STATUSRESP then
return
end
if #parts < 2 then
stdnse.debug2("incomplete status response, script abort")
return
end
local nullend = table.remove(parts)
if nullend ~= "" then
stdnse.debug2("missing terminating endline, script abort")
return
end
local field_data = table.remove(parts, 1)
local player_data = parts
local fields = parsefields(field_data)
local players = parseplayers(player_data)
local basic, other = assorted(fields)
-- Previously observed version strings:
-- "tremulous 1.1.0 linux-x86_64 Aug 5 2010"
-- "ioq3 1.36+svn1933-1/Ubuntu linux-x86_64 Apr 4 2011"
local versionline = basic["version"]
if versionline then
local fields = stringaux.strsplit(" ", versionline)
local product = fields[1]
local version = fields[2]
local osline = fields[3]
port.version.name = "quake3"
port.version.product = product
port.version.version = version
if string.find(osline, "linux") then
port.version.ostype = "Linux"
end
if string.find(osline, "win") then
port.version.ostype = "Windows"
end
nmap.set_port_version(host, port)
end
local fraglimit = fields["fraglimit"]
if not fraglimit then
fraglimit = "?"
end
local response = {}
table.insert(response, formatplayers(players, fraglimit))
table.insert(response, formatfields(basic, "BASIC OPTIONS:"))
if nmap.verbosity() > 0 then
table.insert(response, formatfields(other, "OTHER OPTIONS:"))
end
return stdnse.format_output(true, response)
end
Transform Your Security Services
Elevate your offerings with Vulners' advanced Vulnerability Intelligence. Contact us for a demo and discover the difference comprehensive, actionable intelligence can make in your security strategy.
Book a live demo