Skip to content

Commit 7f061aa

Browse files
committed
asyncronous service and checker fixes
1 parent 66656ae commit 7f061aa

File tree

4 files changed

+179
-9
lines changed

4 files changed

+179
-9
lines changed

checker/checker.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
#!/usr/bin/env python3
33
from enochecker import *
44

5+
import binascii
56
from binascii import unhexlify, hexlify
7+
68
from Crypto.PublicKey import RSA
79
from Crypto.Cipher import DES
810
from Crypto.Util.number import long_to_bytes, bytes_to_long
@@ -53,13 +55,15 @@ def putflag(self): # type: () -> None
5355
self.get_pubkey()
5456
key = RSA.import_key(self.team_db['pubkey'])
5557

58+
conn = self.connect()
59+
expect_command_prompt(conn)
60+
5661
content = 'flag %s %d' % (encrypt(self.flag, key), self.flag_round)
5762
signature = sign(content, private_key)
5863

5964
input_data = ('receive %s %s' % (content, signature)).encode()
60-
conn = self.connect()
61-
expect_command_prompt(conn)
6265
conn.write(input_data + b"\n")
66+
self.debug(f"Sent msg to client: {input_data}")
6367

6468
try:
6569
ret = expect_command_prompt(conn).decode().strip().split(":")
@@ -210,7 +214,12 @@ def getnoise(self): # type: () -> None
210214
joke_id = self.team_db[self.noise_key() + "joke_id"]
211215
conn.write(f"send {joke_id}\n".encode() )
212216
joke_hex = expect_command_prompt(conn).decode().strip()
213-
joke = unhexlify(joke_hex).decode()
217+
self.debug(f"joke recieved: {joke_hex}, len {len(joke_hex)}")
218+
try:
219+
joke = unhexlify(joke_hex).decode()
220+
except binascii.Error:
221+
self.debug("failed to decode joke-hex")
222+
raise BrokenServiceException("Retrieved invalid joke")
214223

215224
joke_orig = self.team_db[self.noise_key() + "joke"]
216225
self.debug(f"{joke_orig}, {joke}")
@@ -251,7 +260,7 @@ def exploit(self):
251260
pass
252261

253262
def expect_command_prompt(conn):
254-
return conn.readline_expect(b'command: ',b'command: ').split(b'command')[0] # need colon and space in split?
263+
return conn.readline_expect(b'command: ',b'command: ').split(b'command: ')[0] # need colon and space in split?
255264

256265
app = CryStoreChecker.service # This can be used for uswgi.
257266
if __name__ == "__main__":

service/Dockerfile

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
1-
FROM ubuntu:18.04
1+
FROM ubuntu:20.04
22

33
RUN apt-get update && apt-get -y upgrade
44
RUN apt-get install -y python3-pip
5-
RUN apt-get install -y socat
5+
# RUN apt-get install -y socat
66
RUN apt-get install -y sqlite
77

88
RUN python3 -m pip install pycryptodome
9+
RUN python3 -m pip install aiosqlite
910

1011
RUN mkdir /service
1112
WORKDIR /service
1213
RUN mkdir data
1314

14-
COPY cry.py crypto.py init_client.sql checker.pubkey run.sh ./
15+
COPY cry_async.py crypto.py init_client.sql checker.pubkey run.sh ./
1516

1617
RUN groupadd -g 2000 cryptogroup \
1718
&& useradd -m -u 2001 -g cryptogroup cryptodude
1819

1920
RUN chown -R cryptodude /service
20-
RUN chmod +x cry.py run.sh
21+
RUN chmod +x cry_async.py run.sh
2122
USER cryptodude
2223

2324
CMD ["/service/run.sh"]

service/cry_async.py

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#!/usr/bin/env python3
2+
3+
import sys
4+
import os
5+
from binascii import unhexlify, hexlify
6+
import string
7+
from Crypto.PublicKey import RSA
8+
from hashlib import sha256
9+
10+
import asyncio
11+
import aiosqlite
12+
13+
from crypto import decrypt, encrypt, sign, verify
14+
15+
# def timeout_handler():
16+
# print("TIMEOUT!")
17+
# os._exit(os.EX_OK)
18+
19+
if os.path.isfile('key.pem'):
20+
try:
21+
KEY = RSA.importKey(open('key.pem','r').read())
22+
except:
23+
KEY = RSA.generate(2048)
24+
key_file = open('key.pem','wb')
25+
key_file.write(KEY.export_key())
26+
key_file.close()
27+
else:
28+
KEY = RSA.generate(2048)
29+
key_file = open('key.pem','wb')
30+
key_file.write(KEY.export_key())
31+
key_file.close()
32+
33+
PUBLIC_KEY = KEY.public_key().export_key()
34+
35+
if os.path.isfile('checker.pubkey'):
36+
CHECKER_KEY = RSA.importKey(open('checker.pubkey','r').read())
37+
else:
38+
raise Exception('Public Key of Checker not found')
39+
40+
global DB_CONN
41+
42+
class Store(object):
43+
def __init__(self, rx, tx):
44+
self.rx = rx
45+
self.tx = tx
46+
47+
async def run(self):
48+
while True:
49+
try:
50+
self.tx.write(b"command: ")
51+
await self.tx.drain()
52+
53+
line = await self.rx.readline()
54+
# print(f"Received command: {line}")
55+
56+
input_data = line.strip()
57+
if not input_data:
58+
break
59+
60+
await self.process_command(input_data)
61+
await self.tx.drain()
62+
63+
except EOFError:
64+
break
65+
except (UnicodeError, IndexError) as e:
66+
print(e, file=sys.stderr)
67+
break
68+
69+
70+
async def process_command(self, input_data : bytes) -> str:
71+
args = input_data.decode().split(' ') # split signature
72+
command = args[0]
73+
74+
if command == 'receive':
75+
#checking signature
76+
if len(args) != 5:
77+
self.tx.write(b"Entered line must have format \"receive category data tick signature\"\n")
78+
return
79+
80+
signature = args[-1]
81+
args = args[1:-1]
82+
if not verify(' '.join(args), signature, CHECKER_KEY):
83+
self.tx.write(b"invalid signature\n")
84+
return
85+
86+
await self.receive(*args)
87+
88+
elif command == 'send':
89+
try:
90+
tick = int(args[1])
91+
except ValueError:
92+
self.tx.write(b'First argument must be integer\n')
93+
await self.send(args[1])
94+
95+
elif command == 'send_pubkey':
96+
self.tx.write(PUBLIC_KEY + b'\n')
97+
else:
98+
self.tx.write(b'Unknown command\n')
99+
#print("Entered line must have format \"command [params]* [signature]\" separated by spaces")
100+
101+
async def receive(self, category : str, data : str, tick : str) -> str:
102+
if category == 'flag':
103+
data = decrypt(data, privkey = KEY)
104+
else:
105+
data = unhexlify(data).decode()
106+
#store data in DB
107+
try:
108+
tick = int(tick)
109+
except ValueError:
110+
self.tx.write(b'tick must be integer\n')
111+
return
112+
113+
if all([char in string.printable for char in data]):
114+
async with DB_CONN.execute('insert into store (tick, category, data) values (?,?,?);', (tick, category, data)) as cursor:
115+
last_id = cursor.lastrowid
116+
117+
if last_id is None:
118+
self.tx.write(b'Failed to add element to db.\n')
119+
return
120+
121+
await DB_CONN.commit()
122+
self.tx.write(f"{sha256(data.encode()).hexdigest()}:{last_id}\n".encode())
123+
else:
124+
self.tx.write(f'Data not correctly decrypted: {data.encode()}\n'.encode())
125+
126+
async def send(self, flag_id : int) -> str:
127+
async with DB_CONN.execute('select data,category from store where id = ' + str(flag_id) + ';') as cursor:
128+
try:
129+
content, category = await cursor.fetchone()
130+
except TypeError:
131+
self.tx.write(b'Key not in Database\n')
132+
return
133+
134+
# print(content, category, file=sys.stderr)
135+
if category == 'flag':
136+
self.tx.write(f"{encrypt(content, CHECKER_KEY)}\n".encode())
137+
else:
138+
self.tx.write(hexlify(content.encode()) + b'\n')
139+
140+
async def handle_connection(reader, writer):
141+
s = Store(reader, writer)
142+
await s.run()
143+
writer.close()
144+
await writer.wait_closed()
145+
146+
async def main():
147+
global DB_CONN
148+
DB_CONN = await aiosqlite.connect('data/store.db')
149+
150+
server = await asyncio.start_server(handle_connection, host="0.0.0.0", port="9122")
151+
152+
addr = server.sockets[0].getsockname()
153+
print(f'Serving on {addr}')
154+
155+
async with server:
156+
await server.serve_forever()
157+
158+
if __name__ == "__main__":
159+
asyncio.run(main())

service/run.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/bin/sh
22
touch data/store.db
33
sqlite3 data/store.db < init_client.sql
4-
socat TCP4-LISTEN:9122,fork,reuseaddr EXEC:'python3 /service/cry.py'
4+
#socat TCP4-LISTEN:9122,fork,reuseaddr EXEC:'python3 /service/cry.py'
5+
python3 /service/cry_async.py

0 commit comments

Comments
 (0)