ちょっと話題の記事

[BODYも分かる!] AWS WAFでXSS/SQLiのログに詳細が記録されるようになりました[アップデート] #reinvent

AWS WAFのXSS/SQLiルールで検知したログの詳細が出力されるようになりました。特にリクエストのBodyの一部を記録することもできるためリクエストの調査やホワイトリスト作成に役に立ちます。
2020.01.05

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、臼田です。

みなさん、WAFWAFしてますか?(久しぶりの挨拶

今回はAWS WAF利用者にとって地味に嬉しいアップデートがありました。

AWS WAF で、一致したルールに関するコンテキストのリクエストログ記録を改善

こちらを紹介しつつ実際にやってみます。

XSS/SQLiのログ詳細記録とは

AWS WAFは2018年9月より下記の通りフルログを取得できるようになっていました。

フルログではWAFを通過した際の処理内容について様々な情報が出力されていましたが、XSS(クロスサイトスクリプティング)やSQLi(SQLインジェクション)の対策はAWS側で元々用意されているルールに合致したかどうかで判定されるため、リクエストのどの内容が問題となってBlockされているかが分かりづらいという欠点がありました。

今回はAWS WAFのフルログの中にterminatingRuleMatchDetailsというログフィールドが追加され、リクエストのどの場所(ヘッダ/クエリストリング/Body等)でどの値が検知に影響したか分かるようになりました。

特にフルログではBodyの中身までは記録されませんでしたが、このアップデートによりリクエストBodyの内容を少しですが確認できます。

これにより、Blockしたリクエストが誤検知かどうかを切り分けたり、ホワイトリストを作成する際に役立てることが可能です。

やってみた

それでは実際にログを出してみます。

環境準備

やられサイトはお馴染みDVWAを用意しています。EC2上に構築しておき、Lowの設定にします。詳細は割愛します。

続いてAWS WAFのセットアップをします。AWS WAFは現在2種類あり、古い方はAWS WAF Classic、新しい方はNew AWS WAFやWAFv2と呼ばれています。今回はv2で行きます。

ちなみにClassicで該当ログフィールドが追加されているかは動作確認していません。

v2ではAWS Managed Rules(AMRs)を利用することができ、細かいRuleの設定を行わなくてもいい感じに設定してくれるのでこれを活用していきます。

今回はAMRsからCore rule setSQL databaseを利用します。セットアップ方法は下記を参照してください。

設定したらWAFのログ出力の設定を行います。ログ出力の設定はClassicと同様です。下記の中盤も参考になります。

ログ出力の設定ができたら、実際に検知させてみます。

XSSのログ

まずはXSSの検知を行います。DVWAのXSS (Reflected)で<script>alert(1)</script>を投げてみます。

検知して403となりました。ログは下記のようになっています。

{
	"timestamp": 1578148627114,
	"formatVersion": 1,
	"webaclId": "arn:aws:wafv2:ap-northeast-1:999999999999:regional/webacl/test-waf/xxxxxxxx-xxxx-xxxx-xxxx-xxxx-xxxxxxxx",
	"terminatingRuleId": "AWS-AWSManagedRulesCommonRuleSet",
	"terminatingRuleType": "MANAGED_RULE_GROUP",
	"action": "BLOCK",
	"terminatingRuleMatchDetails": [
		{
			"conditionType": "XSS",
			"location": "ALL_QUERY_ARGS",
			"matchedData": [
				"<",
				"script"
			]
		}
	],
	"httpSourceName": "ALB",
	"httpSourceId": "999999999999-app/test-alb/xxxxxxxxxxxxxxxx",
	"ruleGroupList": [
		{
			"ruleGroupId": "AWS#AWSManagedRulesCommonRuleSet",
			"terminatingRule": {
				"ruleId": "CrossSiteScripting_QUERYARGUMENTS",
				"action": "BLOCK"
			},
			"nonTerminatingMatchingRules": [],
			"excludedRules": null
		}
	],
	"rateBasedRuleList": [],
	"nonTerminatingMatchingRules": [],
	"httpRequest": {
		"clientIp": "203.0.113.1",
		"country": "JP",
		"headers": [
			{
				"name": "Host",
				"value": "test-alb-9999999999.ap-northeast-1.elb.amazonaws.com"
			},
			{
				"name": "Pragma",
				"value": "no-cache"
			},
			{
				"name": "Cache-Control",
				"value": "no-cache"
			},
			{
				"name": "Upgrade-Insecure-Requests",
				"value": "1"
			},
			{
				"name": "User-Agent",
				"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
			},
			{
				"name": "Accept",
				"value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
			},
			{
				"name": "Referer",
				"value": "http://test-alb-9999999999.ap-northeast-1.elb.amazonaws.com/dvwa/vulnerabilities/xss_r/"
			},
			{
				"name": "Accept-Encoding",
				"value": "gzip, deflate"
			},
			{
				"name": "Accept-Language",
				"value": "ja,en-US;q=0.9,en;q=0.8"
			},
			{
				"name": "Cookie",
				"value": "security=low; PHPSESSID=2k8um9856p78raa5uelpmhf8k3"
			}
		],
		"uri": "/dvwa/vulnerabilities/xss_r/",
		"args": "name=%3Cscript%3Ealert%281%29%3C%2Fscript%3E",
		"httpVersion": "HTTP/1.1",
		"httpMethod": "GET",
		"requestId": null
	}
}

terminatingRuleMatchDetailsが追加されているのが確認できました。conditionTypeでどの種類のルールか、locationでクエリのどの場所か、matchedDataでどの値が影響したかがわかります。

今回はクエリストリングの中の<scriptで引っかかっているようです。わかりやすいですね。

XSSでBodyを検知

続いてXSS (Stored)のページを利用してPOSTのBodyに含まれるXSSを検知してみます。

Messageに<script>alert(1)</script>を入れて送信して検知させると、下記のログが出力されました。

{
	"timestamp": 1578167216862,
	"formatVersion": 1,
	"webaclId": "arn:aws:wafv2:ap-northeast-1:999999999999:regional/webacl/test-waf/xxxxxxxx-xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx",
	"terminatingRuleId": "AWS-AWSManagedRulesCommonRuleSet",
	"terminatingRuleType": "MANAGED_RULE_GROUP",
	"action": "BLOCK",
	"terminatingRuleMatchDetails": [
		{
			"conditionType": "XSS",
			"location": "BODY",
			"matchedData": [
				"<",
				"script"
			]
		}
	],
	"httpSourceName": "ALB",
	"httpSourceId": "999999999999-app/test-alb/xxxxxxxxxx",
	"ruleGroupList": [
		{
			"ruleGroupId": "AWS#AWSManagedRulesCommonRuleSet",
			"terminatingRule": {
				"ruleId": "CrossSiteScripting_BODY",
				"action": "BLOCK"
			},
			"nonTerminatingMatchingRules": [],
			"excludedRules": null
		}
	],
	"rateBasedRuleList": [],
	"nonTerminatingMatchingRules": [],
	"httpRequest": {
		"clientIp": "203.0.113.1",
		"country": "JP",
		"headers": [
			{
				"name": "Host",
				"value": "test-alb-99999999.ap-northeast-1.elb.amazonaws.com"
			},
			{
				"name": "Content-Length",
				"value": "86"
			},
			{
				"name": "Cache-Control",
				"value": "max-age=0"
			},
			{
				"name": "Origin",
				"value": "http://test-alb-99999999.ap-northeast-1.elb.amazonaws.com"
			},
			{
				"name": "Upgrade-Insecure-Requests",
				"value": "1"
			},
			{
				"name": "Content-Type",
				"value": "application/x-www-form-urlencoded"
			},
			{
				"name": "User-Agent",
				"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
			},
			{
				"name": "Accept",
				"value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
			},
			{
				"name": "Referer",
				"value": "http://test-alb-99999999.ap-northeast-1.elb.amazonaws.com/dvwa/vulnerabilities/xss_s/"
			},
			{
				"name": "Accept-Encoding",
				"value": "gzip, deflate"
			},
			{
				"name": "Accept-Language",
				"value": "ja,en-US;q=0.9,en;q=0.8"
			},
			{
				"name": "Cookie",
				"value": "security=low; PHPSESSID=2k8um9856p78raa5uelpmhf8k3"
			}
		],
		"uri": "/dvwa/vulnerabilities/xss_s/",
		"args": "",
		"httpVersion": "HTTP/1.1",
		"httpMethod": "POST",
		"requestId": null
	}
}

内容は前回のものとほぼ変わりませんが、locationBODYとなり、Bodyに<scriptが含まれていたことがわかります。なぜそのリクエストが検知されたか、何もない状態と比べてだいぶわかりやすくなったと思います。

SQLiのログ

続いてSQLiもやっていきます。DVWAのSQL Injectionで' or 1=1--'を投げてみます。

検知して403となりました。ログは下記のようになっています。

{
	"timestamp": 1578166241917,
	"formatVersion": 1,
	"webaclId": "arn:aws:wafv2:ap-northeast-1:999999999999:regional/webacl/test-waf/xxxxxxxx-xxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
	"terminatingRuleId": "AWS-AWSManagedRulesSQLiRuleSet",
	"terminatingRuleType": "MANAGED_RULE_GROUP",
	"action": "BLOCK",
	"terminatingRuleMatchDetails": [
		{
			"conditionType": "SQL_INJECTION",
			"location": "ALL_QUERY_ARGS",
			"matchedData": [
				"",
				"or",
				"1",
				"--'"
			]
		}
	],
	"httpSourceName": "ALB",
	"httpSourceId": "999999999999-app/test-alb/xxxxxxxxxxxxxxxx",
	"ruleGroupList": [
		{
			"ruleGroupId": "AWS#AWSManagedRulesCommonRuleSet",
			"terminatingRule": null,
			"nonTerminatingMatchingRules": [],
			"excludedRules": null
		},
		{
			"ruleGroupId": "AWS#AWSManagedRulesSQLiRuleSet",
			"terminatingRule": {
				"ruleId": "SQLi_QUERYARGUMENTS",
				"action": "BLOCK"
			},
			"nonTerminatingMatchingRules": [],
			"excludedRules": null
		}
	],
	"rateBasedRuleList": [],
	"nonTerminatingMatchingRules": [],
	"httpRequest": {
		"clientIp": "203.0.113.1",
		"country": "JP",
		"headers": [
			{
				"name": "Host",
				"value": "test-alb-9999999999.ap-northeast-1.elb.amazonaws.com"
			},
			{
				"name": "Upgrade-Insecure-Requests",
				"value": "1"
			},
			{
				"name": "User-Agent",
				"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
			},
			{
				"name": "Accept",
				"value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
			},
			{
				"name": "Referer",
				"value": "http://test-alb-9999999999.ap-northeast-1.elb.amazonaws.com/dvwa/vulnerabilities/sqli/"
			},
			{
				"name": "Accept-Encoding",
				"value": "gzip, deflate"
			},
			{
				"name": "Accept-Language",
				"value": "ja,en-US;q=0.9,en;q=0.8"
			},
			{
				"name": "Cookie",
				"value": "security=low; PHPSESSID=2k8um9856p78raa5uelpmhf8k3"
			}
		],
		"uri": "/dvwa/vulnerabilities/sqli/",
		"args": "id=%27+or+1%3D1--%27&Submit=Submit",
		"httpVersion": "HTTP/1.1",
		"httpMethod": "GET",
		"requestId": null
	}
}

今回はクエリストリングの中の(おそらくスペース)or/1/--がマッチしているようです。

まとめ

AWS WAFのログにterminatingRuleMatchDetailsが追加され、XSSやSQLiで検知されたリクエストの調査が捗るようになりました。

特にBodyを調査するための内容が出力されることが大きなメリットです。

特に追加設定をしないでもログフィールドに追加されていますので、ぜひ活用してみましょう。