diff --git a/CHANGELOG b/CHANGELOG index f0dc194a1..0c89c0d91 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,9 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Added allseeingeye-info for gathering information from games + using this query protocol. A version detection probe was also + added. [Marin Maržić] + o [NSE] Add freelancer-info to gather information about the Freelancer game server. Also added a related version detection probe and UDP protocol payload for detecting the service. [Marin Maržić] diff --git a/nmap-service-probes b/nmap-service-probes index dec20c9b9..38cf1a781 100644 --- a/nmap-service-probes +++ b/nmap-service-probes @@ -12336,3 +12336,15 @@ ports 2302 match freelancer m|^\x00\x03\xf1\x26.{88}(.*)\0\0(?:.*?:){5}(.*)\0\0$|s p/Freelancer/ i/name: $P(1); description: $P(2)/ +# All-Seeing Eye service provided by some game servers for querying +# the server's status +# For more info on the protocol see: +# http://int64.org/docs/gamestat-protocols/ase.html +# http://aluigi.altervista.org/papers.htm#ase +# http://sourceforge.net/projects/gameq/ +# (relevant files: games.ini, packets.ini, ase.php) +Probe UDP ASE q|s| +rarity 9 +ports 1258,2126,3123,12444,13200,23196,26000,27138,27244,27777,28138 + +match allseeingeye m/^EYE1.(.*?)(?|\x02(\d)|\x03(\d{2})|\x04(\d{3})|\x05(\d{4})|\x06(\d{5}))/s p/All-Seeing Eye/ i/game: $1; port: $2/ diff --git a/scripts/allseeingeye-info.nse b/scripts/allseeingeye-info.nse new file mode 100644 index 000000000..671010bbd --- /dev/null +++ b/scripts/allseeingeye-info.nse @@ -0,0 +1,232 @@ +local comm = require "comm" +local nmap = require "nmap" +local shortport = require "shortport" +local string = require "string" +local bit = require "bit" +local stdnse = require "stdnse" + +description = [[ +Detects the All-Seeing Eye service. Provided by some game servers for +querying the server's status. + +The All-Seeing Eye service can listen on a UDP port separate from the +main game server port (usually game port + 123). On receiving a packet +with the payload "s", it replies with various game server status info. + +When run as a version detection script (-sV), the script +will report on the game name, version, actual port, and whether it has a +password. When run explicitly (--script ase-info), the +script will additionally report on the server name, game type, map name, +current number of players, maximum number of players, player +information, and various other information. + +For more info on the protocol see: +http://int64.org/docs/gamestat-protocols/ase.html +http://aluigi.altervista.org/papers.htm#ase +http://sourceforge.net/projects/gameq/ +(relevant files: games.ini, packets.ini, ase.php) +]] + +-- @usage +-- nmap -sV +-- @usage +-- nmap -Pn -sU -sV --script allseeingeye-info -p +-- +-- @output +-- PORT STATE SERVICE REASON VERSION +-- 27138/udp open allseeingeye udp-response All-Seeing Eye (game: chrome 1.2.0.0ww; port: 27015; no password) +-- | ase-info: +-- | game: chrome +-- | port: 27015 +-- | server name: ChromeNet Server +-- | game type: Team Death Match +-- | map: Data/LevelsNet/Narrow/Narrow.map +-- | version: 1.2.0.0ww +-- | passworded: 0 +-- | num players: 2 +-- | max players: 16 +-- | settings: +-- | Dedicated: No +-- | Password Required: No +-- | Time Limit: 30 +-- | Points Limit: 200 min. +-- | Respawns Limit: unlimited +-- | Respawn Delay: 10 sec. +-- | Enemies Visible On Map: No +-- | Available Inventory Room: Yes +-- | Identify Enemy Players: No +-- | Available Vehicles: Yes +-- | Vehicle Respaws Limit: unlimited +-- | Vehicle Respawn Delay: 30 sec. +-- | Vehicle Auto Return Time: 90 sec. +-- | Vehicles Visible On Map: Yes +-- | Team Balance: Off +-- | Friendly Fire: On +-- | Friends Visible On Map: Yes +-- | players: +-- | player 0: +-- | name: NoVoDondo +-- | team: BLUE +-- | skin: +-- | score: 71 +-- | ping: 0 +-- | time: +-- | player 1: +-- | name: HeroX +-- | team: RED +-- | skin: +-- | score: 0 +-- | ping: 11 +-- |_ time: +-- +-- @xmloutput +-- chrome +-- 27015 +-- ChromeNet Server +-- Team Death Match +-- Data/LevelsNet/Narrow/Narrow.map +-- 1.2.0.0ww +-- 0 +-- 2 +-- 16 +-- +-- No +-- No +-- 30 +-- 200 min. +-- unlimited +-- 10 sec. +-- No +-- Yes +-- No +-- Yes +-- unlimited +-- 30 sec. +-- 90 sec. +-- Yes +-- Off +-- On +-- Yes +--
+-- +--
+-- NoVoDondo +-- BLUE +-- +-- 71 +-- 0 +-- +--
+-- +-- HeroX +-- RED +-- +-- 0 +-- 11 +-- +--
+-- + +author = "Marin Maržić" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +categories = { "discovery", "safe", "version" } + +-- Unpacks a string preceded by a 1-byte integer length +-- (+1 for the length byte) from another string, starting +-- at the given position. +-- @param str the string from which to unpack +-- @param pos the position in the string from which to start unpacking (position of the length byte) +-- @return ret_pos the position after the last unpacked byte +-- @return string the unpacked string +local unpack_str = function(str, pos) + local ret_pos = pos + str:byte(pos) + return ret_pos, string.sub(str, pos + 1, ret_pos - 1) +end + +portrule = shortport.version_port_or_service({1258,2126,3123,12444,13200,23196,26000,27138,27244,27777,28138}, "allseeingeye", "udp") + +action = function(host, port) + local status, data = comm.exchange(host, port.number, "s", { proto = "udp", timeout = 3000 }) + if not status then + return + end + + -- UDP port is open + nmap.set_port_state(host, port, "open") + + if not string.match(data, "^EYE1") then + return + end + + -- Detected; extract fields + local o = stdnse.output_table() + local pos = 5 + + pos, o["game"] = unpack_str(data, pos) + pos, o["port"] = unpack_str(data, pos) + pos, o["server name"] = unpack_str(data, pos) + pos, o["game type"] = unpack_str(data, pos) + pos, o["map"] = unpack_str(data, pos) + pos, o["version"] = unpack_str(data, pos) + pos, o["passworded"] = unpack_str(data, pos) + pos, o["num players"] = unpack_str(data, pos) + pos, o["max players"] = unpack_str(data, pos) + + -- extract the key-value pairs + local kv = stdnse.output_table() + o["settings"] = kv + while data:byte(pos) ~= 1 do + local key, value + pos, key = unpack_str(data, pos) + pos, value = unpack_str(data, pos) + kv[key] = value + end + pos = pos + 1 + + -- extract player info + local players = stdnse.output_table() + o["players"] = players + local playernum = 0 + while pos <= #data do + local flags = data:byte(pos) + pos = pos + 1 + + local player = stdnse.output_table() + if bit.band(flags, 1) ~= 0 then + pos, player.name = unpack_str(data, pos) + end + if bit.band(flags, 2) ~= 0 then + pos, player.team = unpack_str(data, pos) + end + if bit.band(flags, 4) ~= 0 then + pos, player.skin = unpack_str(data, pos) + end + if bit.band(flags, 8) ~= 0 then + pos, player.score = unpack_str(data, pos) + end + if bit.band(flags, 16) ~= 0 then + pos, player.ping = unpack_str(data, pos) + end + if bit.band(flags, 32) ~= 0 then + pos, player.time = unpack_str(data, pos) + end + + players["player " .. playernum] = player + playernum = playernum + 1 + end + + port.version.name = "ase" + port.version.name_confidence = 10 + port.version.product = "All-Seeing Eye" + local passworded_string + if o["passworded"] == "0" then + passworded_string = "; no password" + else + passworded_string = "; has password" + end + port.version.extrainfo = "game: " .. o["game"] .. " " .. o["version"] .. "; port: " .. o["port"] .. passworded_string + + nmap.set_port_version(host, port, "hardmatched") + + return o +end \ No newline at end of file diff --git a/scripts/script.db b/scripts/script.db index 3c4f99c55..f3a58c7d0 100644 --- a/scripts/script.db +++ b/scripts/script.db @@ -10,6 +10,7 @@ Entry { filename = "ajp-brute.nse", categories = { "brute", "intrusive", } } Entry { filename = "ajp-headers.nse", categories = { "discovery", "safe", } } Entry { filename = "ajp-methods.nse", categories = { "default", "safe", } } Entry { filename = "ajp-request.nse", categories = { "discovery", "safe", } } +Entry { filename = "allseeingeye-info.nse", categories = { "discovery", "safe", "version", } } Entry { filename = "amqp-info.nse", categories = { "default", "discovery", "safe", "version", } } Entry { filename = "asn-query.nse", categories = { "discovery", "external", "safe", } } Entry { filename = "auth-owners.nse", categories = { "default", "safe", } }