NmapのNSEスクリプト「upnp-info」を使ってみた

Nmap の NSE(Nmap Scripting Engine)スクリプトupnp-info」を使ってみたところ、使用前に思っていた実行結果と異なっていたため、スクリプトを一部編集してみました。この日記では、「upnp-info」の実行結果と編集したスクリプトについて書きます。

upnp-info」の実行結果

まず「upnp-info」の実行環境は下表の通りです。

IPアドレス バイス 説明
192.168.0.101 Windows XP SP3 マシン upnp-info」を実行するデバイス
192.168.0.106 nasne upnp-info」の実行対象デバイス


この実行環境で「upnp-info」を実行してみると、nasneUPnP に関する情報が実行結果に出力されませんでした。なお、Nmap の実行結果のうち、MAC アドレスおよび OUI 情報については「12:34:56:78:9a:bc」、「XXXXXXXXXXXXXXXX」に置換しています。

$>nmap -V

Nmap version 6.25 ( http://nmap.org )
Platform: i686-pc-windows-windows
Compiled with: nmap-liblua-5.2.1 openssl-1.0.1c nmap-libpcre-7.6 libpcap-4.1.2 nmap-libdnet-1.12 ipv6
Compiled without:
Available nsock engines: select

$>nmap -sU -p1900 --script=upnp-info -n 192.168.0.106

Starting Nmap 6.25 ( http://nmap.org ) at 2013-02-10 22:40 東京 (標準時)
Nmap scan report for 192.168.0.106
Host is up (0.00s latency).
PORT     STATE         SERVICE
1900/udp open|filtered upnp
MAC Address: 12:34:56:78:9a:bc (XXXXXXXXXXXXXXXX)

Nmap done: 1 IP address (1 host up) scanned in 6.92 seconds


一方で、UPnP を実装しているデバイスを探す NSE スクリプトbroadcast-upnp-info」の実行結果には、nasneUPnP に関する情報が出力されました。「upnp-info」を使用する前には、このような出力を思い描いていました。

$>nmap -sU -p1900 --script=broadcast-upnp-info -n 192.168.0.106

Starting Nmap 6.25 ( http://nmap.org ) at 2013-02-10 23:37 東京 (標準時)
Pre-scan script results:
broadcast-upnp-info:
192.168.0.106
Server: Linux/2.6 UPnP/1.0 nasne/1.0
Location: http://192.168.0.106:58888/
_ Webserver: Linux/2.6 UPnP/1.0 nasne/1.0
Nmap scan report for 192.168.0.106 Host is up (0.00s latency). PORT STATE SERVICE 1900/udp open|filtered upnp MAC Address: 12:34:56:78:9a:bc (XXXXXXXXXXXXXXXX) Nmap done: 1 IP address (1 host up) scanned in 8.86 seconds


upnp-info」と「broadcast-upnp-info」のパケットキャプチャデータを確認したところ、UPnP の M-SEARCH リクエスト(後述)の送信方法に違いがありました。「upnp-info」の場合、この違いによって nasne が M-SEARCH リクエストに応答していないと考えました。

スクリプト実行時に送受信されるパケット

upnp-info」の場合、Nmap で指定した 192.168.0.106 に M-SEARCH リクエストを送信していましたが、「broadcast-upnp-info」の場合、マルチキャストアドレス 239.255.255.250 に送信していました。

  • upnp-info」のパケットキャプチャデータ(CloudShark
    • パケットキャプチャデータ No.3 を参照
  • 「broadcast-upnp-info」のパケットキャプチャデータ(CloudShark
    • パケットキャプチャデータ No.1 を参照

upnp-info」および「broadcast-upnp-info」ともに、下記の M-SEARCH リクエストを送信します。それぞれのパケットキャプチャデータを確認すると、宛先 IP アドレスおよび宛先 MAC アドレスが異なること*1が分かります。なお、「192.168.0.101」および「192.168.0.106」の MAC アドレスについては、それぞれ「bc:9a:78:56:34:12」、「12:34:56:78:9a:bc」に置換しています。

M-SEARCH * HTTP/1.1
Host:239.255.255.250:1900
ST:upnp:rootdevice
Man:"ssdp:discover"
MX:3

UPnP における M-SEARCH リクエス

UPnP Device Architecture 1.1」の「1.3 Search」では、M-SEARCH リクエストが「ネットワーク上の UPnP を実装しているデバイスを探すリクエスト」である旨が記述されています。この仕様書によると、マルチキャストアドレス宛てにM-SEARCH リクエストを送信する以外にも、ユニキャストアドレス宛てに送信できます*2
upnp-info」の場合、192.168.0.106 宛てにも関わらず、Host ヘッダを「239.255.255.250:1900」としていることが原因かと推測しましたが、違いました。Host ヘッダを「192.168.0.106:1900」に修正して ncatで送信してみましたが*3、やはり応答がありませんでした。結論としては、UPnP を実装したデバイスの中には、ユニキャスト宛て M-SEARCH リクエストに応答しないものもある、と理解しました。

upnp-info」の編集

「broadcast-upnp-info」スクリプト broadcast-upnp-info.luaupnp.lua を読みながら、「upnp-info」スクリプト upnp-info.lua を次のように編集してみました。

  • M-SEARCH リクエストをマルチキャストアドレス宛てに送信する
  • M-SEARCH リクエストに対する応答から対象 IP アドレスの結果のみ抽出する

編集した upnp-info.lua は下記の通りとなります。僕が追加したコードは「-----追加 ここから」と「-----追加 ここまで」に囲まれたコード部分です。M-SEARCH リクエストに対して複数の応答が得られる環境で、簡単な動作確認のみ実施しています。

local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local upnp = require "upnp"
-----追加 ここから
local table = require "table"
-----追加 ここまで

description = [[
Attempts to extract system information from the UPnP service.
]]

---
-- @output
-- |  upnp-info:  System/1.0 UPnP/1.0 IGD/1.0
-- |_ Location: http://192.168.1.1:80/UPnP/IGD.xml
--
-- @args upnp-info.override Controls whether we override the IP address information
--                          returned by the UPNP service for the location of the XML
--                          file that describes the device.  Defaults to true for 
--                          unicast hosts.

-- 2010-10-05 - add prerule support <patrik@cqure.net>
-- 2010-10-10 - add newtarget support <patrik@cqure.net>
-- 2010-10-29 - factored out all of the code to upnp.lua <patrik@cqure.net>
-- 2013-02-11 - call helper:setMulticast(true), and filter only result of target ip <kaito834@gmail.com>

author = "Thomas Buchanan"

license = "Same as Nmap--See http://nmap.org/book/man-legal.html"

categories = {"default", "discovery", "safe"}


---
-- Runs on UDP port 1900
portrule = shortport.portnumber(1900, "udp", {"open", "open|filtered"})

---
-- Sends UPnP discovery packet to host, 
-- and extracts service information from results
action = function(host, port)
	local override = stdnse.get_script_args("upnp-info.override")
	local helper = upnp.Helper:new( host, port )
	if ( override ~= nil ) and ( string.lower(override) == "false" ) then
		helper:setOverride( false )
	else
		helper:setOverride( true )
	end
	-----追加 ここから
	helper:setMulticast(true)
	-----追加 ここまで
	local status, result = helper:queryServices()

	-----追加 ここから
	for key, value in ipairs(result) do
		if(not(value['name'] == host['ip'])) then
			table.remove(result, key)
		end
	end
	-----追加 ここまで

	if ( status ) then
		nmap.set_port_state(host, port, "open")
		return stdnse.format_output(true, result)
	end
end


編集した「upnp-info」を実行すると、下記のように「broadcast-upnp-info」と同様の出力が得られました。

$>nmap -sU -p1900 --script=upnp-info -n 192.168.0.106

Starting Nmap 6.25 ( http://nmap.org ) at 2013-02-11 09:46 東京 (標準時)
Nmap scan report for 192.168.0.106
Host is up (0.00s latency).
PORT     STATE SERVICE
1900/udp open  upnp
upnp-info:
192.168.0.106
Server: Linux/2.6 UPnP/1.0 nasne/1.0
Location: http://192.168.0.106:58888/
_ Webserver: Linux/2.6 UPnP/1.0 nasne/1.0
MAC Address: 12:34:56:78:9a:bc (XXXXXXXXXXXXXXXX) Nmap done: 1 IP address (1 host up) scanned in 8.11 seconds

*1:「broadcast-upnp-info」では、宛先 MAC アドレスが 01:00:5e:7f:ff:fa となります。参考:http://d.hatena.ne.jp/naablaa/20061129#p1

*2:「Control points can also send a unicast search message to a known IP address...」という記述が該当します

*3:ncat -u --send-only 239.255.255.250 1900 < msearch.txt