rabbit51

it's since Nov.30 2005
May.29 2014, transferred from broach

ヤマハNVR500のL2TP/IPsec VPNパケットをWiresharkで解析

2019-03-15 12:00:00 | NVR500
ヤマハNVR500にL2TP/IPsec VPN機能が追加されて約1年半が経った。利用しているISP(ぷらら)のIPv6 IPoE接続サービスが開始され快適なIPv6環境となった。IPv6経由のIKEv2/IPsec VPN接続環境も出来、IPv6環境でのMTUも確認した。MTU確認時にぷららIPv4 PPPoE接続のNVR500 L2TP/IPsec VPNパケットを解析した。NVR500のログから解析用の鍵をLUAスクリプトで抽出したのでメモしておく。


(1)NVR500でISAKMPとESP用鍵を記録する
L2TP/IPsec接続中に「show ipsec sa」でgateway_idを確認し「show ipsec sa gateway gateway_id detail」で鍵が表示されるらしい。iPhone(iOS 12.1.4)でAeon mobile(IIJ/Docomo)経由でL2TP/IPsec VPN接続し、sshで接続して確認してみる。

「Key: ** ** ** ** ** (confidential) ** ** ** ** **」?
「administrator」なのですが。。。
正当な表示者としての「秘密の合言葉(option argument)」でもあるのでしょうか?
Google調査してみるが、記述を見つけられない。。。
思い直して「シリアルコンソール」から表示してみると

コピー&ペーストだと手が掛かるので、シリアルコンソールから
「show ipsec sa gateway gateway_id detail > sd1:/local/ipsec-keys.txt」で書き出してみる。
おいおい、また、「Key: ** ** ** ** ** (confidential) ** ** ** ** **」!
「administrator」として認められていない。。。悲しい。。。
YAMAHA RTシリーズのFAQ / IPsec&IKE&VPNの「トラブルシューティング」「どうやって調べたらよいですか?」「 ipsec ike logコマンド」 に従い、「ipsec ike log gateway_id key-info」を設定し、「syslog debug on」。
iPhone(iOS12.1.4)は、ヤマハの動作確認済みクライアント一覧に従いAES256-CBC/SHA-HMACに設定し接続。

「show ipsec sa gateway gateway_id detail」の結果
このデータは、「show ipsec sa gateway gateway_id detail > ipsec-sa.txt」でファイル化出来ない.
copy&pastである。以下のログデータも含め「AES-CBC/SHA-HMAC」で接続した時のデータである。

ISAKMP部では、AES256-CBC/SHA256-HMACで処理が行われている。
Diffie-Hellman値は、modp2048が選択される。
ESP部は、指定されたAES-CBC/SHA-HMACが選択される。ISAKMP部でSHA256が扱えて、ESP部でSHA256が扱えない不思議。
Remote IDは、接続元のグローバルIPv4アドレス。
Local IDは、NAT後のイントラネット側IPv4アドレス。
ISAKMP部のSPIは、CKY-Iの8バイトとCKY-Rの8バイトを結合した16バイトの値が表示されている。Keyは、SKEYID_eの32バイトから先頭8バイトを表示。
ESP部のSPIは、SPIの8バイト。Keyは、key material (first 16 bytes)の16バイトを表示。AES256-CBCが選択されていてもKey materialは、16バイトしか表示されない。

このコマンド結果は、セキュリティ対策や暗号化方式の追加変更に伴い変更が十分な検討が行われていないように見受けられる。
AES256-CBC/SHA-HMAC接続時のデータ



ISAKMP部

NVR500のL2TP/IPsecのISAKMPは、IKEv1との事で、wiresharkが必要とする「CKY-I」値8バイトと「SKEYID_e」値32バイトの鍵データ(256bits)を取り出す。
AES256-CBC/SHA-HMAC接続時のデータ


ESP部

「Local ID -> Remote ID」(send)送信方向のSA情報(responder)としてSPI値4バイト。暗号化方式AES256-CBCの鍵「Key material」値32バイト(256bits)のはずだが、「(first 16 bytes)」って。。。。128bitsだがどうするの。。。
「Remote ID -> Local ID」(receive)受信方向のSA情報(initiator)としてSPI値4バイト。Key material値32バイトも「(first 16 bytes)」。。。
これでは、wiresharkで復号出来ない。
シリアルコンソールの「show ipsec sa gateway gateway_id detail」もAES256-CBCの鍵長16バイト。。。

認証確認用鍵は、「(first 8 bytes)」(64 bits)がログされている。SHA-HMACは、20 bytes(160 bits)で計算し、96 bitsが使用される。160 bitsに満たない鍵は、0x00で補完される。8 bytes(64 bits)で設定したが認証確認できないので鍵長不足と判断。このデータは使用しない。ただし、Wiresharkの「esp_sa」設定で「Authentication」が「NULL」指定だと認証確認データ長が確定出来ないようで、ESP復号がうまくいかない。「HMAC-SHA-1-96 [RFC2404]」を指定する。
AES256-CBC/SHA-HMAC接続時のデータ



L2TP/PPP部

L2TP/IPsec VPN接続が完了しても「Local ID」用のグローバルIPv4アドレスがログに記録されていない。
「show status pp 1」として「ぷららISP」へのPPPoE接続情報から得るしかない。
「Remote ID」用のグローバルIPv4アドレスは、ESP部の「respond IPsec phase to 163.49.207.102」ログで取得。

(2)鍵長対策
消極的対策として、「wiresharkパケット解析」するときだけ暗号化鍵長を短くする。。。
「詳細設定と情報」「VPN接続の設定」「VPN接続設定の修正(Anonymous)]」「VPN接続設定の 修正」「VPN Anonymous 接続 共通設定」で「暗号アルゴリズム=AES-CBC」とする。
または、「詳細設定と情報」「コマンドの実行」で「ipsec sa policy 2 2 esp aes-cbc sha-hmac」とする。
解析作業終了後は、「ipsec sa policy 2 2 esp aes256-cbc sha-hmac」で戻す。

(3)鍵抽出用LUAスクリプト
NVR500の外部メモリSD1をファイル共有設定している。
ここにLUAスクリプトを置き、wireshark用のISAKMP及びESP鍵ファイルを出力する。
Windows10からは、利用できない(レジストリ設定で利用できるらしい)が、Windows7あるいは、macOS(10.12.6)から利用できる。macOSでは、ファインダーで「移動」「サーバへ接続」「cifs://nvr500.familyname/local」

スクリプトは、rt.command("show log")でlogData変数にログを取込み解析する予定であったが、エラーとなる。そこで、一度ファイルに書き出し、再度変数に読み込む事で回避した。たまに、ファイル書き出しが終わる前に読み込みが実行されるとエラーになるので、rt.sleep()で時間調整が必要になる事もある。適宜対応する。
-------- GetNVR500ISAKMPESPKey.lua --------
--[[
	Mar.8 '2019 Rabbit51
	GetNvr500ISAKMPESPKey.lua
]]
logTmp = "sd1:/local/GetNvr500ESPlog.txt"
espsapath = "sd1:/local/"
local logData, iip, rip, ispi, rspi, ieky, reky, dt, r, m
local icky, isakmpkey
local sendconf, receiveconf

function loadLogFile(logfile)
	local handle = io.open(logfile,"r")
	local content=handle:read("*all")
	handle:close()
	return content
end

if arg[1] ~= nil then
    logData = loadLogFile(arg[1])
else
    r,m = rt.command("delete logTmp")
   if r == nil then rt.syslog("info","[LUA] "..arg[0].." "..m ) end
    r,m = rt.command("show log | grep 'IKE|L2TP|ANONYMOUS01' > "..logTmp)
    -- logファイルが生成される前にloadLogFile()が実行される場合にsleep()で時間調整
    -- rt.sleep(6)
    if r ~= nil then
        logData = loadLogFile(logTmp)
    else
        rt.syslog("info","[LUA] "..arg[0].." show log command error" )
		return r,m
    end
end

print("now matching...\r\f")

-- ISAKMP
local tcky, eky1, eky2
for tcky,eky1,eky2 in string.gmatch(logData, "%[IKE%] CKY%-I%s.-%[IKE%]   ([%w ]+)%s.-%[IKE%] SKEYID_e%s.-%[IKE%]   ([%w ]+)%s.-%[IKE%]   ([%w ]+)") do
	if tcky ~= nil then
		icky = string.gsub(tcky," ","")
		isakmpkey = string.gsub(eky1," ","")..string.gsub(eky2," ","")
	end
end

-- find last IKEv1 connection and get that global ip address and date
local pos = 1
while true do
	local pbt,pet,iipt,dtt = string.find(logData, "%[IKE%] respond IPsec phase to ([%d%.]+)%s-([%d/: ]+): ", pos)
	if pbt ~= nil then
		pb=pbt
		pe=pet
		iip = iipt
		dt = dtt
		pos = pet
	else
		break
	end
end
dt = string.gsub(dt,"[/: ]","")

-- get the SPI and the encryption key of Responder
local spi,ekey
pb,pe,spi,ekey = string.find(logData,"%[IKE%] SPI%s.-%[IKE%]   ([%w ]+).-%[IKE%] key material %(first 16byte%)%s.-%[IKE%]   ([%w ]+)",pe)
if pb ~= nil then
	rspi = spi
	rekey = ekey
	pos = pe
else
	rt.syslog("info","[LUA] "..arg[0].." No Responder SA...")
end
-- get the SPI and the encryption key of Initiator
pb,pe,spi,ekey = string.find(logData,"%[IKE%] SPI%s.-%[IKE%]   ([%w ]+).-%[IKE%] key material %(first 16byte%)%s.-%[IKE%]   ([%w ]+)",pe)
if pb ~= nil then
	ispi = spi
	iekey = ekey
else
	rt.syslog("info","[LUA] "..arg[0].." No Initiator SA...")
end
rspi=string.gsub(rspi," ","")
ispi=string.gsub(ispi," ","")
rekey=string.gsub(rekey," ","")
iekey=string.gsub(iekey," ","")

-- Get the Global IP Address of Responder
r,m = rt.command("show status pp 1")
if r == true then
	rip=string.match(m,"PP IP Address Local: ([%d%.]+),")
else
	rt.syslog("info","[LUA] "..arg[0].." "..m)
end

sendconf=string.format("%s,%s\n",icky, isakmpkey)
local fh,estr,ecode = io.open(espsapath..dt.."ikev1_decryption_table","w")
if fh == nil then
	fh:close()
	return nil,estr,ecode
else
	fh:write(sendconf)
	fh:close()
end

sendconf=string.format("%q,%q,%q,%q,%q,%q,%q,%q\n","IPv4",rip,iip,"0x"..rspi,"AES-CBC [RFC3602]","0x"..rekey,"HMAC-SHA-1-96 [RFC2404]","")
receiveconf=string.format("%q,%q,%q,%q,%q,%q,%q,%q\n","IPv4",iip,rip,"0x"..ispi,"AES-CBC [RFC3602]","0x"..iekey,"HMAC-SHA-1-96 [RFC2404]","")
fh,estr,ecode = io.open(espsapath..dt.."esp_sa","w")
if fh == nil then
	fh:close()
	return nil,estr,ecode
else
	fh:write(sendconf)
	fh:write(receiveconf)
	fh:close()
end

print("isakmp cookie id="..icky)
print("isakmp key="..isakmpkey)
print("Local IP Address -> "..rip)
print("Remote IP Address -> "..iip.."\r\fConnection date -> "..dt)
print("Responder SPI="..rspi.."\r\fResponder EKEY="..rekey)
print("Initiator SPI="..ispi.."\r\fInitiator EKEY="..iekey)


(4)Wiresharkでキャプチャ作業
NVR500の「コマンド実行」
「ipsec sa policy 2 2 esp aes-cbc sha-hmac」
「syslog debug on」

Wiresharkのキャプチャ実行
NVR500 L2TP/IPsec VPN 接続
*** キャプチャのための作業***
Wiresharkのキャプチャ終了
Wiresharkのキャプチャデータを保存

NVR500の「コマンド実行」
「ipsec sa policy 2 2 esp aes256-cbc sha-hmac」
「syslog debug off」
「lua sd1:/local/GetNvr500ISAKMPESPKey.lua」
実行結果が表示されないので、ファイル生成を確認
「show file list sd1:/local」

Wiresharkの実行PC(Macbook pro)上で
「cat /Volumes/local/20190313220052ikev1_decryption_table >> .wireshark/ikev1_decryption_table」
「cat /Volumes/local/20190313220052esp_sa >> .wireshark/esp_sa」
Wiresharkの再起動(終了起動)し保存したキャプチャデータの読込
パケットの解析

メモ1: 「syslog debug on」の状態だとwindows10のdhcpcからnvr500の知らない「[DHCPv6] unsupported option vender-opts(0x11)」通知メールが出続けるので「syslog debug off」必須。


--- 2019/04/18追記:
Win10 VPN接続時のNVR500 L2TP/IPsec ISAKMP復号鍵が壊れて表示

コメント    この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« Strongswan IKEv2 IPsec VPN... | トップ | Win10 VPN接続時のNVR500 L2T... »
最新の画像もっと見る

コメントを投稿

ブログ作成者から承認されるまでコメントは反映されません。

NVR500」カテゴリの最新記事