CVE-2024-56159 Astro Sourcemap Exposure
This is a quick exploration of a recent vulnerability discovered within the Astro framework. Astro is one of the latest in a line of frontend JavaScript frameworks that offers SSR capabilities (among other features). I’ve been working with Astro this past year, which is why this announcement caught my eye. The particular issue is that server source code is exposed when Astro is configured with sourcemaps enabled.
Typically, one would enable sourcemaps in production if there is a need to supply a third-party tool with sourcemaps for diagnostic purposes. For example, we can provide a stack trace to Sentry if our page crashes for the user, allowing a human developer to parse something readable instead of the obfuscated code produced by JavaScript compilation and transpilation.
It just so happens that one of my projects—an ecommerce site at treecommerce.dev—integrated Sentry and was using an outdated version of Astro.
astro <=4.16.17 || 5.0.0-alpha.0 - 5.0.0-beta.12
Severity: high
DOM Clobbering Gadget found in astro's client-side router that leads to XSS - https://github.com/advisories/GHSA-m85w-3h95-hcf9
Atro CSRF Middleware Bypass (security.checkOrigin) - https://github.com/advisories/GHSA-c4pw-33h3-35xw
Astro's server source code is exposed to the public if sourcemaps are enabled - https://github.com/advisories/GHSA-49w6-73cw-chjr
Depends on vulnerable versions of cookie
Inspection
Treecommerce’s code is hosted publicly, and there aren’t any exposed secrets. Regardless, it’s still an opportunity to poke around a website and see what we can find.
According to the GitHub advisory, we can just navigate to a path like dist/client/pages/index.astro.mjs.map
, download a map file, and look at the code in a visualizer (the advisory links to a hosted tool).
My process did not repeat so nicely. My code is stored in the default _astro
directory and there aren’t any immediately obvious map files to download. Instead I had to do a little digging using my browser tools and inspect the JavaScript files that my browser retrieved from the server. These have to map to something after all.
Inspecting these chunks reveals a commented out sourceMappingURL
pointing to the sourcemap for that file. Now I can download map files, but running them through the visualizer would usually fail due to importing issues. If they didn’t, it was most likely pure client-side code, and thus probably useless.
This makes sense. We import files, and the visualizer can’t import code that it’s not aware of. Inspecting these map files revealed something else. Sometimes sourceMappingURL
is a base64-encoded string.
Copying and pasting that string into its own file, decoding it into a map file, and then running that through the parser did the trick. Now I could read my server code.
Exploitation
This process is laborious, and admittedly, I could be a little more knowledgeable on visualizer tools. But still, we should ask ourselves, “Is there a better way?”. I wrote a small tool using Node and Playwright, Astro Mapper, that can:
- grab the chunked js files from a webpage
- use their filenames to locate their sourcemap counterpart
- retrieve the sourcemaps
- parse those files for any base64 string
- decode those strings into a separate file (small snippet from source code below)
const encodedSourceMap = body.match(/sourceMappingURL=data:application\/json;.*;base64,([^"]+)/)?.[1];
if (encodedSourceMap) {
const decodedSourceMap = Buffer.from(encodedSourceMap, 'base64').toString('utf-8');
fs.writeFile(`${path.resolve(LOCAL_MAPS_DIR, fileName+'.decoded.map')}`, decodedSourceMap, err => {
if (err) {
console.error(err)
}
})
}
Those files can then be run through the visualizer. Now all we need to do is run it like so.
node astro-mapper.js <URL>
And there we have it; we have a directory containing source maps for a page that we can later analyze. This code could probably be improved upon, but this vulnerability has been patched, and it’s more for demonstrative purposes than anything.