Beanstalk Notion
Beanstalk Notion
/
🪲
Bug Reports
/
BIC Notes
/
📄
Report #40808
📄

Report #40808

Report Date
March 4, 2025
Status
Closed
Payout

GraphQL response size amplification leads to takedown of the app features in app.bean.money

‣
Report Info

Report ID

#40808

Report type

Websites and Applications

Has PoC?

Yes

Target

https://app.bean.money

Impacts

Taking down the application/website requiring manual restoration

Description

It is possible to force the server to respond with arbitrarily large response than the request by utilizing GraphQL misconfiguration that makes it possible to amplificate response size. By utilizing GraphQL default functionalities such as alias querying, fragments and value re-naming, it is possible to cause the server to return arbitrarily large amplificated responses. This leads to "takedown of application" - situation since multiple functionalities that require the data fetched from GraphQL in app.bean.money do not work anymore. The GraphQL is used for example to fetch data for Pods and liquidity. The GraphQL is handled by graph.bean.money.

Vulnerability Details

GraphQL at https://graph.bean.money/bean_eth (POST request) is vulnerable to response size amplification because of the misconfiguration that lies in the GraphQL. User is able to fully control the query that is entered to the application and the input is reflected in the response.

If user implements multiple aliased queries of the same query with own user created fragment and inside the fragment user renames one of the values with arbitrarily long name, it is possible with a single rename of a value to increase the length of all of the same parameter names that are returned in the response, thus leading to really large responses.

In more detail, let's take a query for example:

query SeasonalLiquidityPerPool($first: Int, $season_lte: Int, $pool: String!) {
  poolHourlySnapshots {
    id
  }
}

By implementing previous modifications (2x alias queries ALIAS1, ALIAS2 with fragment calls, fragment A and renamed value BBB), the query looks like this:

query SeasonalLiquidityPerPool($first: Int, $season_lte: Int, $pool: String!) {
  ALIAS1:poolHourlySnapshots {
    ...A
  }
  ALIAS2:poolHourlySnapshots {
    ...A
  }
}

fragment A on PoolHourlySnapshot {
          BBB:id
     }

This would return something like this:

As you can see that the user inputted value name BBB is reflected in all of the occasions where the real value would be returned for the query and also as many times as we have alias queries.

With this the following response size amplification can be achieved (response data controllable by the user): (Value name length) * (Amount of aliases) * (Amount of times the value is returned in one query) --> size of the response

In this specific case the value name length was set to "AAA" = 3; Amount of aliases = 3; Amount of times the value is returned for one query = 100

3 * 3 * 100 = 600 bytes, size of the response data controllable by the user. Not a lot but when we increase the values controllable by the user, we can achieve >4000x response size amplification easily!

From here we can then increase the amount of aliases and length of the value name to achieve arbitrarily large response size amplification ratios!

This case was tested with different combinations of amounts of aliases and lengths of values. The results for these are represented below:

  • 10 alias queries with value renamed to 100x "A" resulted in 157,000 bytes response with a response delay of 1.8 sec.
  • 10 alias queries with value renamed to 1000x "A" resulted in 1 MB response with a response delay of 2 sec.
  • 20 alias queries with value renamed to 5000x "A" resulted in 10 MB response with a response delay of 3.5 sec.
  • 100 alias queries with value renamed to 2000x "A" resulted in 20,5 MB response with a response delay of 5.5 sec.

Since the last combination already had such a large response size and a long response delay, I did not want to continue further until permission is given or until you have validated this yourselves in your controlled testing environment not to cause any issues to real customers or to the production environment.

It still could be clearly observed from these testing results that by continuing to increase the length of the value name and increasing the amount of alias queries, would this ultimately cause unnecessarily large load to the server and lead to takedown of the service.

Supporting Material/References:

Response size amplification: http://urn.fi/URN:NBN:fi:jyu-202305032824

Impact Details

This could be utilized by an unauthenticated attacker to cause major availability issues to the webservices that utilize the data fetched via the GraphQL. Since it is application-level, it requires only very limited amount of requests to be sent by the attacker to cause the impact described and thus makes it really hard to be detected by security tools. Ability to amplify the reflected response without limitations and thus possibly cause takedown of the application. Since the GraphQL is used to facilitate fetching data used in the app.bean.money for functions such as trading, this could result in situation where no user is able to do any actions that require this functionality.

When the GraphQL is taken down, would this cause major unavailability issues to the app.bean.money since it uses the GraphQL endpoint for it's main functionalities. In other words app.bean.money would be "taken down" since the functions could not be used. When the attacker keeps sending the requests over and over again, will this cause "persistent" takedown of the application until further action is taken

Since the endpoint is controlled by the program and the apps stated to be in scope do not contain any functionalities in their respective domains, it is believed that this endpoint therefore should be considered to be in scope.

Proof of concept

  1. Send request:
  1. See that you get normal response with id values
  2. Send request (2x alias queries ALIAS1, ALIAS2 with fragment calls, fragment A and renamed value BBB):
  1. See that you get now BBB instead of the previous value names and 2x the same query results
  2. Increase the amount of alias queries (these alias names need to be distinct) to *100 and increase the length of the value from BBB to 1,000x B and send the request
  3. Observe ~20,5mb response
  4. Continue increasing the amount of alias queries and the length of the value until impact is achieved

Note: If for example query complexity limit kicks in, just reduce the amount of aliases a little and just continue by increasing the length of the value.

BIC Response

Per our bug bounty program documentation, the following type of report is ineligible for a reward:

Any denial of service attacks that are executed against project assets

Therefore we are closing the report and no reward will be issued.

{"data":{
"ALIAS1":[
{"BBB":"0x3a70dfa7d2262988064a2d051dd47521e43c9bdd-455972"},
{"BBB":"0x3a70dfa7d2262988064a2d051dd47521e43c9bdd-455973"},
{"BBB":"0x3a70dfa7d2262988064a2d051dd47521e43c9bdd-455974"},
...
"ALIAS2":[
{"BBB":"0x3a70dfa7d2262988064a2d051dd47521e43c9bdd-455972"},
{"BBB":"0x3a70dfa7d2262988064a2d051dd47521e43c9bdd-455973"},
{"BBB":"0x3a70dfa7d2262988064a2d051dd47521e43c9bdd-455974"},
...
POST /bean_eth HTTP/1.1
Host: graph.bean.money
Content-Type: application/json
Content-Length: 272

{"operationName":"SeasonalLiquidityPerPool","variables":{"pool":"0x3a70dfa7d2262988064a2d051dd47521e43c9bdd","first":1000,"season_lte":2075},"query":"query SeasonalLiquidityPerPool($first: Int, $season_lte: Int, $pool: String!) {\n  poolHourlySnapshots {\n    id\n  }\n}"}
POST /bean_eth HTTP/1.1
Host: graph.bean.money
Content-Type: application/json
Content-Length: 400

{"operationName":"SeasonalLiquidityPerPool","variables":{"pool":"0x3a70dfa7d2262988064a2d051dd47521e43c9bdd","first":1000,"season_lte":2075},"query":"query SeasonalLiquidityPerPool($first: Int, $season_lte: Int, $pool: String!) {\n  ALIAS1:poolHourlySnapshots {\n    ...A\n  }\n  ALIAS2:poolHourlySnapshots {\r\n    ...A\r\n  }\n}\n\nfragment A on PoolHourlySnapshot {\r\n          BBB:id\r\n     }"}