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

2013年2月11日の日記を書いたあとも、Nmap の NSE スクリプトupnp-info」を使い、「upnp.lua*1」(「upnp-info」が読み込む lua スクリプト)で次の 2 点を変更しました。このとき「upnp-info」を実行すると、次のような結果が得られます。この日記では、この 2 点の変更についてメモします。

  1. HTTP レスポンスから Location ヘッダ の URL を抽出する正規表現
  2. 取得した Device description の解析処理
$>nmap -sU -p1900 --script=upnp-info -n 192.168.0.106

Starting Nmap 6.25 ( http://nmap.org ) at 2013-03-03 23:35 東京 (標準時)
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
|         Name: nasne
|         Manufacturer: Sony Computer Entertainment Inc.
|         Model Descr: nasne
|         Model Name: nasne
|         serviceId: urn:upnp-org:serviceId:ConnectionManager
|           SCPDURL   : http://192.168.0.106:58888/MediaServer_ConnectionManager/scpd.xml
|           controlURL: http://192.168.0.106:58888/MediaServer_ConnectionManager/control
|         serviceId: urn:upnp-org:serviceId:ContentDirectory
|           SCPDURL   : http://192.168.0.106:58888/MediaServer_ContentDirectory/scpd.xml
|           controlURL: http://192.168.0.106:58888/MediaServer_ContentDirectory/control
|         serviceId: urn:s-bras-org:serviceId:X_PvrControl
|           SCPDURL   : http://192.168.0.106:64230/X_PvrControl.xml
|           controlURL: http://192.168.0.106:64230/X_PvrControl
|         serviceId: urn:xsrs-org:serviceId:X_ScheduledRecording
|           SCPDURL   : http://192.168.0.106:64230/XSRS.xml
|_          controlURL: http://192.168.0.106:64230/XSRS
MAC Address: 12:34:56:78:9a:bc (XXXXXXXXXXXXXXXX)

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

HTTP レスポンスから Location ヘッダ の URL を抽出する正規表現

2013年2月11日の日記を書いたときには、nasne に対する「upnp-info」の実行結果に Device description*2 の friendlyName 要素や manufacturer 要素が出力されていませんでした。他の機器に実行した場合、それらが出力されていました。
そこで、nasne に対する「upnp-info」が送信するパケットをキャプチャして確認したところ、GET リクエストの URI が「0x2f 0x0d」(/)となっていました。このため、nasne の 58888/tcp から HTTP レスポンスコード 404(Not Found)が応答され、「Device description」が取得できていなかったと判断しました。
upnp.lua」を調べてみると、Location ヘッダから URL を取得するときに「0x0d」を含む文字列(改行コードとして「0x0a」だけを含まない文字列)に一致する正規表現が書かれており、この部分に問題がありました。
この問題を修正するため、次のようなパッチを作成してみました。改めて読み返すと適当すぎますね...この正規表現(x。このパッチをメーリングリスト「nmap-dev」に投稿したところ、(きちんとした正規表現に修正されたうえで)upnp.lua反映されたようです:)

--- upnp.lua	2013-02-16 17:49:50.745090000 +0900
+++ upnp_edited.lua	2013-02-16 18:28:38.307590000 +0900
@@ -183,7 +183,7 @@
 			local server, location
 			server = string.match(response, "[Ss][Ee][Rr][Vv][Ee][Rr]:%s*(.-)\010")
 			if server ~= nil then table.insert(output, "Server: " .. server ) end
-			location = string.match(response, "[Ll][Oo][Cc][Aa][Tt][Ii][Oo][Nn]:%s*(.-)\010")
+			location = string.match(response, "[Ll][Oo][Cc][Aa][Tt][Ii][Oo][Nn]:%s*(.-)[\010\013]+")
 			if location ~= nil then
 				table.insert(output, "Location: " .. location )
 			

取得した Device description の解析処理

どうせなら Service description*3 の URL(SCPDURL 要素)なども出力したいと思い、さらに次のようなパッチを作成しました。このパッチを upnp.lua に適用すると、冒頭にある出力結果が得られます。

--- upnp.lua	2012-11-30 04:39:54.000000000 +0900
+++ upnp-edited.lua	2013-03-12 23:40:17.140625000 +0900
@@ -183,7 +183,7 @@
 			local server, location
 			server = string.match(response, "[Ss][Ee][Rr][Vv][Ee][Rr]:%s*(.-)\010")
 			if server ~= nil then table.insert(output, "Server: " .. server ) end
-			location = string.match(response, "[Ll][Oo][Cc][Aa][Tt][Ii][Oo][Nn]:%s*(.-)\010")
+			location = string.match(response, "[Ll][Oo][Cc][Aa][Tt][Ii][Oo][Nn]:%s*(.-)[\010\013]+")
 			if location ~= nil then
 				table.insert(output, "Location: " .. location )
 			
@@ -271,6 +271,19 @@
 				if nm ~= nil then table.insert(output,"Model Name: " .. nm) end
 				if ver ~= nil then table.insert(output,"Model Version: " .. ver) end
 			end
+
+			for service in string.gmatch(response['body'], "<service>(.-)</service>") do
+				local serviceId, SCPDURL, controlURL
+
+				serviceId = string.match(service, "<serviceId>(.-)</serviceId>")
+				SCPDURL = string.match(service, "<SCPDURL>(.-)</SCPDURL>")
+				controlURL = string.match(service, "<controlURL>(.-)</controlURL>")
+
+				if serviceId ~= nil then table.insert(output, "serviceId: " .. serviceId) end
+				if SCPDURL ~= nil then table.insert(output, "  SCPDURL   : " .. SCPDURL) end
+				if controlURL ~= nil then table.insert(output, "  controlURL: " .. controlURL) end
+
+			end
 			return true, output
 		else
 			return false, "Could not retrieve XML file"

*1:Nmap インストールディレクトリ\nselib\ 以下にあります。

*2:pp.43-48 2.3 Device description, "UPnP Device Architecture 1.1"

*3:pp.48-55 2.5 Service description, "UPnP Device Architecture 1.1"