sploit for arena-battle
build-and-push / detect (push) Successful in 10s
build-and-push / build (${{ fromJSON(needs.detect.outputs.services) }}) (push) Successful in 51s

This commit is contained in:
2026-08-26 11:29:05 +03:00
parent 593500424a
commit 5d765a09db
1436 changed files with 539800 additions and 14 deletions
+65 -13
View File
@@ -21,6 +21,33 @@
#include <signal.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <cstdint>
#include <random>
// Auth tokens must be unguessable: NOTE_GET authenticates on the token alone.
// Keeps the original "token_<id>_<digits>" shape so clients see no change.
std::ifstream urandom_source("/dev/urandom", std::ios::binary);
std::mutex urandom_mutex;
std::string gen_auth_token(int id) {
uint64_t v = 0;
{
std::lock_guard<std::mutex> lock(urandom_mutex);
if (urandom_source.is_open()) {
urandom_source.read(reinterpret_cast<char*>(&v), sizeof(v));
if (!urandom_source) {
urandom_source.clear();
v = 0;
}
}
}
if (v == 0) {
std::random_device rd;
v = (static_cast<uint64_t>(rd()) << 32) ^ static_cast<uint64_t>(rd());
}
v |= (1ULL << 63);
return "token_" + std::to_string(id) + "_" + std::to_string(v);
}
struct Item {
int id;
@@ -151,7 +178,7 @@ int create_fighter(const std::string& name, const std::string& fighter_class) {
f.last_activity = now();
f.active = true;
f.auth_token = "token_" + std::to_string(f.id) + "_" + std::to_string(rand());
f.auth_token = gen_auth_token(f.id);
if (fighter_class == "warrior") {
f.strength = 15;
@@ -227,20 +254,18 @@ std::string cmd_buy(int client_fd, const std::vector<std::string>& args) {
Fighter& f = fighters[fid];
Item& item = items[item_id];
int total_price = item.price * quantity;
int64_t total_price = static_cast<int64_t>(item.price) * static_cast<int64_t>(quantity);
if (total_price < 0) {
f.gold += abs(total_price);
f.last_activity = now();
return "OK: Overflow exploited! Gold:" + std::to_string(f.gold);
if (total_price > 1000000000LL) {
return "ERROR: Invalid quantity";
}
if (f.gold < total_price) {
return "ERROR: Not enough gold. Need:" + std::to_string(total_price) +
" Have:" + std::to_string(f.gold);
}
f.gold -= total_price;
f.gold -= static_cast<int>(total_price);
for (int i = 0; i < quantity; i++) {
f.inventory.push_back(item_id);
}
@@ -389,6 +414,7 @@ std::string cmd_attack(int client_fd, const std::vector<std::string>&) {
Fighter& attacker = fighters[fid];
int defender_id = (b.fighter1_id == fid) ? b.fighter2_id : b.fighter1_id;
if (!fighters.count(defender_id)) return "ERROR: Opponent not found";
Fighter& defender = fighters[defender_id];
// Calculate damage
@@ -487,6 +513,7 @@ std::string cmd_status(int client_fd) {
Battle& b = battles[bid];
Fighter& f = fighters[fid];
int opponent_id = (b.fighter1_id == fid) ? b.fighter2_id : b.fighter1_id;
if (!fighters.count(opponent_id)) return "ERROR: Opponent not found";
Fighter& opponent = fighters[opponent_id];
std::stringstream ss;
@@ -561,8 +588,11 @@ std::string cmd_note_get(const std::vector<std::string>& args) {
return "ERROR: Note not found";
}
static const int MAX_BAD_TOKENS = 16;
void handle_client(int client_fd) {
char buffer[1024];
int bad_tokens = 0;
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
@@ -593,6 +623,9 @@ void handle_client(int client_fd) {
std::string response;
{
std::lock_guard<std::mutex> lock(state_mutex);
if (cmd == "REGISTER") {
response = cmd_register(client_fd, args);
} else if (cmd == "FIGHTER") {
@@ -615,20 +648,37 @@ void handle_client(int client_fd) {
response = cmd_note_create(client_fd, args);
} else if (cmd == "NOTE_GET") {
response = cmd_note_get(args);
if (response.rfind("ERROR: Invalid auth token", 0) == 0) {
bad_tokens++;
}
} else if (cmd == "LOGOUT") {
response = cmd_logout(client_fd);
} else if (cmd == "QUIT" || cmd == "EXIT") {
response = "BYE";
write(client_fd, response.c_str(), response.length());
break;
} else {
response = "ERROR: Unknown command: " + cmd;
}
}
// QUIT/EXIT keeps its original reply: "BYE" with no trailing newline.
if (cmd == "QUIT" || cmd == "EXIT") {
write(client_fd, response.c_str(), response.length());
break;
}
response += "\n";
write(client_fd, response.c_str(), response.length());
// Throttle token guessing: a legitimate client never needs this many.
if (bad_tokens >= MAX_BAD_TOKENS) {
LOG(client_str + " disconnected: too many invalid auth tokens");
break;
}
}
std::lock_guard<std::mutex> lock(state_mutex);
// Cleanup
if (client_fighter.count(client_fd)) {
int fid = client_fighter[client_fd];
@@ -667,7 +717,8 @@ void cleanup_thread() {
for (auto cf = client_fighter.begin(); cf != client_fighter.end(); ) {
if (cf->second == fid) {
close(cf->first);
// Do not close(): the handler thread still owns this fd
// and the number would be recycled onto another client.
cf = client_fighter.erase(cf);
} else {
++cf;
@@ -733,7 +784,8 @@ int main(int argc, char* argv[]) {
return 1;
}
if (listen(server_fd, 10) < 0) {
// Backlog of 10 let a burst of connections lock the checker out.
if (listen(server_fd, 256) < 0) {
return 1;
}