why client-side environment variables are a bad idea

or, the fuck-ups of a major minecraft/microsoft partner

at the time of writing this is all patched, dont come to me asking if it still works and how you can do it.

so, recently i was looking into a microsoft partner called gamersafer which promotes facial recognition KYC to log into games (fun stuff) and also notably runs the official minecraft serverlist which is riddled with servers that are against mojangs own rules

the start

im in some cringy "minecraft server owner" or "minecraft influencer" discord server where people like to shit on mojang (for no real reason) on, and i saw this message chain on one:

im too lazy to actually alt-text this, im so sorry, but its a very long convo, the gist of it is BlueBandit from PvPLegacy asking misterepic if he has any idea on the review times on findmcserver.com

this reminded me of the fact that gamersafer exists and that they are a microsoft partner, which got me to poke around their service!

initial recon

so, as i do i was just looking at their subdomains to see if theres anything interesting on them, and one stuck out: admin.gamersafer.com.

opening the domain in a browser displayed a login prompt that looked a little like this:

a username and password prompt with the gamersafer logo at the top, and the title "Welcome to GS Sudo" with a subtitle "The GamerSafer's super user admin panel"

upon opening firefox devtools, i discovered that this page actually had JS sourcemaps on, which was going to be useful for reversing stuff.

the first thing i do is to try to look for api calls, and as i did so, something stuck out with the api client, it seemed to be referencing something called REACT_APP_AWS_ACCESS_KEY and REACT_APP_AWS_SECRET_KEY while i dont know about aws that much, this looked a little funky.

it was supposed to be in the process.env but obviously we arent in node so that doesnt exist so i decided to just search for it in the non-sourcemapped js:

the app environment config

so, i cleaned this up and took a better look at it:

{
	"NODE_ENV": "production",
	"PUBLIC_URL": "",
	"WDS_SOCKET_HOST": null,
	"WDS_SOCKET_PATH": null,
	"WDS_SOCKET_PORT": null,
	"FAST_REFRESH": true,
	"REACT_APP_API_HOST": "apiv2.gamersafer.com",
	"REACT_APP_AWS_SECRET_KEY": "redacted+redacted",
	"REACT_APP_CHECKOUT_API_URL": "https://redacted.execute-api.us-east-1.amazonaws.com/prod/",
	"REACT_APP_AWS_ACCESS_KEY": "redacted",
	"REACT_APP_AUTH_SECRET_KEY": "redacted"
}

chaos begins

at first i thought that the AWS secret key being exposed was fine because it had very limited permissions, so to confirm that i quickly looked at the AWS rest api to see how it worked, the module that seemed the simplest and juiciest to me was IAM, and long story short, the account/key we had access to, had full administrator access:

<Path>/</Path>
<AttachedManagedPolicies>
  <member>
    <PolicyArn>arn:aws:iam::aws:policy/AmazonAPIGatewayAdministrator</PolicyArn>
    <PolicyName>AmazonAPIGatewayAdministrator</PolicyName>
  </member>
  <member>
    <PolicyArn>arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs</PolicyArn>
    <PolicyName>AmazonAPIGatewayPushToCloudWatchLogs</PolicyName>
  </member>
  <member>
    <PolicyArn>arn:aws:iam::aws:policy/AdministratorAccess</PolicyArn>
    <PolicyName>AdministratorAccess</PolicyName>
  </member>
  <member>
    <PolicyArn>arn:aws:iam::aws:policy/AmazonSESFullAccess</PolicyArn>
    <PolicyName>AmazonSESFullAccess</PolicyName>
  </member>
  <member>
    <PolicyArn>arn:aws:iam::aws:policy/AmazonSQSFullAccess</PolicyArn>
    <PolicyName>AmazonSQSFullAccess</PolicyName>
  </member>
  <member>
    <PolicyArn>arn:aws:iam::aws:policy/AmazonAPIGatewayInvokeFullAccess</PolicyArn>
    <PolicyName>AmazonAPIGatewayInvokeFullAccess</PolicyName>
  </member>
  <member>
    <PolicyArn>arn:aws:iam::aws:policy/AmazonS3FullAccess</PolicyArn>
    <PolicyName>AmazonS3FullAccess</PolicyName>
  </member>
  <member>
    <PolicyArn>arn:aws:iam::aws:policy/AWSLambda_FullAccess</PolicyArn>
    <PolicyName>AWSLambda_FullAccess</PolicyName>
  </member>
  <member>
    <PolicyArn>arn:aws:iam::aws:policy/service-role/AmazonS3ObjectLambdaExecutionRolePolicy</PolicyArn>
    <PolicyName>AmazonS3ObjectLambdaExecutionRolePolicy</PolicyName>
  </member>
</AttachedManagedPolicies>
<GroupList />
<UserName>redacted</UserName>
<Arn>arn:aws:iam::redacted:user/redacted</Arn>
<UserId>redacted</UserId>
<CreateDate>redatced</CreateDate>
<Tags />

which means that if i wanted to, i could do anything like exfiltrate all their data and delete everything.

disclosure

so first i tried the gamersafer.com contact form, which didnt seem like it did anything.

then i tried to get some contacts in gamersafer with the help of some friends. i was eventually able to get in contact with TheMisterEpic, which had previously talked to gamersafer before and he quickly created a groupchat with the product manager, who quickly responded to my inquiry.

from there, it was just taking down stuff and revoking the keys.

they said thanks but there was no bounty given (kinda expected)

lessons learned

dont use client environment variables for secrets, please.

this mistake couldve been avoided by delegating more things to serverside api calls from authorized clients.

oh fuck here we go again

it has been 72 hours since disclosure of this vulnerability to gamersafer, they have not disclosed the breach of all of their infrastructure to end users which (i believe) is highly illegal in the GDPR.

i also found another vulnerability in findmcserver which allowed me to approve my own server, give myself badges and approving my own server.

find minecraft server serverlist, a server named "gamersafer" which is mine with all of the available badges, approved

while this isnt as critical, they found the server and i started getting spam logged out (lol), i soon recieved a DM from the CEO.

i also got this email from the ceo saying they disclosed it (havent checked and it was literally past the disclosure time anyway, better then nothing) Dear Eva, Sending this email to let you know that players were notified about the incident and we follow the steps required by data privacy authorities as well. Furthermore, we are setting up https://securitytxt.org/ in the following days as well as analyzing how to use https://hackerone.com/ as additional resources for us (considering a bug bounty program). Once again thanks for your support and ethical approach to the incident. Best, Rodrigo