Major rewrite in python, text editors no longer allowed

This commit is contained in:
sose 2021-11-16 18:11:29 +00:00
parent 5b29bde02a
commit 13e2f4b745
21 changed files with 191 additions and 228 deletions

6
.gitignore vendored
View File

@ -1,3 +1,9 @@
.*.swp .*.swp
.*.swo .*.swo
*.pubkey
*.pub
sose.pubkey sose.pubkey
table.route
solutions.txt
oldstuff/
image/inhere

View File

@ -18,7 +18,7 @@ COPY --chown=root ./slbr.env /etc
COPY --chown=root ./package.list /package.list COPY --chown=root ./package.list /package.list
COPY --chown=root ./insults.txt /insults.txt COPY --chown=root ./insults.txt /insults.txt
COPY ./bin/progress /usr/local/bin/ COPY ./bin/scores /usr/local/bin/
COPY ./bin/submit /usr/local/bin/ COPY ./bin/submit /usr/local/bin/
COPY ./banner /etc/motd COPY ./banner /etc/motd
COPY ./pubkey /pubkey COPY ./pubkey /pubkey
@ -28,12 +28,15 @@ RUN apt remove bash-completion \
RUN apt update \ RUN apt update \
&& apt install -y $(cat /package.list) \ && apt install -y $(cat /package.list) \
&& rm /package.list && apt -y remove nano \
&& rm /package.list \
&& rm /bin/grep \
&& rm /bin/tar
RUN sed -i "s/{{ HOST_IP }}/$HOST_IP/g" /etc/bash.bashrc /usr/local/bin/progress /usr/local/bin/submit \ RUN sed -i "s/{{ HOST_IP }}/$HOST_IP/g" /etc/bash.bashrc /usr/local/bin/scores /usr/local/bin/submit \
&& sed -i "s/{{ SLBR_PORT }}/$SLBR_PORT/g" /etc/bash.bashrc /usr/local/bin/progress /usr/local/bin/submit \ && sed -i "s/{{ SLBR_PORT }}/$SLBR_PORT/g" /etc/bash.bashrc /usr/local/bin/scores /usr/local/bin/submit \
&& sed -i "s/{{ INFO_PORT }}/$INFO_PORT/g" /etc/bash.bashrc /usr/local/bin/progress /usr/local/bin/submit \ && sed -i "s/{{ INFO_PORT }}/$INFO_PORT/g" /etc/bash.bashrc /usr/local/bin/scores /usr/local/bin/submit \
&& sed -i "s/{{ LOG_PORT }}/$LOG_PORT/g" /etc/bash.bashrc /usr/local/bin/progress /usr/local/bin/submit \ && sed -i "s/{{ LOG_PORT }}/$LOG_PORT/g" /etc/bash.bashrc /usr/local/bin/scores /usr/local/bin/submit \
&& mkdir /home/$USERNAME \ && mkdir /home/$USERNAME \
&& mkdir /run/sshd \ && mkdir /run/sshd \
&& mkdir /home/$USERNAME/.ssh \ && mkdir /home/$USERNAME/.ssh \
@ -42,7 +45,7 @@ RUN sed -i "s/{{ HOST_IP }}/$HOST_IP/g" /etc/bash.bashrc /usr/local/bin/progress
&& echo "PasswordAuthentication no" >> /etc/ssh/sshd_config \ && echo "PasswordAuthentication no" >> /etc/ssh/sshd_config \
&& echo "Port $SSHD_PORT" >> /etc/ssh/sshd_config && echo "Port $SSHD_PORT" >> /etc/ssh/sshd_config
COPY ./clues /home/$USERNAME/clues COPY ./inhere /home/$USERNAME/inhere
COPY ./rules.md /home/$USERNAME COPY ./rules.md /home/$USERNAME
RUN useradd $USERNAME -d /home/$USERNAME \ RUN useradd $USERNAME -d /home/$USERNAME \

View File

@ -10,4 +10,4 @@ Welcome to:
For rules, `cat ~/rules.md` For rules, `cat ~/rules.md`
To submit your solution for a challenge, run `submit <solution number> "<your solution>"` To submit your solution for a challenge, run `submit <solution number> "<your solution>"`
To see your current progress, run `progress` To see your current scores, run `scores`

View File

@ -1,20 +0,0 @@
#!/bin/bash
scoreboard="$(nc -q0 -d {{ HOST_IP }} {{ INFO_PORT }})"
my_ip="$(ip addr | grep 'scope global eth0' | cut -f6 -d ' ')"
total_challenges="$(echo "$scoreboard" | head -n1)"
completed_challenges="$(
echo "$scoreboard" \
| grep "$my_ip" \
| cut -f2- -d ' ' \
| tr -d ' ' \
| tr ',' '\n' \
| sort \
| tr '\n' ',' \
| sed 's/,/, /g' \
| sed 's/^, //' \
| sed 's/, $//'
)"
num_completed="$(echo $completed_challenges | grep -oE '[0-9]' | wc -l)"
echo "There are $total_challenges total challenges"
echo "You have completed the following challenges: $completed_challenges"
echo "You have completed $num_completed of the $total_challenges total challenges"

4
image/bin/scores Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
echo "SCORE" \
| nc {{ HOST_IP }} {{ SLBR_PORT }}

View File

@ -5,54 +5,10 @@ submit.sh - submit an slbr solution to be verified
usage: ./submit <challenge number> <solution> usage: ./submit <challenge number> <solution>
for example: ./submit 3 \"this is the solution for challenge 3\" for example: ./submit 3 \"this is the solution for challenge 3\"
" "
challenge_no="$1" challenge_no="$1"
submitted_solution="$2" submitted_solution="$2"
[ -z "$submitted_solution" ] && echo "$help_text" && exit [ -z "$submitted_solution" ] && echo "$help_text" && exit
echo "Submitting \"$submitted_solution\" as the solution to challenge #$challenge_no..." echo "SUBMIT $challenge_no $submitted_solution" \
| nc {{ HOST_IP }} {{ SLBR_PORT }}
my_ip="$(ip addr | grep 'scope global eth0' | cut -f6 -d ' ')"
scoreboard="$(nc -q0 -d {{ HOST_IP }} {{ INFO_PORT }})"
prev_score="$(
echo "$scoreboard" \
| grep "$my_ip"
)"
max_score="$(echo "$scoreboard" | head -n1)"
current_score=""
echo "SOLUTION $challenge_no $submitted_solution" \
| nc -q0 {{ HOST_IP }} {{ SLBR_PORT }}
# give the server ample time to process the request
echo "Verifying..."
sleep 5
# we need to request the scoreboard twice here for the score to properly update
nc -q0 -d {{ HOST_IP }} {{ INFO_PORT }} > /dev/null
current_score="$(
nc -q0 -d {{ HOST_IP }} {{ INFO_PORT }} \
| grep "$my_ip"
)"
if [ "$prev_score" != "$current_score" ]
then
echo "w00t! You got the right answer!!!"
num_correct="$(
echo $current_score \
| grep -oE '[0-9],' \
| wc -l
)"
echo "$num_correct correct answers so far"
if [ "$num_correct" = "$max_score" ]
then
printf "\e[0;92]"
echo "Hey! thats all $max_score! that means YOU WIN!"
echo "!!!!!!!CONGRATULATIONS!!!!!!!"
printf "\e[0;91m THIS CONTAINER WILL NOW SELF DESTRUCT\e[0m\n"
fi
else
echo "|X_x| Your answer was rejected!!!."
fi

View File

@ -1 +0,0 @@
ubj qb lbh fcryy ahzoref?

View File

@ -1,9 +1,5 @@
netcat-openbsd netcat-openbsd
emacs-nox
curl
nano nano
vim
tmux tmux
man man
xxd
openssh-server openssh-server

View File

@ -19,6 +19,7 @@ are subject to normal bash escaping rules)
- You may write scripts - You may write scripts
- You may not use sh, you must use bash - You may not use sh, you must use bash
- You may not install more packages - You may not install more packages
- You may not use programs other than the ones provided on your container
- You may not attempt to obtain root access - You may not attempt to obtain root access
- You may not communicate with other players - You may not communicate with other players
- You may not interact or tamper with the containers or environments of other - You may not interact or tamper with the containers or environments of other

View File

@ -15,7 +15,7 @@ function command_not_found_handle {
} }
function log_command { function log_command {
echo "$(history 1)" | nc -q0 {{ HOST_IP }} {{ LOG_PORT }} echo "LOG $(history 1)" | nc -q0 {{ HOST_IP }} {{ SLBR_PORT }}
} }
readonly -f command_not_found_handle readonly -f command_not_found_handle

View File

@ -1,3 +1,5 @@
export XDG_RUNTIME_DIR=/home/slbr-admin/.docker/run
export DOCKER_HOST=unix:///home/slbr-admin/.docker/run/docker.sock
username="$1" username="$1"
pubkey_path="$2" pubkey_path="$2"
slbr_port="1337" slbr_port="1337"
@ -30,12 +32,7 @@ get_user_sshd_port() {
get_user_sshd_port get_user_sshd_port
cp "$pubkey_path" image/pubkey cp "$pubkey_path" image/pubkey
host_ip="$( host_ip="host.docker.internal"
ip -br -4 addr \
| grep eth0 \
| awk '{print $3}' \
| cut -f 1 -d '/'
)"
docker build \ docker build \
-t slbr:$username \ -t slbr:$username \
@ -59,6 +56,8 @@ container_id="$(
--cap-add SETPCAP \ --cap-add SETPCAP \
--cap-add SETUID \ --cap-add SETUID \
--cap-add SYS_CHROOT \ --cap-add SYS_CHROOT \
--network slbr \
--add-host host.docker.internal:host-gateway \
-p $user_sshd_port:$user_sshd_port \ -p $user_sshd_port:$user_sshd_port \
-h slbr \ -h slbr \
-d "slbr:$username" -d "slbr:$username"

View File

@ -22,21 +22,20 @@ contact ~ben or sose on tilde.chat (in the #slbr channel) to sign up.
## Configuration ## Configuration
- Configuring a new set of challenges is as simple as editing the - Configuring a new set of challenges is as simple as editing the
`solutions.txt` file with their solutions, and providing new clues in the `solutions.txt` file with their solutions, and providing new clues in the
`./image/clues` folder. `./image/inhere` folder.
- In the `solutions.txt` file, the line number of each solution corresponds to - In the `solutions.txt` file, the line number of each solution corresponds to
the challenge it is the solution for. Solutions may not take up multiple lines. the challenge it is the solution for. Solutions may not take up multiple lines.
## Prerequisites ## Prerequisites
- docker - docker
- pslist
- jq - jq
- BSD style netcat (`netcat-openbsd` on debian) - python3
- Twisted (`pip3 install twisted`)
- Python Docker SDK (`pip3 install docker`)
## Pre-Setup ## Pre-Setup
- Make sure your ports 1337, 1338 and 1339 are not exposed to the internet, as - Make sure your port 1337 is not exposed to the internet, as SLBR will use it
SLBR will use them internally internally
- Make sure to employ user namespace remapping with docker, eg. by starting
dockerd with `dockerd --userns-remap=default`
## Setup ## Setup
- `mkdir /home/slbr-admin` - `mkdir /home/slbr-admin`
@ -47,10 +46,11 @@ contact ~ben or sose on tilde.chat (in the #slbr channel) to sign up.
- `cd` - `cd`
- `git clone https://tildegit.org/sose/SLBRV2` - `git clone https://tildegit.org/sose/SLBRV2`
- `cd SLBRV2` - `cd SLBRV2`
- Run the docker daemon rootlessly, i.e. with `dockerd-rootless.sh`
- You are now ready to manage an slbr game - You are now ready to manage an slbr game
## Running the game ## Running the game
- run `./start.sh` to start the listeners for various game events - run `./start.sh` to start the sever listening for various game events
- run `./new_image.sh <user name> </path/to/pubkey>` to create a new user - run `./new_image.sh <user name> </path/to/pubkey>` to create a new user
- All game events and user commands will be logged to log.txt - All game events and user commands will be logged to log.txt
- You can `./reset.sh` to cleanup after a game has finished - You can `./reset.sh` to cleanup after a game has finished

View File

@ -1,8 +1,6 @@
#!/bin/sh #!/bin/sh
num_challenges="$( export XDG_RUNTIME_DIR=/home/slbr-admin/.docker/run
wc -l solutions.txt \ export DOCKER_HOST=unix:///home/slbr-admin/.docker/run/docker.sock
| cut -f1 -d ' '
)"
docker ps \ docker ps \
-f label="description=SLBR User Container" \ -f label="description=SLBR User Container" \
--format "{{.ID}}" \ --format "{{.ID}}" \
@ -12,5 +10,5 @@ docker images -a \
| awk '{print $3}' \ | awk '{print $3}' \
| xargs docker rmi -f | xargs docker rmi -f
printf "$num_challenges\n" > scoreboard.txt
printf "" > log.txt printf "" > log.txt
rm scores/*

View File

@ -1 +0,0 @@
5

0
scores/.gitkeep Normal file
View File

145
server.py Executable file
View File

@ -0,0 +1,145 @@
#!/usr/bin/env python3
import os
import docker
import ipaddress
from twisted.protocols.basic import LineReceiver
from twisted.internet.protocol import Factory, Protocol
from twisted.internet.endpoints import TCP4ServerEndpoint
from twisted.internet import reactor
class SLBRManager(Protocol):
def __init__(self, config):
self.client_addr = None
self.config = config
def suicide(self):
slbr_containers = self.config['docker_client'].containers.list(
filters = {'label': 'description=SLBR User Container'}
)
for container in slbr_containers:
if container.attrs['NetworkSettings']['Networks']['slbr']['IPAddress'] == self.client_addr:
container.remove(force=True)
def connectionMade(self):
self.client_addr = self.transport.getPeer().host
if (
ipaddress.ip_address(self.client_addr)
not in
ipaddress.ip_network(self.config['docker_netmask'])
):
print('Dropping client {} not in docker network'.format(self.client_addr))
self.transport.loseConnection()
def dataReceived(self, data):
message = data.decode('utf8').split(' ')
command = message[0].replace('\n', '')
if len(message) > 1:
args = message[1:]
if command == 'LOG':
entry = '[{}]: {}'.format(self.client_addr, ' '.join(args))
print(entry)
elif command == 'DEAD':
print('Killing {}'.format(self.client_addr))
self.suicide()
self.transport.loseConnection()
elif command == 'SCORE':
message = 'PLAYER | CHALLENGES COMPLETED\n' + ('-' * 30) + '\n'
slbr_containers = self.config['docker_client'].containers.list(
filters = {'label': 'description=SLBR User Container'}
)
for player_addr in os.listdir('./scores'):
player_name = 'err'
for container in slbr_containers:
if container.attrs['NetworkSettings']['Networks']['slbr']['IPAddress'] == player_addr:
player_name = container.attrs['Config']['Image'].replace('slbr:', '')
break
message += player_name + ': '
with open('./scores/{}'.format(player_addr)) as score_file:
completed_challenges = ', '.join(score_file.readlines()).replace('\n', '')
message += completed_challenges + '\n'
self.transport.write(bytes(message, 'utf8'))
self.transport.loseConnection()
elif command == 'SUBMIT' and len(args) == 2:
try:
challenge_num = int(args[0])
except ValueError:
self.transport.loseConnection()
return
submitted_solution = args[1].replace('\n', '')
with open(self.config['solutions_file']) as solutions_file:
correct_solutions = solutions_file.readlines()
correct_solutions = list(map(lambda x: x.replace('\n', ''), correct_solutions))
num_solutions = len(correct_solutions)
print(
'{} submitted \"{}\" as solution for challenge {}'.format(
self.client_addr,
submitted_solution,
challenge_num
)
)
game_ended = False
if challenge_num <= num_solutions:
print('(Correct answer is {})'.format(correct_solutions[challenge_num -1]))
if correct_solutions[challenge_num -1] == submitted_solution:
message = 'w00t! You got the right answer!!!'
with open(
'{}/{}'.format(
self.config['scores_folder'],
self.client_addr
), 'a+'
) as score_file:
score_file.seek(0)
completed_challenges = score_file.readlines()
completed_challenges = list(
map(lambda x: x.replace('\n', ''), completed_challenges)
)
if str(challenge_num) in completed_challenges:
message = 'You already submitted the correct answer for that challenge!'
else:
score_file.write(str(challenge_num) + '\n')
if len(completed_challenges) + 1 == num_solutions:
message = (
'w00t! You got the right answer!!!\n'
+ 'Hey! thats all {}! that means YOU WIN!\n'.format(num_solutions)
+ '!!!!!!!CONGRATULATIONS!!!!!!!\n'
+ 'THIS CONTAINER WILL NOW SELF DESTRUCT'
)
game_ended = True
else:
message = '|X_x| answer was rejected!!!.'
else:
message = ('What? You tried to submit a solution for challenge '
+ '{} but there are only {} challenges!'.format(
challenge_num,
num_solutions
))
print(message.replace('You', 'User'))
self.transport.write(bytes(message + '\n', 'utf8'))
self.transport.loseConnection()
if game_ended:
self.suicide()
class SLBRManagerFactory(Factory):
def __init__(self, config):
self.config = config
def buildProtocol(self, addr):
return SLBRManager(self.config)
docker_client = docker.from_env()
endpoint = TCP4ServerEndpoint(reactor, 1337)
endpoint.listen(SLBRManagerFactory({
'docker_client': docker_client,
'docker_netmask': '172.19.0.0/16',
'scores_folder': './scores',
'solutions_file': './solutions.txt',
'log_file': './log.txt'
}))
reactor.run()

View File

@ -1,87 +0,0 @@
#!/bin/sh
# we will need the suicide bashrc to continuously write DEAD to the nc socket
# in case two messages are sent at the same time.
# this will not work if your slbr admin is not in the "docker" group
PORT="$1"
LOG="log.txt"
[ -z "$1" ] && echo "port?" && exit
docker_subnet_mask="$(
docker network inspect bridge \
| jq '.[0].IPAM.Config[0].Subnet' \
| tr -d '"' \
| cut -f2 -d '/'
)"
handle_packet() {
packet="$1"
message="$(echo $packet | cut -f3 -d '|')"
address="$(
echo "$packet" \
| cut -f2 -d '|' \
| grep -Eo \
'(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}'
)"
address="$address/$docker_subnet_mask"
if [ "$message" = "DEAD" ]
then
echo "Killing user with ip $address" >> $LOG
container_name="$(
docker network inspect bridge \
| jq \
'.[0].Containers
| map(select(.IPv4Address == "'"$address"'"))
| .[0].Name' \
| tr -d '"'
)"
docker rm -f "$container_name"
echo "killed container $container_name" >> $LOG
elif [ "$(echo $message | cut -f1 -d ' ')" = "SOLUTION" ]
then
submitted_solution="$(echo $message | cut -f3- -d ' ')"
challenge_no="$(echo $message | cut -f2 -d ' ')"
correct_solution="$(sed $challenge_no'q;d' solutions.txt)"
sed_escaped_address="$(echo $address | sed 's/\./\\./g')"
echo "$address submitted solution for challenge $challenge_no: $submitted_solution" >> $LOG
if [ "$submitted_solution" = "$correct_solution" ]
then
num_correct=""
echo "Solution was correct!" >> $LOG
grep -qE "^$address" scoreboard.txt \
|| echo "$address" >> scoreboard.txt
grep -qE "^$address .*$challenge_no," scoreboard.txt \
|| sed -i "s|$sed_escaped_address|$address $challenge_no,|" scoreboard.txt
num_correct="$(
grep -m1 -E "^$address" scoreboard.txt \
| grep -oE '[0-9],' \
| wc -l
)"
total_solutions="$(cat solutions.txt | wc -l)"
if [ $num_correct = $total_solutions ]
then
echo "$address HAS WON THE GAME!!!" >> $LOG
sleep 5
echo "Killing all continers..." >> $LOG
docker ps -aq --filter label="description=SLBR User Container" | xargs docker rm -f
exit
fi
echo "$num_correct correct answers so far" >> $LOG
fi
fi
}
echo "game server listening on $PORT"
while true
do
packet=$(nc -w 1 -W 1 -nvlp $PORT 2>&1)
handle_packet "$(echo "$packet" | head -n 3 | tr '\n' '|')" &
done

View File

@ -1,6 +0,0 @@
#!/bin/sh
PORT="$1"
LOG="log.txt"
[ -z "$1" ] && echo "port?" && exit
echo "log server listening on $PORT" >> $LOG
nc -w1 -W1 -nvlkp $PORT >> $LOG 2>&1

View File

@ -1,8 +0,0 @@
#!/bin/sh
PORT="$1"
[ -z "$1" ] && echo "port?" && exit
echo "scoreboard server listening on $PORT"
while (cat scoreboard.txt | nc -w 1 -W 1 -lp "$PORT")
do
echo "Got scoreboard request."
done

View File

@ -1,5 +0,0 @@
one
two
three
four
five

View File

@ -1,22 +1,5 @@
#!/bin/sh #!/usr/bin/sh
LOG="log.txt" SLBR_DIR='/home/slbr-admin/SLBRV2'
death_pid="" docker network rm slbr
score_pid="" docker network create --driver=bridge --subnet=172.19.0.0/16 slbr
log_pid="" nsenter -U --preserve-credentials -n -m -t $(cat $XDG_RUNTIME_DIR/docker.pid) sh -c "cd $SLBR_DIR && ./server.py"
cleanup() {
rkill $death_pid >/dev/null 2>&1
rkill $score_pid >/dev/null 2>&1
rkill $log_pid >/dev/null 2>&1
}
trap 'cleanup' 1 2 3 9
servers/deathlistener.sh 1337 & death_pid="$!"
echo "death listener pid: $death_pid" >> "$LOG"
servers/scoreboard.sh 1338 & score_pid="$!"
echo "scoreboard server pid: $score_pid" >> "$LOG"
servers/gamelog.sh 1339 & log_pid="$!"
echo "log server pid: $log_pid" >> "$LOG"
printf "\e[0;91mReading log, exit to stop game\e[0m\n"
tail -f "$LOG"