nmap指令碼詳解

2023-01-15 06:01:56

nmap --script

前言

我們通過nmap script來大幅擴充套件nmap的功能,nmap具有強大的指令碼引擎NSE(Nmap Scripting Engine),它允許使用者編寫(和共用)簡單的指令碼(使用lua程式語言)自動化各種網路任務

官方檔案

NSEDoc Reference Portal — Nmap Scripting Engine documentation

目前nmap官方有604個NSE指令碼,安裝nmap後儲存在script資料夾下,這些都是Nmap特有的,是基於lua語言編寫的

nmap指令碼分類

nmap指令碼主要分為以下幾類,在掃描時可根據需要設定--script=類別這種方式進行比較籠統的掃描:

auth: 負責處理鑑權證書(繞開鑑權)的指令碼  
broadcast: 在區域網內探查更多服務開啟狀況,如dhcp/dns/sqlserver等服務  
brute: 提供暴力破解方式,針對常見的應用如http/snmp等  
default: 使用-sC或-A選項掃描時候預設的指令碼,提供基本指令碼掃描能力  
discovery: 對網路進行更多的資訊,如SMB列舉、SNMP查詢等  
dos: 用於進行拒絕服務攻擊  
exploit: 利用已知的漏洞入侵系統  
external: 利用第三方的資料庫或資源,例如進行whois解析  
fuzzer: 模糊測試的指令碼,傳送異常的包到目標機,探測出潛在漏洞 intrusive: 入侵性的指令碼,此類指令碼可能引發對方的IDS/IPS的記錄或遮蔽  
malware: 探測目標機是否感染了病毒、開啟了後門等資訊  
safe: 此類與intrusive相反,屬於安全性指令碼  
version: 負責增強服務與版本掃描(Version Detection)功能的指令碼  
vuln: 負責檢查目標機是否有常見的漏洞(Vulnerability),如是否有MS08_067

使用方式

  • 指定執行指令碼

當我們使用Nmap並新增引數-sC或者--script時,Nmap會執行NSE指令碼

例如,在script目錄下有http-title.nsehttp-trace.nse指令碼,執行如下命令:

nmap --script http-title scanme.nmap.org
  • 傳遞引數
--script-args=key1=value1,key2=value2... 
  • 從檔案中讀取引數
–script-args-file=filename
  • 顯示全部傳送和收到的資料
--script-trace
  • 更新資料庫

在Nmap的scripts目錄裡有一個script.db,該檔案中儲存了當前Nmap可用的指令碼,類似於一個小型資料庫,如果我們開啟nmap並且呼叫了此引數,則nmap會自行掃描scripts目錄中的擴充套件指令碼,進行資料庫更新

--script-updatedb
  • 指令碼幫助

指定對應的指令碼資訊,Nmap會輸出該指令碼名稱對應的指令碼使用引數,以及詳細介紹資訊。

--script-help=指令碼名稱

nmap指令碼簡介

一個NSE指令碼的書寫一般分為四步:

  • 匯入指令碼編寫所需庫
  • 編寫指令碼描述資訊
  • 確定Rule型別
  • 編寫Action

Nmap nse指令碼模板結構圖

其中Rule存在四種型別:

  • Prerule 在Nmap沒有掃描之前進行觸發
  • Hostrule 在Nmap執行主機發現或探測時進行觸發
  • Protrule 在Nmap執行埠掃描的時候觸發
  • Postrule 在Nmap結束的時候觸發指令碼,通常用於掃描結果的資料提取和整理

用於描述指令碼的觸發規則,返回值只有true和false兩種。返回值決定了後面的action對應的函數是否執行

  • true - 執行
  • false - 不執行

在我看來

Rule描述指令碼的觸發規則,定義了指令碼在nmap生命週期的啥時候來執行

我們選擇 http-php-version.nse指令碼來跟進分析一下,這個指令碼的主要作用是檢測PHP伺服器的版本

檢視API手冊 https://nmap.org/nsedoc/lib/ 來匯入你要匯入的lua指令碼 - nmap內建模組

local http = require "http"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"

local openssl = stdnse.silent_require "openssl"

description對指令碼進行概述

description = [[
Attempts to retrieve the PHP version from a web server. PHP has a number
of magic queries that return images or text that can vary with the PHP
version. This script uses the following queries:
* <code>/?=PHPE9568F36-D428-11d2-A769-00AA001ACF42</code>: gets a GIF logo, which changes on April Fool's Day.
* <code>/?=PHPB8B5F2A0-3C92-11d3-A3A9-4C7B08C10000</code>: gets an HTML credits page.

A list of magic queries is at http://www.0php.com/php_easter_egg.php.
The script also checks if any header field value starts with
<code>"PHP"</code> and reports that value if found.

PHP versions after 5.5.0 do not respond to these queries.

Link:
* http://phpsadness.com/sad/11
]]

作者和指令碼分類

author = {"Ange Gutek", "Rob Nicholls"}
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"discovery", "safe"}

在埠掃描的時候執行判斷函數,這裡直接呼叫了shortport提供的判斷

portrule = shortport.http

然後定義了存取的介面和PHP對應關係

-- These are the magic queries that return fingerprintable data.
local LOGO_QUERY = "/?=PHPE9568F36-D428-11d2-A769-00AA001ACF42"
local CREDITS_QUERY = "/?=PHPB8B5F2A0-3C92-11d3-A3A9-4C7B08C10000"

-- For PHP 5.x hashes up to 5.2.14 and 5.3.3 see:
-- http://seclists.org/nmap-dev/2010/q4/518

local LOGO_HASHES = {
  -- Bunny (Carmella)
  ["37e194b799d4aaff10e39c4e3b2679a2"] = {"5.0.0 - 5.0.3"},
  -- Black Scottish Terrier (Scotch)
  ["4b2c92409cf0bcf465d199e93a15ac3f"] = {"4.3.11", "4.4.0 - 4.4.9", "5.0.4 - 5.0.5", "5.1.0 - 5.1.2"},
  -- Colored
  ["50caaf268b4f3d260d720a1a29c5fe21"] = {"5.1.3 - 5.1.6", "5.2.0 - 5.2.17"},
  -- PHP Code Guy With Breadsticks (Thies C. Arntzen)
  ["85be3b4be7bfe839cbb3b4f2d30ff983"] = {"4.0.0 - 4.2.3"},
  -- Brown Dog In Grass (Nadia)
  ["a57bd73e27be03a62dd6b3e1b537a72c"] = {"4.3.0 - 4.3.11"},
  -- Elephant
  ["fb3bbd9ccc4b3d9e0b3be89c5ff98a14"] = {"5.3.0 - 5.3.29", "5.4.0 - 5.4.45"},
}

local CREDITS_HASHES = {
  ["744aecef04f9ed1bc39ae773c40017d1"] = {"4.0.1pl2", "4.1.0 - 4.1.2", "4.2.2"},
  ["4ba58b973ecde12dafbbd40b54afac43"] = {"4.1.1 OpenVMS"},
  ["8bc001f58bf6c17a67e1ca288cb459cc"] = {"4.2.0 - 4.2.2"},
  ["3422eded2fcceb3c89cabb5156b5d4e2"] = {"4.2.3"},
  ["1e04761e912831dd29b7a98785e7ac61"] = {"4.3.0"},
  ["1e04761e912831dd29b7a98785e7ac61"] = {"4.3.1"},
  ["65eaaaa6c5fdc950e820f9addd514b8b"] = {"4.3.1 Mandrake Linux"},
  ["8a8b4a419103078d82707cf68226a482"] = {"4.3.2"},
  ["22d03c3c0a9cff6d760a4ba63909faea"] = {"4.3.2"}, -- entity encoded "'"
  ["8a4a61f60025b43f11a7c998f02b1902"] = {"4.3.3 - 4.3.5"},
  ["39eda6dfead77a33cc6c63b5eaeda244"] = {"4.3.3 - 4.3.5"}, -- entity encoded "'"
  ["913ec921cf487109084a518f91e70859"] = {"4.3.6 - 4.3.8"},
  ["884ba1f11e0e956c7c3ba64e5e33ee9f"] = {"4.3.6 - 4.3.8"}, -- entity encoded
  ["c5fa6aec2cf0172a5a1df7082335cf9e"] = {"4.3.8 Mandrake Linux"},
  ["8fbf48d5a2a64065fc26db3e890b9871"] = {"4.3.9 - 4.3.11"},
  ["f9b56b361fafd28b668cc3498425a23b"] = {"4.3.9 - 4.3.11"}, -- entity encoded "'"
  ["ddf16ec67e070ec6247ec1908c52377e"] = {"4.4.0"},
  ["3d7612c9927b4c5cfff43efd27b44124"] = {"4.4.0"}, -- entity encoded "'"
  ["55bc081f2d460b8e6eb326a953c0e71e"] = {"4.4.1"},
  ["bed7ceff09e9666d96fdf3518af78e0e"] = {"4.4.2 - 4.4.4"},
  ["692a87ca2c51523c17f597253653c777"] = {"4.4.5 - 4.4.7"},
  ["50ac182f03fc56a719a41fc1786d937d"] = {"4.4.8 - 4.4.9"},
  ["3c31e4674f42a49108b5300f8e73be26"] = {"5.0.0 - 5.0.5"},
  ["e54dbf41d985bfbfa316dba207ad6bce"] = {"5.0.0"},
  ["6be3565cdd38e717e4eb96868d9be141"] = {"5.0.5"},
  ["b7cf53972b35b5d57f12c9d857b6b507"] = {"5.0.5 ActiveScript"},
  ["5518a02af41478cfc492c930ace45ae5"] = {"5.1.0 - 5.1.1"},
  ["6cb0a5ba2d88f9d6c5c9e144dd5941a6"] = {"5.1.2"},
  ["82fa2d6aa15f971f7dadefe4f2ac20e3"] = {"5.1.3 - 5.1.6"},
  ["6a1c211f27330f1ab602c7c574f3a279"] = {"5.2.0"},
  ["d3894e19233d979db07d623f608b6ece"] = {"5.2.1"},
  ["56f9383587ebcc94558e11ec08584f05"] = {"5.2.2"},
  ["c37c96e8728dc959c55219d47f2d543f"] = {"5.2.3 - 5.2.5", "5.2.6RC3"},
  ["1776a7c1b3255b07c6b9f43b9f50f05e"] = {"5.2.6"},
  ["1ffc970c5eae684bebc0e0133c4e1f01"] = {"5.2.7 - 5.2.8"},
  ["54f426521bf61f2d95c8bfaa13857c51"] = {"5.2.9 - 5.2.14"},
  ["adb361b9255c1e5275e5bd6e2907c5fb"] = {"5.2.15 - 5.2.17"},
  ["db23b07a9b426d0d033565b878b1e384"] = {"5.3.0"},
  ["a4c057b11fa0fba98c8e26cd7bb762a8"] = {"5.3.1 - 5.3.2"},
  ["b34501471d51cebafacdd45bf2cd545d"] = {"5.3.3"},
  ["e3b18899d0ffdf8322ed18d7bce3c9a0"] = {"5.3.4 - 5.3.5"},
  ["2e7f5372931a7f6f86786e95871ac947"] = {"5.3.6"},
  ["f1f1f60ac0dcd700a1ad30aa81175d34"] = {"5.3.7 - 5.3.8"},
  ["23f183b78eb4e3ba8b3df13f0a15e5de"] = {"5.3.9 - 5.3.29"},
  ["85da0a620fabe694dab1d55cbf1e24c3"] = {"5.4.0 - 5.4.14"},
  ["ebf6d0333d67af5f80077438c45c8eaa"] = {"5.4.15 - 5.4.45"},
}

最後就是判定的主要邏輯了,雖然我們不懂Lua,但是我們還是能大概看懂這裡做的事情

action = function(host, port)
  local response
  local logo_versions, credits_versions
  local logo_hash, credits_hash
  local header_name, header_value
  local lines

  -- 1st pass : the "special" PHP-logo test
  response = http.get(host, port, LOGO_QUERY)
  if response.body and response.status == 200 then
    logo_hash = stdnse.tohex(openssl.md5(response.body))
    logo_versions = LOGO_HASHES[logo_hash]
  end

  -- 2nd pass : the PHP-credits test
  response = http.get(host, port, CREDITS_QUERY)
  if response.body and response.status == 200 then
    credits_hash = stdnse.tohex(openssl.md5(response.body))
    credits_versions = CREDITS_HASHES[credits_hash]
  end

  for name, value in pairs(response.header) do
    if string.match(value, "^PHP/") then
      header_name = name
      header_value = value
      break
    end
  end

  lines = {}
  if logo_versions then
    lines[#lines + 1] = "Versions from logo query (less accurate): " .. table.concat(logo_versions, ", ")
  elseif logo_hash and nmap.verbosity() >= 2 then
    lines[#lines + 1] = "Logo query returned unknown hash " .. logo_hash
  end
  if credits_versions then
    lines[#lines + 1] = "Versions from credits query (more accurate): " .. table.concat(credits_versions, ", ")
  elseif credits_hash and nmap.verbosity() >= 2 then
    lines[#lines + 1] = "Credits query returned unknown hash " .. credits_hash
  end
  if header_name and header_value then
    lines[#lines + 1] = "Version from header " .. header_name .. ": " .. header_value
  end

  if #lines > 0 then
    return table.concat(lines, "\n")
  end
end

存取可能存在洩露PHP版本的介面,並將返回值進行md5計算,檢查字典裡是否有對應值,如果有的話就說明存在PHP的版本洩露,最後返回結果。

事實上只是實現的語言不一樣,核心的檢測原理都是一致的

按照應用服務掃描

nmap指令碼的完整檔案可以看官方檔案 https://nmap.org/nsedoc/scripts/

下面是常用的一些應用掃描

VNC掃描

VNC (Virtual Network Console)是虛擬網路控制檯的縮寫。它 是一款優秀的遠端控制工具軟體

  • 檢查vnc bypass
nmap  --script=realvnc-auth-bypass 192.168.137.4
  • 檢查vnc認證方式
nmap  --script=vnc-auth  192.168.137.4  
  • 獲取vnc資訊
nmap  --script=vnc-info  192.168.137.4  

smb掃描

SMB(全稱是Server Message Block)是一個網路協定名,它能被用於Web連線和使用者端與伺服器之間的資訊溝通

  • smb破解
nmap  --script=smb-brute.nse 192.168.137.4  
  • smb字典破解
nmap --script=smb-brute.nse --script-args=userdb=/var/passwd,passdb=/var/passwd 192.168.137.4
  • smb漏洞檢測
nmap  --script=smb-check-vulns.nse --script-args=unsafe=1 192.168.137.4

Mssql掃描

  • 暴力破解使用者名稱密碼
nmap -p1433 --script=ms-sql-brute --script-args=userdb=/var/passwd,passdb=/var/passwd 192.168.137.4 
  • 有密碼後xp_cmdshell執行命令
nmap -p 1433 --script ms-sql-xp-cmdshell --script-args mssql.username=sa,mssql.password=sa,ms-sql-xp-cmdshell.cmd="net user" 192.168.137.4  
  • 有密碼後dymphash
nmap -p 1433 --script ms-sql-dump-hashes.nse --script-args mssql.username=sa,mssql.password=sa  192.168.137.4  

Mysql掃描

  • 掃描root空口令
nmap -p3306 --script=mysql-empty-password.nse 192.168.137.4   
  • 列舉所有mysql使用者
nmap -p3306 --script=mysql-users.nse --script-args=mysqluser=root 192.168.137.4 
  • 掃描mysql相關的指令碼
nmap --script=mysql-* 192.168.137.4  

Oracle掃描

  • oracle sid 掃描
nmap --script=oracle-sid-brute -p 1521-1560 192.168.137.5 
  • oracle弱口令破解
nmap --script oracle-brute -p 1521 --script-args oracle-brute.sid=ORCL,userdb=/var/passwd,passdb=/var/passwd 192.168.137.5 

如果想要了解更多的指令碼內容,移步官方檔案 -> https://nmap.org/nsedoc/scripts/

參考連結

END

建了一個微信的安全交流群,歡迎新增我微信備註進群,一起來聊天吹水哇,以及一個會發布安全相關內容的公眾號,歡迎關注