Write-Ups
Rayhan0x01,
May 15
2022
In this write-up, we'll go over the web challenge Mutation Lab, rated as medium difficulty in the Cyber Apocalypse CTF 2022. The solution requires exploiting a local file read vulnerability to steal the cookie signing key and crafting a session cookie for the admin.
One of the renowned scientists in the research of cell mutation, Dr. Rick, was a close ally of Draeger. The by-products of his research, the mutant army wrecked a lot of havoc during the energy-crisis war. To exterminate the leftover mutants that now roam over the abandoned areas on the planet Vinyr, we need to acquire the cell structures produced in Dr. Rick's mutation lab. Ulysses managed to find a remote portal with minimal access to Dr. Rick's virtual lab. Can you help him uncover the experimentations of the wicked scientist?
The application homepage displays a login form and a link to the registration page. Since we don't have an account, we can create an account via the registration page and log in. After logging in, the application redirects to the following dashboard page:
We can interact with the two canvas elements displayed on the webpage. Clicking on the buttons below the canvas exports a PNG image file of the canvas. Clicking the export button sends the following API request to the server:
That is pretty much all the accessible features in this web application.
Clicking the second export button downloads a PNG image of the second canvas but the Networks tab in the browser shows two different requests that originated when we click the button:
Looking into the client-side JavaScript code of the dashboard.js file, the button with the id exportTadpoleCanvas
has two onClick
event handlers specified to call the exportExp
function:
$('#exportCellCanvas').on('click', () => {exportExp(scope1)});
$('#exportTadpoleCanvas').on('click', () => {exportExp(scope2)});
$('#exportTadpoleCanvas').on('click', () => {exportExp(scope3)});
const exportExp = async (scope) => {
await fetch(`/api/export`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
svg: scope.project && scope.project.exportSVG({asString: true})
}),
})
.then((response) => response.json())
.then((data) => {
if (data.hasOwnProperty('png')) {
window.open(data.png);
}
})
.catch((error) => {
console.log(error);
});
}
The scope3.project
attribute is null, which causes the second request to fail as the request JSON parameter svg
is null
. The response contains an error stack trace:
From the error stack trace, we can see the convert-svg-core npm module is used to convert the SVG code to a PNG image. Searching for vulnerabilities in this module leads us to the following Snyk advisory page:
https://security.snyk.io/vuln/SNYK-JS-CONVERTSVGCORE-1582785
The advisory contains a proof-of-concept payload that we can send to the API to read the server-side file /app/index.js
:
{
"svg":"<svg-dummy></svg-dummy>\n<iframe src=\"///app/index.js\" width=\"100%\" height=\"1000px\"></iframe><svg viewBox=\"0 0 240 80\" height=\"1000\" width=\"1000\" xmlns=\"http://www.w3.org/2000/svg\"> <text x=\"0\" y=\"0\" class=\"Rrrrr\" id=\"demo\">data</text></svg>"
}
Sending the above payload to the /api/export
endpoint returns a PNG image URL which discloses the application source code in the exported PNG image file: