Lucene search

K
nmapJiayi YeNMAP:TOR-CONSENSUS-CHECKER.NSE
HistoryJun 06, 2015 - 1:24 a.m.

tor-consensus-checker NSE Script

2015-06-0601:24:05
Jiayi Ye
nmap.org
149

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

10 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:L/Au:N/C:C/I:C/A:C

0.973 High

EPSS

Percentile

99.8%

Checks if a target is a known Tor node.

The script works by querying the Tor directory authorities. Initially, the script stores all IPs of Tor nodes in a lookup table to reduce the number of requests and make lookups quicker.

Script Arguments

slaxml.debug

See the documentation for the slaxml library.

http.host, http.max-body-size, http.max-cache-size, http.max-pipeline, http.pipeline, http.truncated-ok, http.useragent

See the documentation for the http library.

smbdomain, smbhash, smbnoguest, smbpassword, smbtype, smbusername

See the documentation for the smbauth library.

Example Usage

nmap --script=tor-consensus-checker <host>

Script Output

Host script results:
| tor-consensus-checker:
|_  127.0.0.1 is a Tor node

Requires


local http = require "http"
local ipOps = require "ipOps"
local stdnse = require "stdnse"
local string = require "string"
local nmap = require "nmap"

description = [[
Checks if a target is a known Tor node.

The script works by querying the Tor directory authorities. Initially,
the script stores all IPs of Tor nodes in a lookup table to reduce the
number of requests and make lookups quicker.
]]

---
-- @usage
-- nmap --script=tor-consensus-checker <host>
--
-- @output
-- Host script results:
-- | tor-consensus-checker:
-- |_  127.0.0.1 is a Tor node
---

author = "Jiayi Ye"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"external", "safe"}

-- from Tor 0.2.9 auth_dirs.inc
local dir_authorities = {
  { ip = "128.31.0.39", port = 9131},
  { ip = "86.59.21.38", port = 80 },
  { ip = "45.66.33.45", port = 80 },
  { ip = "66.111.2.131", port = 9030 },
  { ip = "131.188.40.189", port = 80 },
  { ip = "193.23.244.244", port = 80 },
  { ip = "171.25.193.9", port = 443 },
  { ip = "154.35.175.225", port = 80 },
  { ip = "199.58.81.140", port = 80 },
  { ip = "204.13.164.118", port = 80 },
}

hostrule = function(host)
  if nmap.registry.tornode and not(nmap.registry.tornode.connect) then
    return false
  end
  return not ipOps.isPrivate(host.ip)
end

function get_consensus(server)
  local response = http.get(server.ip, server.port, "/tor/status-vote/current/consensus",
    {
      -- consensus files were 2.3 MiB as of February 2020
      -- https://metrics.torproject.org/collector/recent/relay-descriptors/consensuses/
      no_cache = true,
      max_body_size=3*1024*1024
    })

  if not response.status then
    stdnse.print_debug(2, "failed to connect to " .. server.ip)
  elseif response.status ~= 200  then
    stdnse.print_debug(2, "%s http error %s", server.ip, response.status)
  else
    stdnse.print_debug(2, "consensus retrieved from %s", server.ip)
    return response.body
  end

  -- no valid server found
  return nil
end

function script_init()
  -- Data and flags shared between threads.
  -- @name tornode
  -- @class table
  -- @field cache     A table for cached tor nodes
  -- @field connect   A flag which prevents threads from looking up when failed to connnect to directory authorities
  nmap.registry.tornode = {}
  nmap.registry.tornode.cache = {}

  local isConnected = false
  local regexp = "r [%S]+ [%S]+ [%S]+ [%d-]+ [%d:]+ ([%d.]+) ([%d]+) [%d]*"
  for _, server in ipairs(dir_authorities) do
    local consensus = get_consensus(server)
    if consensus then
      -- parse the consensus
      for line in string.gmatch(consensus,"[^\n]+") do
        local _, _, ip, port = string.find(line,regexp)
        if ip then
          isConnected = true
          nmap.registry.tornode.cache[ip] = true
        end
      end
    end
    if isConnected then
      break
    end
  end
  if not(isConnected) then
    stdnse.verbose1("failed to connect to directory authorities")
  end
  nmap.registry.tornode.connect = isConnected
end

function check_tornode_cache(ip)
  if not next( nmap.registry.tornode.cache ) then return false end
  if type( ip ) ~= "string" or ip == "" then return false end
  return nmap.registry.tornode.cache[ip]
end

action = function(host)
  local mutex = nmap.mutex("tornode")
  mutex "lock"
  --initialize nmap.registry.tornode
  if not nmap.registry.tornode then
    script_init()
  end
  mutex "done"

  if not(nmap.registry.tornode.connect) then
    if nmap.verbosity() > 2 then
      return "Couln't connect to Tor dir authorities"
    else
      return nil
    end
  end

  local output_tab = stdnse.output_table()
  if check_tornode_cache(host.ip) then
    output_tab.tor_nodes = host.ip
    return output_tab, host.ip .. " is a Tor node"
  else
    return output_tab, host.ip .. " not found in Tor consensus"
  end
end

9.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

10 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:L/Au:N/C:C/I:C/A:C

0.973 High

EPSS

Percentile

99.8%

Related for NMAP:TOR-CONSENSUS-CHECKER.NSE