HexBrain

HackerOne · Report #981036

Hacker can bypass minimum bounty amount restrictions in "invitation preferences" setting via UpdateInvitationPreferencesMutation GraphQL operation

Informativestatus
lowseverity
29upvotes
20 Sept 2021disclosed

How it was found

Summary: Hacker can bypass minimum bounty amount restrictions in invitation preferences due to trusted client-side input to UpdateInvitationPreferencesMutation GraphQL operation

Description: The new "Bounty Preferences" feature at https://hackerone.com/settings/preferences allows the hacker to set a minimum critical bounty size for their future private program invitations. The largest minimum bounty a hacker can select using the slider in the user interface is based on their average bounty payout. I've submitted a few low severity/low payout issues lately so my average is dropping like a stone. I was excited to see if there was a way around this.

A hacker can bypass this minimum bounty slider by sending a crafted POST to the UpdateInvitationPreferencesMutation GraphQL operation at https://hackerone.com/graphql

{"operationName":"UpdateInvitationPreferencesMutation",
  "variables":
    {"min_bounty":1337},
"query":"mutation UpdateInvitationPreferencesMutation($receive_invites: Boolean, $bounty_programs_only: Boolean, $exclude_crypto_programs: Boolean, $min_bounty: Float, $time_off_ends_at: DateTime) {\n  updateInvitationPreferences(input: {receive_invites: $receive_invites, bounty_programs_only: $bounty_programs_only, exclude_crypto_programs: $exclude_crypto_programs, min_bounty: $min_bounty, time_off_ends_at: $time_off_ends_at}) {\n    me {\n      id\n      hacker_invitations_profile {\n        id\n        receive_invites\n        bounty_programs_only\n        min_bounty\n        exclude_crypto_programs\n        active_time_off\n        time_off_ends_at\n        __typename\n      }\n      __typename\n    }\n    was_successful\n    __typename\n  }\n}\n"}

Steps To Reproduce

  1. Start your favorite interception proxy (I used Portswigger BurpSuite)

  2. Login to a hackerone account (hacker persona)

  3. visit https://hackerone.com/settings/preferences

  4. Toggle on "Only invite me for programs that award a Bounty"

  5. set slider to an initial value (for later comparison)

  6. Enable request interception in the proxy

  7. set slider to a different value

  8. the intercepted request will look like this:

    POST /graphql HTTP/1.1
    Host: hackerone.com
    Connection: close
    Content-Length: 848
    accept: */*
    X-Auth-Token: ████████████████████████████████████
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
    content-type: application/json
    Origin: https://hackerone.com
    Sec-Fetch-Site: same-origin
    Sec-Fetch-Mode: cors
    Sec-Fetch-Dest: empty
    Referer: https://hackerone.com/settings/preferences
    Accept-Encoding: gzip, deflate
    Accept-Language: en-US,en;q=0.9
    Cookie: ███████████████████████████████████
    
    {"operationName":"UpdateInvitationPreferencesMutation",
      "variables":{
        "min_bounty":1337
      },
      "query":"mutation UpdateInvitationPreferencesMutation($receive_invites: Boolean, $bounty_programs_only: Boolean, $exclude_crypto_programs: Boolean, $min_bounty: Float, $time_off_ends_at: DateTime) {\n  updateInvitationPreferences(input: {receive_invites: $receive_invites, bounty_programs_only: $bounty_programs_only, exclude_crypto_programs: $exclude_crypto_programs, min_bounty: $min_bounty, time_off_ends_at: $time_off_ends_at}) {\n    me {\n      id\n      hacker_invitations_profile {\n        id\n        receive_invites\n        bounty_programs_only\n        min_bounty\n        exclude_crypto_programs\n        active_time_off\n        time_off_ends_at\n        __typename\n      }\n      __typename\n    }\n    was_successful\n    __typename\n  }\n}\n"
    }
    
  9. Update the min_bounty variable to the desired minimum bounty. on my account I set to 7000

    {"operationName":"UpdateInvitationPreferencesMutation",
      "variables":{
        "min_bounty":7000
      },
      "query":"mutation UpdateInvitationPreferencesMutation($receive_invites: Boolean, $bounty_programs_only: Boolean, $exclude_crypto_programs: Boolean, $min_bounty: Float, $time_off_ends_at: DateTime) {\n  updateInvitationPreferences(input: {receive_invites: $receive_invites, bounty_programs_only: $bounty_programs_only, exclude_crypto_programs: $exclude_crypto_programs, min_bounty: $min_bounty, time_off_ends_at: $time_off_ends_at}) {\n    me {\n      id\n      hacker_invitations_profile {\n        id\n        receive_invites\n        bounty_programs_only\n        min_bounty\n        exclude_crypto_programs\n        active_time_off\n        time_off_ends_at\n        __typename\n      }\n      __typename\n    }\n    was_successful\n    __typename\n  }\n}\n"
    }
    
  10. Now submit your crafted POST request. my request received a 200 OK with "was_successful":true with the following result:

    {
       "data":{
          "updateInvitationPreferences":{
             "me":{
                "id":"██████",
                "hacker_invitations_profile":{
                   "id":"██████",
                   "receive_invites":true,
                   "bounty_programs_only":true,
                   "min_bounty":7000.0,
                   "exclude_crypto_programs":true,
                   "active_time_off":false,
                   "time_off_ends_at":null,
                   "__typename":"HackerInvitationsProfile"
                },
                "__typename":"User"
             },
             "was_successful":true,
             "__typename":"UpdateInvitationPreferencesPayload"
          }
       }
    }
    

    ████

  11. to further verify, I queried GraphQL again with the UserInvitationSettingsQuery operation and received back

    {
       "data":{
          "me":{
             "id":███████████████████████████████████,
             "hacker_invitations_profile":{
                "id":███████████████████████████████████,
                "receive_invites":true,
                "bounty_programs_only":true,
                "min_bounty":7000.0,
                "exclude_crypto_programs":true,
                "time_off_ends_at":null,
                "active_time_off":false,
                "__typename":"HackerInvitationsProfile"
             },
             "bounties":{
                "average_amount":600.625,
                "__typename":"BountyConnection"
             },
             "__typename":"User"
          }
       }
    }
    

    ██████

  12. await your next private program invitation with a higher critical bounty payout.

Optional: Your Environment (Browser version, Device, etc)

  • Chrome/ Chromium 85.0.4183.83 (Official Build) (64-bit) (Mac OS 10.15.6)

Optional: Supporting Material/References (Screenshots)

Updating the minimum critical bounty payout. ███████

Querying the current set minimum bounty ███

Optional: Did you use recon data made available by HackerOne to find this vulnerability?

no

Impact

Hacker can further influence which private programs they are invited to outside of the intended design of the minimum critical bounty feature.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original