glacier ctf 25

This commit is contained in:
2025-11-28 12:20:39 +01:00
parent 4df936a8d9
commit dc96452364
51 changed files with 105301 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
# Debian 12
# PHP 8.4.6
# Apache 2.4.62
FROM docker.io/library/php@sha256:abce8fe7e3390e96d3ac52dff8e6f5ff9507f4a7bee2b18f11404b74d7efec66
# Copy challenge required files
COPY ./config/php.ini $PHP_INI_DIR/php.ini
COPY ./web /var/www/html
COPY ./flag.txt /flag.txt

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
@echo off
setlocal
call :setESC
:req
echo %ESC%[34m[+] Note: This script has only been tested for docker using WSL2. It might work with Hyper-V, but it was not tested.%ESC%[0m
REM echo %ESC%[93m[+] Challenge Integrity is disabled for Windows%ESC%[0m
where docker > NUL 2>&1
if %ERRORLEVEL% NEQ 0 (
ECHO %ESC%[31m[+] docker command not found. Is docker installed?%ESC%[0m
exit /B 1
)
docker ps >NUL 2>&1
if %ERRORLEVEL% NEQ 0 (
ECHO %ESC%[31m[+] "docker ps" failed. Is docker running?%ESC%[0m
exit /B 1
)
:build
echo %ESC%[34m[+] Building Challenge Container%ESC%[0m
REM !!! THIS DOES NOT WORK IF YOU ARE IN A SYMLINKED FOLDER !!!
docker build -t localhost/chall-glacier-todo --platform linux/amd64 --pull=true .
:run
echo %ESC[34m[+] Running Challenge Container on 127.0.0.1:1337%ESC%[0m"
docker run --name chall-glacier-todo --rm -p 127.0.0.1:1337:1337 -p 127.0.0.1:8080:8080 -e HOST=127.0.0.1 -e PORT=1337 -e PUBPORTSTART=11000 -e PUBPORTEND=15000 -e NAME=glacier-todo -e TIMEOUT=600 -e DOMAIN=localhost -e DOMAIN_PORT=8080 -e DOMAIN_PROT=http -e REGISTRY=localhost --privileged --platform linux/amd64 --pull=never localhost/chall-glacier-todo
:setESC
for /F "tokens=1,2 delims=#" %%a in ('"prompt ### & echo on & for %%b in (1) do rem"') do (
set ESC=%%b
exit /B 0
)
exit /B 0

View File

@@ -0,0 +1,51 @@
#!/bin/sh
check() {
echo -e "\e[1;34m[+] Verifying Challenge Integrity\e[0m"
sha256sum -c sha256sum
}
build_container() {
echo -e "\e[1;34m[+] Building Challenge Docker Container\e[0m"
docker build -t localhost/chall-glacier-todo --platform linux/amd64 --pull=true .
}
# Common error on default Ubuntu 24.04:
#
# initCloneNs():391 mount('/', '/', NULL, MS_REC|MS_PRIVATE, NULL): Permission denied
# Change --user 1337:1337 to --user 0:0 in run_container()
# or
# $ sudo sysctl -w kernel.apparmor_restrict_unprivileged_unconfined=0
# $ sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
# and then restore them back when finished
run_container() {
echo -e "\e[1;34m[+] Running Challenge Docker Container on 127.0.0.1:1337\e[0m"
docker run --name chall-glacier-todo --rm -p 1337:80 localhost/chall-glacier-todo
}
kill_container() {
docker ps --filter "name=chall-glacier-todo" --format "{{.ID}}" \
| tr '\n' ' ' \
| xargs docker stop -t 0 \
|| true
}
case "${1}" in
"check")
check
;;
"build")
build_container
;;
"run")
run_container
;;
"kill")
kill_container
;;
*)
check
build_container && run_container
;;
esac

View File

@@ -0,0 +1 @@
gctf{IT_WOULD_BE_REALLY_STUPID_TO_NOT_TRY_OUT_IF_THIS_FLAG_IS_CORRECT}

View File

@@ -0,0 +1,11 @@
82fcbc9281b0abf9401f216efe7c9e81e78cf9bcf184a424623509779c7de19e ./deploy.sh
8147c53c32ade1451cc1dfaf52eaf8f1fe88ae0bdd6eef27f7120256094a69db ./deploy.bat
2533da00f66b1cf18430b08871262afa12f58cf2ee43b2077740c9c3972bb980 ./config/php.ini
e5538bfc30e2b06a53a8362c1bdec3c89fc2851847588fad084eed609ab35491 ./web/index.html
6416c3446cd37ff7d158794f3266458d2ed19ee56ecd5c05f167704e6087f11f ./web/api.php
105b3b42d4c7dccd85d776dcb47f2db51e95185a9394f0f150b84494e40e7691 ./web/assets/img/bg.jpg
333bbde178ad17fe6bf64916ab790315fd96ff84c0e4df057a4994749d09d595 ./web/assets/js/app.js
3e0ccc2e71ad0494b29b88b89daa77b1517efefeb86d718023b19ed54170c918 ./web/assets/js/api.js
61cb273217adca3a58bd7942a37621e02d16f06f687f7194a5528ebf8fe1d217 ./web/assets/css/style.css
dad3ca5cd81789d651a7dd3f7f2f65e830be0f0c1df26a5bb354cbd3b626fd17 ./Dockerfile
a1789ea1c25dd7c2249dbdf3cca873220afb0b06eafdc0426c2574c16731759f ./flag.txt

View File

@@ -0,0 +1,89 @@
<?php
session_start();
header("Content-Type: application/json");
$start = microtime(true);
define("TODOS", "/tmp/todos");
define("USERS", "/tmp/users.json");
define("SESS", "LOGIN_ID");
if(!file_exists(USERS)) file_put_contents(USERS, "[]");
if(!is_dir(TODOS)) mkdir(TODOS);
$res = array();
$data = array();
$status = 1;
$path = $_GET["path"];
if($path === "/todos/list") {
$isLoggedIn = isset($_SESSION[SESS]);
if(!$isLoggedIn) goto fail;
$user = $_SESSION[SESS];
if(!file_exists(TODOS . "/" . $user)) file_put_contents(TODOS . "/" . $user, "[]");
$todos = json_decode(file_get_contents(TODOS . "/" . $user), true);
$data["todos"] = array_values($todos);
} elseif($path === "/todos/add") {
$isLoggedIn = isset($_SESSION[SESS]);
if(!$isLoggedIn) goto fail;
$user = $_SESSION[SESS];
if(!file_exists(TODOS . "/" . $user)) file_put_contents(TODOS . "/" . $user, "[]");
$todos = json_decode(file_get_contents(TODOS . "/" . $user));
$name = isset($_POST["name"]) ? filter_input(INPUT_POST, "name") : '';
$desc = isset($_POST["desc"]) ? filter_input(INPUT_POST, "desc") : '';
$todos[] = array(
"id" => uniqid(),
"name" => $name,
"desc" => $desc
);
file_put_contents(TODOS . "/" . $user, json_encode(array_values($todos)));
} elseif($path === "/todos/remove") {
$isLoggedIn = isset($_SESSION[SESS]);
if(!$isLoggedIn) goto fail;
$user = $_SESSION[SESS];
if(!file_exists(TODOS . "/" . $user)) file_put_contents(TODOS . "/" . $user, "[]");
$todos = json_decode(file_get_contents(TODOS . "/" . $user));
$id = isset($_POST["id"]) ? filter_input(INPUT_POST, "id") : '';
file_put_contents(TODOS . "/" . $user, json_encode(array_values(array_filter($todos, function($item) use($id) {
return $item->id !== $id;
}))));
} elseif($path === "/account/info") {
$isLoggedIn = isset($_SESSION[SESS]);
$data["loggedIn"] = $isLoggedIn;
$data["username"] = $_SESSION[SESS];
} elseif($path === "/account/login") {
$username = isset($_POST["username"]) ? filter_input(INPUT_POST, "username") : '';
$password = isset($_POST["password"]) ? filter_input(INPUT_POST, "password") : '';
$users = json_decode(file_get_contents(USERS));
foreach($users as $user)
if($user->username === $username && password_verify($password, $user->password))
goto valid_login;
goto fail;
valid_login:
$_SESSION[SESS] = $username;
} elseif($path === "/account/register") {
$username = isset($_POST["username"]) ? filter_input(INPUT_POST, "username") : '';
$password = isset($_POST["password"]) ? filter_input(INPUT_POST, "password") : '';
$users = json_decode(file_get_contents(USERS));
foreach($users as $user)
if($user->username === $username) goto fail;
$users[] = array(
"username" => $username,
"password" => password_hash($password, PASSWORD_DEFAULT)
);
file_put_contents(USERS, json_encode($users));
} else {
http_response_code(404);
}
goto end;
fail:
http_response_code(500);
$status = 0;
end:
$end = microtime(true);
$res["data"] = $data;
$res["success"] = $status;
$res["exec_time"] = $end - $start;
echo json_encode($res);

View File

@@ -0,0 +1,81 @@
body {
margin: 0;
font-family: 'Segoe UI', sans-serif;
background: linear-gradient(to bottom, #e0f7fa, #80deea);
background-image: url('/assets/img/bg.jpg');
background-size: cover;
color: #fff;
min-height: 100vh;
backdrop-filter: blur(0px);
}
.container {
max-width: 600px;
margin: 5rem auto;
background: rgba(0, 60, 100, 0.7);
padding: 2rem;
border-radius: 15px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
}
h1 {
text-align: center;
margin-bottom: 2rem;
}
input {
width: 100%;
padding: 0.7rem;
margin: 0.5rem 0;
border: none;
border-radius: 8px;
font-size: 1rem;
}
button {
background: #4dd0e1;
color: #003c64;
border: none;
padding: 0.7rem 1.2rem;
margin: 0.5rem 0.2rem;
font-size: 1rem;
border-radius: 8px;
cursor: pointer;
transition: background 0.3s ease;
}
button:hover {
background: #00acc1;
color: #fff;
}
ul {
list-style: none;
padding: 0;
}
li {
background: rgba(255, 255, 255, 0.1);
margin: 0.5rem 0;
padding: 1rem;
border-radius: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.hidden {
display: none;
}
.todo-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.auth-buttons {
display: flex;
justify-content: space-between;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

View File

@@ -0,0 +1,52 @@
const API = new class {
register(username, password) {
const formData = new FormData();
formData.append("username", username)
formData.append("password", password)
return fetch("/api.php?path=" + encodeURIComponent("/account/register"), {
method: "POST",
body: formData
})
}
login(username, password) {
const formData = new FormData();
formData.append("username", username)
formData.append("password", password)
return fetch("/api.php?path=" + encodeURIComponent("/account/login"), {
method: "POST",
body: formData,
})
}
info() {
return fetch("/api.php?path=" + encodeURIComponent("/account/info"), {
method: "GET",
})
}
list() {
return fetch("/api.php?path=" + encodeURIComponent("/todos/list"), {
method: "GET",
})
}
add(name, desc) {
const formData = new FormData();
formData.append("name", name)
formData.append("desc", desc)
return fetch("/api.php?path=" + encodeURIComponent("/todos/add"), {
method: "POST",
body: formData
})
}
remove(id) {
const formData = new FormData();
formData.append("id", id)
return fetch("/api.php?path=" + encodeURIComponent("/todos/remove"), {
method: "POST",
body: formData
})
}
}()

View File

@@ -0,0 +1,79 @@
function register() {
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
API.register(username, password)
.then(res => res.json())
.then(data => {
alert('Registered successfully!');
}).catch(err => alert('Registration failed'));
}
function login() {
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
API.login(username, password)
.then(res => res.json())
.then(data => {
loadApp();
}).catch(err => alert('Login failed'));
}
function loadApp() {
API.info().then(res => res.json()).then(user => {
document.getElementById('auth').classList.add('hidden');
document.getElementById('todoApp').classList.remove('hidden');
document.getElementById('userDisplay').innerText = user.data.username;
loadTodos();
}).catch(err => alert('Session expired or unauthorized'));
}
function loadTodos() {
API.list()
.then(res => res.json())
.then(data => {
const todos = data.data.todos;
const list = document.getElementById('todoList');
list.innerHTML = '';
todos.forEach(todo => {
const li = document.createElement('li');
li.innerHTML = `
<div>
<strong>${todo.name}</strong><br>
<small>${todo.desc}</small>
</div>
<button onclick="deleteTodo('${todo.id}')">🗑</button>
`;
list.appendChild(li);
});
}).catch(err => alert('Failed to load todos'));
}
function addTodo() {
const name = document.getElementById('todoName').value;
const desc = document.getElementById('todoDesc').value;
API.add(name, desc)
.then(res => res.json())
.then(() => {
document.getElementById('todoName').value = '';
document.getElementById('todoDesc').value = '';
loadTodos();
}).catch(err => alert('Failed to add todo'));
}
function deleteTodo(id) {
API.remove(id)
.then(res => res.json())
.then(() => {
loadTodos();
}).catch(err => alert('Failed to delete todo'));
}
// Try auto-login
window.addEventListener('DOMContentLoaded', () => {
API.info()
.then(res => res.json())
.then(data => {
if (data.data.loggedIn) loadApp();
});
});

View File

@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Glacier TODO App</title>
<link rel="stylesheet" href="./assets/css/style.css">
<script defer src="./assets/js/api.js"></script>
<script defer src="./assets/js/app.js"></script>
</head>
<body>
<div class="container">
<h1>🗻 Glacier TODO</h1>
<div id="auth">
<input type="text" id="username" placeholder="Username">
<input type="password" id="password" placeholder="Password">
<div class="auth-buttons">
<button onclick="register()">Register</button>
<button onclick="login()">Login</button>
</div>
</div>
<div id="todoApp" class="hidden">
<div class="todo-header">
<h2>Welcome, <span id="userDisplay"></span>!</h2>
</div>
<div class="add-todo">
<input type="text" id="todoName" placeholder="Todo title">
<input type="text" id="todoDesc" placeholder="Todo description">
<button onclick="addTodo()">Add</button>
</div>
<ul id="todoList"></ul>
</div>
</div>
</body>
</html>