Nmap腳本引擎原理

Nmap腳本引擎原理

1、NSE介紹

  雖然Nmap內嵌的服務於版本探測已足夠強大,可是在某些狀況下咱們須要多倫次的交互纔可以探測到服務器的信息,這時候就須要本身編寫NSE插件實現這個功能。NSE插件可以完成網絡發現、複雜版本探測、脆弱性探測、簡單漏洞利用等功能。html

  轉載請註明出處:http://www.cnblogs.com/liun1994/前端

  在文章   "Nmap腳本文件分析(AMQP協議爲例)"   中會將版本探測NSE腳本原理聯繫起來,具體分析Nmap探測的執行過程。
api

  在文章  " 編寫本身的NSE腳本"  中將以一個簡單的例子說明NSE的編寫。
安全

腳本掃描經過選項被激活 -sC: 使通用scripts生效 --script: 指定本身的腳本文件 --script-trace: 查看腳本執行過程
-A: 同時進行版本探測和腳本掃描 爲了避免進行主機發現也不進行端口掃描,直接使用自定義的腳本探測。可使用下面的選項: -Pn -sn --script

--script-args和--script-args-file,指定腳本要讀入的參數
nmap --script snmp-sysdescr --script-args snmpcommunity=admin example.com

nmap --script default,safe 加載default和safe類腳本。
參考:
https://nmap.org/book/nse-usage.html#nse-categories

  腳本文件分類:brute、default、dos、safe、exploit等,具體的可查看:https://nmap.org/book/nse-usage.html#nse-categories服務器

  根據腳本的運行階段不一樣分爲四類:Prerule scripts、Host scripts、Service scripts、Postrule scripts。
網絡

  應用最多的就是Service scripts,Post scripts對Nmap輸出進行格式化輸出,Host scripts這個階段在Nmap運行完主機發現、端口掃描、版本探測、OS偵測後執行;Prerule scripts執行一些資源操做,先於各探測執行。
session

2、Nmap執行原理圖

  

  由上圖能夠看到四類腳本的運行階段,以及他們的功能。數據結構

  1)在nmap_main裏面,調用init_main()進行詳細的初始化過程,加載Lua標準庫與Nmap擴展庫,準備參數環境,加載並執行nse_main.lua文件;這個文件加載用戶選擇的腳本文件,執行完以後返回函數對象給init_main(),被保存到Lua註冊表中。ssh

  2)在nse_main.lua中,定義兩個核心的類,Script和Thread,Script用於管理NSE腳本,當新的腳本被加載時,調用 Script.new建立腳本對象,該對象被保存下來在後續的掃描過程當中使用;Thread用於管理腳本的執行,該類中也包含對腳本健全性的檢查。在腳本執行時,若是腳本之間存在依賴關係,那麼會將基礎的無依賴的腳本統一執行完畢,再執行依賴性的腳本。socket

  3)執行腳本掃描時,從nmap_main()中調用script_scan()函數。在進入script_scan()後,會標記掃描階段類型,而後進入到初始化階段返回的main()函數(來自nse_main.lua腳本中的main)中,在函數中解析具體的掃描類型。

  4)main()函數負責處理三種類型的腳本掃描:預掃描(SCRIPT_PRE_SCAN)、腳本掃描(SCRIPT_SCAN)、後掃描 (SCRIPT_POST_SCAN)。預掃描即在Nmap調用的最前面(沒有進行主機發現、端口掃描等操做)執行的腳本掃描,一般該類掃描用於準備基本的信息,例如到第三服務器查詢相關的DNS信息。而腳本掃描,是使用NSE腳原本掃描目標主機,這是最核心的掃描方式。後掃描,是整個掃描結束後,作一些善後處理的腳本,好比優化整理某些掃描。

  5)在main()函數中核心操做由run函數負責。而run()函數的自己設計用於執行全部同一級別的腳本(根據依賴關係劃分的級別),直到全部線程執行完畢才退出。run()函數中實現三個隊列:執行隊列(Running Queue)、等待隊列(Waiting Queue)、掛起隊列(Pending Queue),並管理三個隊列中線程的切換,直到所有隊列爲空或出錯而退出。

3、Nmap API

  數據傳遞

  nmap.luadoc是與nmap內部函數交互和數據結構化的API,API提供目標主機的詳細信息例如端口狀態和版本探測結果;同時API也提供與Nsock交互的接口,這樣方便咱們本身寫NSE腳本與服務器交互,目前文件中共48個函數。

  在腳本引擎中,用戶能夠輕鬆訪問Nmap已經瞭解的有關目標主機的信息。該數據做爲參數傳遞給NSE腳本的action方法,參數host和port是lua表,其中包含腳本執行的目標的信息。下面介紹每一個表裏面所含有的變量。

  nmap使用註冊表來共享信息,每一個腳本之間共享nmap.registry每一個主機還有本身的註冊表名爲host.registry在整個掃描會話中,全局註冊表始終存在。腳本可使用它,例如,存儲稍後將由postrule腳本顯示的值。機註冊表僅在主機被掃描時存在。它們能夠用於將信息從一個腳本發送到另外一個腳本。例如:1)ssh-hostkey腳本的portrule收集SSH密鑰指紋,並將其存儲在全局nmap.registry中,以便稍後能夠由postrule打印。2)ssl-cert腳本收集SSL證書並將其存儲在每一個主機註冊表中,以便ssl-google-cert-catalog腳本可使用它們,而無需再次鏈接到服務器。nmap.registry是全局的,所以key選擇很重要;使用另外一個腳本結果的腳本必須使用dependencies變量來聲明它,以確保先前的腳本首先運行。

host.ip host.name 經過dns反向查詢的主機名,若是沒查到則爲空串 host.targetname host.reason: 給出host處於如今這個狀態的解釋 host.reason_ttl host.mac_addr host.directly_connected host.mac_addr_next_hop host.mac_addr_src host.interface host.interface_mtu host.bin_ip host.bin_ip_src host.times host.traceroute port.number port.protocol: 有效值爲tcp、udp port.service: 字符串表示的運行在端口號上的服務,該服務由服務探測階段探測出,若是 port.version.service_dtype字段是table,那麼Nmap基於端口號猜想服務;若是不是table,那麼版本探測階段可以肯定是什麼服務,這個字段的值被設定爲port.version.name port.reason: 字符串解釋處於port.state狀態的緣由。 port.reason_ttl port.version: 這個字段是一個表格,包含版本探測階段返回的所有信息,包括:name;name_confidence;等,具體可參考官方文檔版本探測章節。 port.state

參考:https://nmap.org/book/nse-api.htm

  Network I/O API

require("nmap") -- 簡單的使用Nsock鏈接服務器的例子 local socket = nmap.new_socket() socket:set_timeout(1000) try = nmap.new_try(function() socket:close() end) try(socket:connect(host.ip, port.number)) try(socket:send("login")) response = try(socket:receive()) socket:close()

除此以外還有receive_bytes方法,receive_lines方法,receive_buf方法,可查看nmap.luadoc文件
除了Network的方式還有行包鏈接的方式:參考https://nmap.org/book/nse-api.html

4、編寫本身的腳本的建議

  編寫NSE腳本,須要根據Nmap規範編寫description,author,license,categories,rule,action字段的內容,其中主要是action字段的編寫;若是rule函數返回結果爲真,那麼執行編寫的action函數。

  The Rule

  這部分決定是否執行action函數,A prerule or a postrule 類型的腳本老是執行;在端口規則腳本里面,NSE僅給咱們當前掃描端口的信息,好比一個腳本要執行,可是必須保證當前端口開啓而且113端口開啓,爲了檢測113端口是否開啓,咱們使用nmap.get_port_state這個函數,若是113端口沒有被掃描,函數將返回nil。

portrule = function(host, port) local auth_port = { number=113, protocol="tcp" } local identd = nmap.get_port_state(host, auth_port) return identd ~= nil
        and identd.state == "open"
        and port.protocol == "tcp"
        and port.state == "open"
end

  The Action

  腳本首先鏈接到咱們探測的端口,經過調用nmap.new_socket建立兩個套接字選項。接下來,咱們定義一個錯誤處理捕獲功能,若是檢測到故障,則關閉這些套接字。此時咱們能夠安全地使用諸如打開,關閉,發送和接收的對象方法來在網絡套接字上操做。在這種狀況下,咱們調用connect來創建鏈接。 NSE的異常處理機制用於避免過多的錯誤處理代碼。 try用來包圍可能出錯的代碼,若是有任何問題,這將調用catch函數。若是兩個鏈接成功,咱們構造一個查詢字符串並解析響應,最後返回解析結果。

 

action = function(host, port) local owner = ""
        local client_ident = nmap.new_socket() local client_service = nmap.new_socket() local catch = function() client_ident:close() client_service:close() end
        local try = nmap.new_try(catch) try(client_ident:connect(host.ip, 113)) try(client_service:connect(host.ip, port.number)) local localip, localport, remoteip, remoteport = try(client_service:get_info()) local request = port.number .. ", " .. localport .. "\r\n" try(client_ident:send(request)) owner = try(client_ident:receive_lines(1)) if string.match(owner, "ERROR") then owner = nil
        else owner = string.match(owner, "%d+%s*,%s*%d+%s*:%s*USERID%s*:%s*.+%s*:%s*(.+)\r?\n") end try(client_ident:close()) try(client_service:close()) return owner end

5、參考文獻

  https://nmap.org/changelog.html#7.50       Nmap各版本更新內容

   https://nmap.org/book/nse-tutorial.html      NSE編寫指南

   https://nmap.org/book/nse-api.html           Nmap API

   https://nmap.org/                                       Nmap官網

相關文章
相關標籤/搜索