alert() is dead!?

2021年7月2日(UTC)にpostswigger.netに「alert() is dead, long live print()」と言うタイトルの記事が掲載されました。

portswigger.net

「Webアプリケーションの脆弱性診断でXSSを検出する際に愛用されているalert()メソッドが Chrome92(2021年7月20日)から無効になる」との事。・・・といってもクロスドメインのiframeに限った話のようです。

こういう事かな

<iframe src="http://jiranyan.local/mytest/xss.php?p=<img src=1 onerror=alert(1)>"></iframe> 

iframeから呼び出されているxss.phpは、パラメータ「p」の値をエコーバックする単純なプラグラムです。なので、わざわざxxs.phpを呼び出さなくても

<img src=1 onerror=alert(1)>

と書いたhtmlを作成してiframeから呼び出せば結果は同じです。

Chrome91で実行した結果 f:id:jiranyan:20210713092507p:plain

もちろん、firefoxの結果も同じです。 f:id:jiranyan:20210713092959p:plain

もしくは、iframeのsrcをパラメータから持ってくるようなphp(iframexss.php ↓)を作成し

<body>
<?php
        $p = $_GET['p'];
        $out = "<iframe src='".$p."'></iframe>";
        echo $out;
?>
</body>

脆弱性のある外部のphp(xss.php)をパラメータに指定し呼び出します。

http://jiranyan.in/mytest/iframexss.php?p=http%3A%2F%2Fjiranyan.local%2Fmytest%2Fxss.php%3Fp%3D%3Cimg+src%3D1+onerror%3Dalert%281%29%3E

上記のurlをデコードしたものはこちら

http://jiranyan.in/mytest/iframexss.php?p=http://jiranyan.local/mytest/xss.php?p=<img src=1 onerror=alert(1)>

Chrome91で実行した結果 f:id:jiranyan:20210713141629p:plain

もちろん、firefoxの結果も同じです。 f:id:jiranyan:20210713141647p:plain

Chrome92にアップ f:id:jiranyan:20210721191240p:plain

上記で試したヤツを実行する。 f:id:jiranyan:20210721191401p:plain

おぉー確かにalert()が動いていない(ポップアップがあがらない)

代替としてはprint()を使うらしいです。 こんな感じですかね。

http://jiranyan.in/mytest/iframexss.php?p=http://jiranyan.local/mytest/xss.php?p=<img src=1 onerror=print()>

実行結果はこんな感じです。 f:id:jiranyan:20210721191628p:plain

ちなみにiframe絡みではないalertは今まで通りに動作するようなのでご安心を f:id:jiranyan:20210721191750p:plain

脆弱性診断をやっている人はfirefoxを使う人が多いし、そもそもIframeを使うようなサイトは少なくなったので影響は少ないかもしれませんがね。

ちょっとメモ(これSQLiなん?)

とある診断員からの診断結果レポートをレビューしていたところ「う〜ん」と悩んだのでメモしておく

指摘している脆弱性はSQLi、いくつかの診断文字列を試行してしっかりと検証をしていたが、ある検証文字列で疑問が浮かんだ、データ挿入(更新)系の機能で文字列連結のSQL文として 「'ho'+'ge'」と「'ho''ge'」と「'ho'ge'」を検証していた。

インサート文にすると

insert into samle_tbl values('ho'+'ge')
insert into samle_tbl values('ho''ge')
insert into samle_tbl values('ho'ge')

のイメージとなる

診断員は一番目のSQLを実行すると「+」(プラス)が作用し「ho」と「ge」が結合され「hoge」がインサート、 二番目のSQLを実行すると文字列中の片方の「'」(シングルクォート)がエスケープとなりもう片方の「'」(シングルクォート)が文字列と認識され 「ho'ge」がインサート、三番目のSQLを実行すると'(シングルクォート)が不整合となりエラーとなることを期待し、まさにその通りの結果となっていた。

最初はなんの疑いもなかったが、診断対象システムがMariaDBであったことで疑問が発生した。

「あれ?MariaDBの文字列連結で「+」(プラス)って使えないでしょ」と思ったが、いまいち自信がなかったので検証をしてみた

検証したMariaDB

mysql> select version();
+--------------------------+
| version()                |
+--------------------------+
| 10.0.38-MariaDB-1~xenial |
+--------------------------+
1 row in set (0.00 sec)

mysql> 

一番目のSQL

mysql> insert into sample_tbl values('ho'+'ge');
Query OK, 1 row affected, 2 warnings (0.03 sec)

mysql> commit;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from sample_tbl;
+---------+
| message |
+---------+
| 0       |
+---------+
1 row in set (0.00 sec)

mysql> 

やはり連結はされない

念のため他のSQLも確認してみる

二番目のSQL

mysql> insert into samle_tbl values('ho''ge');
Query OK, 1 row affected (0.03 sec)

mysql> commit;
Query OK, 0 rows affected (0.01 sec)

mysql> select * from samle_tbl;
+---------+
| message |
+---------+
| 0       |
| ho'ge   |
+---------+
2 rows in set (0.00 sec)

mysql> 

これは想定通り

3番目のSQL

mysql> insert into samle_tbl values('ho'ge');
    '>
mysql> 

「'」(シングルクォート)の数が奇数?なので、まだ続きがあるんだろ的は動きwなので想定通り

とりあえず、SQLiの検証としては二番目、三番目でOKなので、SQLiは指摘できるが、なぜ一番目の検証文字列で文字列が結合されたのかは不明である。

お客さんにソースくれとは言えなし・・・

Metasploitable2でWebアプリの脆弱性を勉強する(SQLi その4)

※記事の内容は、ご自身が管理するサイト以外には絶対に使用しないでください。

[OWASP TOP10] A1_Injection_SQLi_4_Blind_SQL_via_Timing_Login


f:id:jiranyan:20190307170158p:plain

Blind SQL via TimingなのでTime-based SQL Injectionという理解でよいでしょう。

ログイン画面は A1_Injection_SQLi_2_BypassAuthentication_Login で既に検証済みなのでなのでHTTPリスエスト(パラメータ)やSQLなどについてはをそちらをご覧ください。

Time-based SQL Injectionについては、徳丸さんの記事で簡潔に説明されています。後述する実用例も参考にさせていただきました。

blog.tokumaru.org

まずは、単純なTime-based SQL Injectionの検証を行います。時差を利用してSQLiを検証するのでSleepを使用します。

SQLのイメージは下記のようになります。

SELECT * FROM accounts WHERE username='hogehogehoge' and password='hoge' and sleep(1)-- '

まずパラメータに「' and sleep(1)-- 」(1秒スリープ)を挿入し送信します。右下の処理時間に着目してください。

f:id:jiranyan:20190307173849p:plain

次にパラメータに「' and sleep(5)-- 」(5秒スリープ)を挿入し送信します。右下の処理時間に着目してください。

f:id:jiranyan:20190307174257p:plain

実際には8秒遅延していますが、まあSQLiが効いているということでよいでしょう。本来ですともっとサンプリングした方がよいですがsleepが効いていることがわかればよいです。

次に、Time-based SQL Injectionを利用して、情報を抜いてみます。やはり攻撃者としては認証情報が欲しいところですので、パスワードを調査します。

SQLのイメージです。「admin」のパスワード(文字列)を先頭から1文字づつ抜いていき、指定した文字と同じだったらsleepするという検証していくという方法です。

SELECT * FROM accounts WHERE username='hogehogehoge' AND password='hoge' and (select if(substr((select password from accounts  where username='admin'),1,1)='a',sleep(1),0))

試してみます(ブラウザからのリクエストを改ざん)

まずは先頭の文字が一致しないケースです。ちなみにパスワードは「adminPass」です。82millsecで処理されています。

f:id:jiranyan:20190307175950p:plain

次に先頭の文字が一致する[a]ケースです。2069millsecで処理されています。sleepが効いているようです。2秒遅延だけど・・・

f:id:jiranyan:20190307180551p:plain

2文字目不一致

f:id:jiranyan:20190307181036p:plain

2文字目一致[d]

f:id:jiranyan:20190307181339p:plain

3文字目不一致

f:id:jiranyan:20190307182710p:plain

3文字目一致[m]

f:id:jiranyan:20190307182812p:plain

4文字目不一致

f:id:jiranyan:20190307183406p:plain

4文字目一致[i]

f:id:jiranyan:20190307183423p:plain

5文字目不一致

f:id:jiranyan:20190307183438p:plain

5文字目一致[n]

f:id:jiranyan:20190307183451p:plain

以降、同じように繰り返していけば、いつかパスワードを抜くことができますね。

Metasploitable2でWebアプリの脆弱性を勉強する(SQLi その3)

※記事の内容は、ご自身が管理するサイト以外には絶対に使用しないでください。

[OWASP TOP10] A1_Injection_SQLi_3_Insert_injection_Register


f:id:jiranyan:20190305183631p:plain

Insert injection・・・なんか曖昧です。Registerなので登録時のインサートをインジェクションしろ!ってことでしょう。

例によって、どのようなサイト(機能)かどうかを調査します。

お題のとおりユーザ登録画面です。

f:id:jiranyan:20190305184001p:plain

ブラウザからどのようなリクエストを送信しているか調査します。

usernameとpassword(確認用含む)、signatureなどを送信しています。

f:id:jiranyan:20190307082251p:plain

今回は脆弱性診断ぽく、パラメータに「'」を挿入してアプリケーションの動作を確認してみます。(test0')

f:id:jiranyan:20190307082607p:plain

さすがの脆弱なシステムです、エラー(システム情報)が表示されました。末尾のシングルクォートがSQLの一部と解釈されエラーとなってます。

f:id:jiranyan:20190307082939p:plain

表示されたSQLを見ると、パラメータに挿入した「'」がSQLに組み込まれていることがわかります。SQLiは効きそうです。

INSERT INTO accounts (username, password, mysignature) VALUES ('testuser0','testuser0','test0'')

上記のSQLは「test0''」にシングルクォートが1つ余計に入っているため文法エラーを引き起こしていますので、試しにパラメータに挿入する文字列を「')-- 」にして強制的にSQLを終了させ、末尾の余計な文字列はコメント扱いにしてみます。

SQLのイメージは下記のようになります。

INSERT INTO accounts (username, password, mysignature) VALUES ('testuser0','testuser0','test0')-- ')

パラメータに「')-- 」を挿入します。

f:id:jiranyan:20190307085539p:plain

正常にユーザ登録が完了しました。末尾のコメントが有効に作用し正常にInsert文が実行されたようです。

f:id:jiranyan:20190307090028p:plain

SQLiが効いていることはわかりましが、なにか悪用できないか考えてみます。ご丁寧なエラー情報によりアカウント情報が格納されているテーブル(accounts)のカラム(username, password, mysignature)がわかります。攻撃者としては認証情報が欲しいところです。Metasploitable2でWebアプリの脆弱性を勉強する(SQLi その1)で利用したアカウント情報を表示する機能がありますので、攻撃者が自らのアカウントを登録する際に「mysignature」に他ユーザのパスワード(パスワード平文・・・)を挿入できるかもしれません。

SQLのイメージです。mysignature列に副問い合わせで「admin」のパスワードを挿入するイメージです。

INSERT INTO accounts (username, password, mysignature) VALUES ('hogehogehoge', 'hoge',(select password from (select password from accounts where username='admin') as temp1))

試してみます(ブラウザからのリクエストを改ざん)

f:id:jiranyan:20190307092310p:plain

パラメータ「password」の値「hoge」のあとに「',(select password from (select password from accounts where username='admin') as temp1))-- 」を挿入するイメージです。

SQLのイメージです。

INSERT INTO accounts (username, password, mysignature) VALUES ('hogehogehoge', 'hoge',(select password from (select password from accounts where username='admin') as temp1))-- ', 'hogehoge')

アカウント登録に成功しました。

f:id:jiranyan:20190307092935p:plain

試しにカウント情報を表示する機能を使って、「admin」のパスワードが取れるか確認してみます。

f:id:jiranyan:20190307093048p:plain

f:id:jiranyan:20190307093103p:plain

Signatureに「admin」のパスワードが格納されていることが確認できます。

SQLiもそうなのですが

  • アプリのエラー処理でSQLを含むエラー情報を画面に表示させている
  • パスワードを平文で格納している

のような脆弱性にも気をつけなければなりませんね。

Metasploitable2でWebアプリの脆弱性を勉強する(SQLi その2)

※記事の内容は、ご自身が管理するサイト以外には絶対に使用しないでください。

[OWASP TOP10] A1_Injection_SQLi_2_BypassAuthentication_Login


f:id:jiranyan:20190305124442p:plain

BypassAuthenticationですのでSQLiを使って認証を回避するという事が想像できます。

例によって、どのようなサイト(機能)かどうかを調査します。

NameとPasswordを入力し、Passwordがあっていればログインに成功します。ふつーのログイン画面ですw

f:id:jiranyan:20190305124649p:plain

ブラウザからどのようなリクエストを送信しているか調査します。

usernameとpasswordを送信しています。(あたりまえ)

f:id:jiranyan:20190305125051p:plain

SQLiなのでどのようなSQLを投げているかソースを見てみます。(またまた反則)

<?php
$query =    "SELECT * FROM accounts WHERE username='".  
                            $username.                  
                            "' AND password='".         
                            $password.                  
                            "'";  

具体的に値をあてはめたSQLです。

SELECT * FROM accounts WHERE username='test' AND password='test'

目的(認証回避)を遂行するには、別にパスワードが一致しないでも条件(WHERE句)が真でかえるSQLを投げればよいことが想像できます。

こんな感じです。

SELECT * FROM accounts WHERE username='test' AND password='test' OR 'a'='a'

試してみます(ブラウザからのリクエストを改ざん)

f:id:jiranyan:20190305131109p:plain

ログインに成功しました。※「test」というユーザが居なかったのでSQLで最初にヒットしたレコード(Admin)が表示されたと思われます。

f:id:jiranyan:20190305131539p:plain

Metasploitable2でWebアプリの脆弱性を勉強する(SQLi その1)

Metasploitable2を使ってWebアプリの脆弱性を勉強する機会があったので備忘録がてら記事にします。

※記事の内容は、ご自身が管理するサイト以外には絶対に使用しないでください。

[OWASP TOP10] A1_Injection_SQLi_1_ExtractData_UserInfo


f:id:jiranyan:20190305112112p:plain

ExtractDataですのでSQLiを使ってデータを抽出するという事が想像できます。

まずは、どのようなサイト(機能)かどうかを調査します。

NameとPasswordを入力し、Passwordがあっていればアカウント情報を表示する機能のようですので、他ユーザの情報を収集することが目的になります。

f:id:jiranyan:20190305113409p:plain

ブラウザからどのようなリクエストを送信しているか調査します。

NameとPassowdを送信しています。(あたりまえ)

f:id:jiranyan:20190305114323p:plain

SQLiなのでどのようなSQLを投げているかソースを見てみます。(反則)

<?php
$lQuery = "SELECT * FROM accounts WHERE username='".$lUsername."' AND password='".$lPassword."'";

具体的に値をあてはめたSQLです。

SELECT * FROM accounts WHERE username='jiranyan' AND password='password'

目的(他ユーザの情報を収集)を遂行するには、ユーザを限定せず(条件がヒットしちゃうと1レコードしか返らないので)、すべてのユーザを情報が取得できるSQLを投げなければならないことが想像できます。

こんな感じです。

SELECT * FROM accounts WHERE username='test' AND password='test' OR 'a'='a'

試してみます(ブラウザからのリクエストを改ざん)

f:id:jiranyan:20190305120844p:plain

全ユーザの情報がとれました。

f:id:jiranyan:20190305121107p:plain