summaryrefslogtreecommitdiffstats
path: root/iv/orodja/napad
diff options
context:
space:
mode:
Diffstat (limited to 'iv/orodja/napad')
-rw-r--r--iv/orodja/napad/config33
-rwxr-xr-xiv/orodja/napad/exploit.sh65
-rwxr-xr-xiv/orodja/napad/submission.py41
3 files changed, 128 insertions, 11 deletions
diff --git a/iv/orodja/napad/config b/iv/orodja/napad/config
index 371faed..c71c9bd 100644
--- a/iv/orodja/napad/config
+++ b/iv/orodja/napad/config
@@ -19,6 +19,39 @@ export ROUND_DURATION=120
# When does the game start (in UTC). Used to calculate current round id.
export GAME_START=2024-09-01T07:00:00
+# Team numbers to attack
+export GAME_TEAMS={0..42}
+
+# Flag IDs URL
+export game_flag_ids_url()
+{
+ echo http://10.10.0.1:8081/flagIds?service=$1&team=$2&round=$3
+}
+export -f game_flag_ids_url
+
+# Target IP from ID
+export game_target_ip()
+{
+ echo 10.60.$1.1
+}
+export -f game_target_ip
+
+# NOP TEAM ID
+export GAME_NOP_TEAM=0
+
+# For how many rounds are flags valid at a time?
+# It doesn't make sense for this to be less than 1.
+export GAME_VALID_ROUNDS=5
+
+# Function exploit.sh should call on errors.
+# Args: service team pwd usr@pc message
+# 1 2 3 4 5
+export exploit_error_handler()
+{
+ echo "[exploit.sh] ERROR $1"
+ notify-send "exploit.sh ERROR" "$5" --urgency critical
+}
+
# ==========================
# ====== SUBMISSION.PY =====
diff --git a/iv/orodja/napad/exploit.sh b/iv/orodja/napad/exploit.sh
index 1111b00..729bb68 100755
--- a/iv/orodja/napad/exploit.sh
+++ b/iv/orodja/napad/exploit.sh
@@ -3,8 +3,8 @@ if [ x$1 = x ]
then
echo >&2 <<EOF
No command. Subcommands:
- $0 once <exploit> # runs an exploit once, print captured flags
- $1 loop <exploit> # runs an exploit in a loop once per round
+ $0 once <service> <exploit> [team=$GAME_NOP_TEAM] # runs exploit once
+ $0 loop <service> <exploit> # runs an exploit in a loop once per round
<exploit> is an executable file. Flags, grepped from stdout, are submitted.
It is called for every target. Args are target IP and flag IDs JSON object.
Example: <exploit> 10.1.2.3 '{"user": "root", "pass": "hunter2"}'
@@ -17,7 +17,68 @@ Exploits are NOT executed in parallel.
Make sure that your system time is set CORRECTLY TO THE SECOND, it's used
to get the current round id. Current time: `date`.
Configuration values are also available in environment of exploits.
+<service> is the name of the service (used for getting flag IDs)
+If the env. var EXPLOIT_STDOUT is set, stdout of exploit will be printed to stderr:
+ "EXPLOIT_STDOUT=1 $0 loop <service> <exploit>" will also print stdout to term
+If the env. var EXPLOIT_LOOP_ONCE is set (only valid for loop mode), looping will
+ stop after looping through all valid rounds and teams:
+ "EXPLOIT_LOOP_ONCE=1 $0 loop <service> <exploit>" won't wait for next round
EOF
exit 1
fi
set -xeuo pipefail
+startunix=`date +%s --utc --date $GAME_START`
+current=`date +%s --utc`
+if [ ${ROUND_ID:-x} = x ]
+then
+ export ROUND_ID=`$((($current-$startunix)/$ROUND_DURATION))` # BREAKS WHEN THERE ARE LEAP SECONDS DURING GAME
+fi
+subcommand=$1
+service=$2
+exploit=$3
+# tees stdout, collects flags, puts stdout to stderr, prints counts
+exploit_pipe()
+{
+ stdoutwhere=/dev/null
+ [ ${EXPLOIT_STDOUT:-x} = x ] && stdoutwhere=/dev/stderr
+ tee $stdoutwhere | grep -Eo "$FLAG_REGEX" | while read line
+ do
+ echo $line `whoami`@`hostname``pwd` $exploit $service
+ done | nc -v $SUBMISSION_HOST $SUBMISSION_PORT | cut -d\ -f1 | sort | uniq -c | tr $'\n' ' ' | cat /dev/stdin <(echo $'\t<= izkupiček poslanih zastavic')
+}
+# args: team round
+get_flag_ids()
+{
+ output_flagids=$(curl --fail-with-body --no-progress-bar `game_flag_ids_url $service $1 $2`)
+ echo [$0] ERROR: failed to get flag ids: $output_flagids >&2
+}
+# args: team message
+send_error()
+{
+ echo [$0] ERROR: team=$1: $2
+ exploit_error_handler $service $1 `pwd` `whoami`@`hostname` $2
+}
+case $subcommand in
+ once)
+ target_team=$GAME_NOP_TEAM
+ if [ $# -ge 3 ]
+ then
+ target_team=$3
+ fi
+ timeout $EXPLOIT_TIMEOUT $exploit `game_target_ip $target_team` `get_flag_ids $target_team $ROUND_ID` | expoit_pipe $subcommand
+ exit_code=$?
+ if [ ! $exit_code -eq 0 ] && [ ! $exit_code -eq 124 ]
+ then
+ send_error $target_team "$exploit exited with $exit_code"
+ fi
+ ;;
+ loop)
+ for round in {$ROUND_ID..}
+ do
+ for target_team in $GAME_TEAMS
+ do
+ ROUND_ID=$round $0 once $target_team
+ done
+ done
+ ;;
+esac
diff --git a/iv/orodja/napad/submission.py b/iv/orodja/napad/submission.py
index dcf75ca..30e8257 100755
--- a/iv/orodja/napad/submission.py
+++ b/iv/orodja/napad/submission.py
@@ -5,15 +5,27 @@ import re
import sqlite3
import aiohttp
db = sqlite3.connect(os.getenv("SUBMISSION_DB", "flags.db"))
-db.execute("CREATE TABLE IF NOT EXISTS flags (id INTEGER PRIMARY KEY, flag TEXT NOT NULL UNIQUE, sent INTEGER NOT NULL DEFAULT 0, date TEXT DEFAULT (strftime('%FT%R:%f', 'now')) NOT NULL, status TEXT, msg TEXT) STRICT")
+db.execute("CREATE TABLE IF NOT EXISTS flags (id INTEGER PRIMARY KEY, flag TEXT NOT NULL UNIQUE, team INTEGER, service BLOB, round INTEGER, context BLOB, sent INTEGER NOT NULL DEFAULT 0, date TEXT DEFAULT (strftime('%FT%R:%f', 'now')) NOT NULL, status TEXT, msg TEXT) STRICT")
flag_regex = re.compile(os.getenv("FLAG_REGEX", "^[A-Z0-9]{31}=$").encode(), re.ASCII | re.DOTALL | re.VERBOSE)
async def submitter ():
while True:
print("submitter loop")
+ flags_balance = dict()
+ unsent_flags = 0
+ for flag, team, service in db.execute("SELECT flag, team, service FROM flags WHERE sent == 0 ORDER BY date DESC"):
+ if (team, service) not in flags_balance.keys():
+ flags_balance[(team, service)] = []
+ flags_balance[(team, service)].append(flag)
+ unsent_flags += 1
flags = []
- for row in db.execute("SELECT flag FROM flags WHERE sent == 0 ORDER BY date DESC"):
- if len(flags) < int(os.getenv("SUBMISSION_MAX_FLAGS", "2560")):
- flags.append(row[0])
+ while len(flags) < int(os.getenv("SUBMISSION_MAX_FLAGS", "2560")) and unsent_flags > 0: # to zna biti počasno, najdi lepši način
+ for key in flags_balance.keys():
+ try:
+ zastava = flags_balance[key].pop(0)
+ except IndexError:
+ flags_balance.pop(key)
+ else:
+ flags.append(zastava)
if len(flags) == 0:
await asyncio.sleep(1)
for i in [1]:
@@ -43,15 +55,26 @@ async def handle_client (reader, writer):
if buffer.startswith(b'#'):
writer.write(str(len(db.execute(buffer[1:].decode()).fetchall())).encode() + b'\n')
continue
- if re.match(flag_regex, buffer) == None:
+ if re.match(flag_regex, buffer.split(b' ')[0]) == None:
writer.write(b'BAD_FLAG\n')
continue
- flag = buffer.decode()
+ # SUBMISSION LINE FORMAT: "flag teamnumber roundnumber service any other context"
+ flag = buffer.split(b' ')[0].decode()
+ context = b' '.join(buffer.split(b' ')[1:])
try:
- db.execute("INSERT INTO flags (flag) VALUES (?)", [flag])
+ team = int(buffer.split(b' ')[1].decode())
+ except (ValueError, UnicodeDecodeError):
+ team = -1
+ try:
+ runda = int(buffer.split(b' ')[2].decode())
+ except (ValueError, UnicodeDecodeError):
+ runda = -1
+ service = buffer.split(b' ')[3]
+ try:
+ db.execute("INSERT INTO flags (flag, team, service, round, context) VALUES (?, ?)", [flag, team, service, runda, context])
except sqlite3.IntegrityError:
- status, msg, date = [x for x in db.execute("SELECT status, msg, date FROM flags WHERE flag=?", [flag])][0]
- writer.write(b"OLD_FLAG " + date.encode() + b" " + str(status).encode() + b" " + str(msg).encode() + b"\n")
+ status, msg, date, context = [x for x in db.execute("SELECT status, msg, date, context FROM flags WHERE flag=?", [flag])][0]
+ writer.write(b"OLD_FLAG " + date.encode() + b"\t" + str(status).encode() + b"\t" + str(context).encode() + b"\t" + str(msg).encode() + b"\n")
else:
writer.write(b'NEW_FLAG\n')
writer.close()