glacier ctf 25
This commit is contained in:
89
2025/glacier/web/glacier-todo/web/api.php
Normal file
89
2025/glacier/web/glacier-todo/web/api.php
Normal 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);
|
||||
|
||||
81
2025/glacier/web/glacier-todo/web/assets/css/style.css
Normal file
81
2025/glacier/web/glacier-todo/web/assets/css/style.css
Normal 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;
|
||||
}
|
||||
|
||||
BIN
2025/glacier/web/glacier-todo/web/assets/img/bg.jpg
Normal file
BIN
2025/glacier/web/glacier-todo/web/assets/img/bg.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 396 KiB |
52
2025/glacier/web/glacier-todo/web/assets/js/api.js
Normal file
52
2025/glacier/web/glacier-todo/web/assets/js/api.js
Normal 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
|
||||
})
|
||||
}
|
||||
}()
|
||||
79
2025/glacier/web/glacier-todo/web/assets/js/app.js
Normal file
79
2025/glacier/web/glacier-todo/web/assets/js/app.js
Normal 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();
|
||||
});
|
||||
});
|
||||
|
||||
38
2025/glacier/web/glacier-todo/web/index.html
Normal file
38
2025/glacier/web/glacier-todo/web/index.html
Normal 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>
|
||||
Reference in New Issue
Block a user