nya
@ -0,0 +1,13 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[VERSION]
|
||||||
|
insert_final_newline = false
|
||||||
|
|
||||||
|
[*.py]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
@ -0,0 +1,12 @@
|
|||||||
|
FROM python:3.7.14-slim
|
||||||
|
ADD src VERSION /dist/
|
||||||
|
WORKDIR /dist
|
||||||
|
|
||||||
|
# setup the services
|
||||||
|
RUN pip install --requirement aesthetic/requirements.txt
|
||||||
|
RUN pip install --requirement editor/requirements.txt
|
||||||
|
RUN pip install --requirement jinnice/requirements.txt
|
||||||
|
RUN pip install --requirement myblog/requirements.txt
|
||||||
|
|
||||||
|
# start game simulation
|
||||||
|
CMD ["python", "-u", "main.py"]
|
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2022 VolgaCTF
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -0,0 +1,67 @@
|
|||||||
|
# VolgaCTF 2022 Final Homework
|
||||||
|
|
||||||
|
This repo contains all the checkers from `VolgaCTF 2022 Final` along with a game-simulating script.
|
||||||
|
|
||||||
|
Could be useful to do your _homework_.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker build -t volgactf2022/homework-image .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker run \
|
||||||
|
-e TEAM_IP=<host-ip> \
|
||||||
|
-e ROUND_DURATION=10 \
|
||||||
|
--rm \
|
||||||
|
volgactf2022/homework-image
|
||||||
|
```
|
||||||
|
|
||||||
|
## Optional environment variables
|
||||||
|
|
||||||
|
### Simulation-related variables
|
||||||
|
| Var name | Description | Default value |
|
||||||
|
|-----------------------------|------------------------------------------------------|:-------------------:|
|
||||||
|
| `ROUND_DURATION` | Round duration (time between two consecutive PUSHes) | 30 sec |
|
||||||
|
| `SKIP_EDITOR` | Skip `Editor` service | False |
|
||||||
|
| `SKIP_AESTHETIC` | Skip `Aesthetic` service | False |
|
||||||
|
| `SKIP_MYBLOG` | Skip `MyBlog` service | False |
|
||||||
|
| `SKIP_JINNICE` | Skip `Jinnice` service | False |
|
||||||
|
| `PULL_COUNT` | Number of PULLs for each round | 5 |
|
||||||
|
| `PRINT_STATS_EVERY_N_ROUND` | Output stats frequency | 1 |
|
||||||
|
| `PRINT_STATS_SINGLE_COLUMN` | Output stats in a single column | False (two columns) |
|
||||||
|
|
||||||
|
### Checkers' variables
|
||||||
|
| Var name | Description | Default value |
|
||||||
|
|--------------------------------|------------------------------------------|:-------------:|
|
||||||
|
| `EDITOR_PORT` | `Editor` service port | 8080 |
|
||||||
|
| `EDITOR_TIMEOUT` | `Editor` service connection timeout | 30 |
|
||||||
|
| `EDITOR_N_MAX_IMAGES_PER_PUSH` | Max number of images to PUSH to `Editor` | 3 |
|
||||||
|
| `AESTHETIC_PORT` | `Aesthetic` service port | 8777 |
|
||||||
|
| `AESTHETIC_TIMEOUT` | `Aesthetic` service connection timeout | 15 |
|
||||||
|
| `MYBLOG_PORT` | `MyBlog` service port | 13377 |
|
||||||
|
| `MYBLOG_TIMEOUT` | `MyBlog` service connection timeout | 20 |
|
||||||
|
| `JINNICE_PORT` | `Jinnice` service port | 8888 |
|
||||||
|
| `JINNICE_TIMEOUT` | `Jinnice` service connection timeout | 30 |
|
||||||
|
|
||||||
|
### Example with more options
|
||||||
|
Below is an example usage which assumes that only `Editor` and `MyBlog` services are spawned,
|
||||||
|
`Editor`'s port is `18080`, and `MyBlog` checker's connection timeout is increased (e.g. for debugging purposes):
|
||||||
|
```bash
|
||||||
|
$ docker run \
|
||||||
|
-e TEAM_IP=<host-ip> \
|
||||||
|
-e ROUND_DURATION=10 \
|
||||||
|
-e SKIP_AESTHETIC= \
|
||||||
|
-e SKIP_JINNICE= \
|
||||||
|
-e EDITOR_PORT=18080 \
|
||||||
|
-e MYBLOG_TIMEOUT=1800 \
|
||||||
|
--rm \
|
||||||
|
volgactf2022/homework-image
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT @ [VolgaCTF](https://github.com/VolgaCTF)
|
@ -0,0 +1 @@
|
|||||||
|
42.0.0
|
@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from .main import push, pull
|
@ -0,0 +1,5 @@
|
|||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIF/LZh6xyFkuNfwP349RlC4PBv4DyGPt6BzBiublvj1yoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEGySmc8D9mKY0VGif5el/bnbIlQeRhLEWYtvvINH/IM5W2BfgtrrZ
|
||||||
|
Vl5dyGy7tAWqpcqluIipcmcYcHqJndIneg==
|
||||||
|
-----END EC PRIVATE KEY-----
|
@ -0,0 +1,7 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from random import choice
|
||||||
|
from string import ascii_letters, digits
|
||||||
|
|
||||||
|
|
||||||
|
def get_random_message(size=16):
|
||||||
|
return ''.join(choice(ascii_letters + digits) for _ in range(size))
|
@ -0,0 +1,117 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
|
||||||
|
import jwt
|
||||||
|
from volgactf.final.checker.result import Result
|
||||||
|
|
||||||
|
from .utils import read_message, send_message
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
SERVICE_PORT = int(os.getenv('AESTHETIC_PORT', 8777))
|
||||||
|
SESSION_TOTAL_TIMEOUT = int(os.getenv('AESTHETIC_TIMEOUT', 15))
|
||||||
|
|
||||||
|
|
||||||
|
async def push(endpoint, capsule: str, label, metadata):
|
||||||
|
try:
|
||||||
|
logger.debug('[%s on PUSH]: connecting', endpoint)
|
||||||
|
fd = socket.create_connection((endpoint, SERVICE_PORT), timeout=SESSION_TOTAL_TIMEOUT)
|
||||||
|
logger.debug('[%s on PUSH]: connected to service', endpoint)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.error('[%s on PUSH]: failed to connect, reason: %s', endpoint, str(ex))
|
||||||
|
return Result.DOWN, '', 'Failed to connect'
|
||||||
|
|
||||||
|
try:
|
||||||
|
send_message(fd, b'PUSH')
|
||||||
|
|
||||||
|
iv = b'\x70\x67\x4a\xd5\xaf\x53\x92\xf9\xb2\x94\xde\x78' + os.urandom(4)
|
||||||
|
|
||||||
|
send_message(fd, capsule.encode('utf-8'))
|
||||||
|
send_message(fd, metadata.round.to_bytes(4, 'big'))
|
||||||
|
send_message(fd, iv)
|
||||||
|
|
||||||
|
encrypted_capsule = read_message(fd)
|
||||||
|
ec_hash = hashlib.sha256(encrypted_capsule).digest()
|
||||||
|
auth_tag = read_message(fd)
|
||||||
|
|
||||||
|
with open('ec_private.pem', 'rb') as jwtkey:
|
||||||
|
key = jwtkey.read()
|
||||||
|
signature = jwt.encode(
|
||||||
|
{'message': 'It\'s me, Mario!'},
|
||||||
|
key=key,
|
||||||
|
algorithm='ES256'
|
||||||
|
)
|
||||||
|
|
||||||
|
send_message(fd, signature.encode('utf-8'))
|
||||||
|
|
||||||
|
if read_message(fd) != b"+":
|
||||||
|
send_message(fd, b'EXIT')
|
||||||
|
read_message(fd)
|
||||||
|
return Result.MUMBLE, '', ''
|
||||||
|
|
||||||
|
send_message(fd, b'EXIT')
|
||||||
|
read_message(fd)
|
||||||
|
|
||||||
|
return Result.UP, \
|
||||||
|
(base64.b64encode(iv) + b'::' +
|
||||||
|
base64.b64encode(auth_tag) + b'::' +
|
||||||
|
base64.b64encode(ec_hash)).decode(), \
|
||||||
|
'UP'
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
logger.error('[%s on PUSH]: failed on PUSH, reason: %s', endpoint, str(ex))
|
||||||
|
return Result.MUMBLE, '', ''
|
||||||
|
|
||||||
|
|
||||||
|
async def pull(endpoint, capsule: bytes, label: str, metadata):
|
||||||
|
try:
|
||||||
|
logger.debug('[%s on PULL]: connecting', endpoint)
|
||||||
|
fd = socket.create_connection((endpoint, SERVICE_PORT), timeout=SESSION_TOTAL_TIMEOUT)
|
||||||
|
logger.debug('[%s on PULL]: connected to service', endpoint)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.error('[%s on PULL]: failed to connect, reason: %s', endpoint, str(ex))
|
||||||
|
return Result.DOWN, ''
|
||||||
|
|
||||||
|
try:
|
||||||
|
b64_iv, b64_auth_tag, b64_ec_hash = label.encode().split(b'::')
|
||||||
|
iv = base64.b64decode(b64_iv)
|
||||||
|
auth_tag = base64.b64decode(b64_auth_tag)
|
||||||
|
ec_hash = base64.b64decode(b64_ec_hash)
|
||||||
|
|
||||||
|
send_message(fd, b'PULL')
|
||||||
|
send_message(fd, metadata.round.to_bytes(4, 'big'))
|
||||||
|
|
||||||
|
received_enc_capsule = read_message(fd)
|
||||||
|
rec_hash = hashlib.sha256(received_enc_capsule).digest()
|
||||||
|
|
||||||
|
if rec_hash != ec_hash:
|
||||||
|
print(rec_hash, ec_hash)
|
||||||
|
send_message(fd, b'-')
|
||||||
|
send_message(fd, b'EXIT')
|
||||||
|
read_message(fd)
|
||||||
|
return Result.DOWN, 'Wrong hash'
|
||||||
|
else:
|
||||||
|
send_message(fd, b'+')
|
||||||
|
|
||||||
|
send_message(fd, iv)
|
||||||
|
send_message(fd, auth_tag)
|
||||||
|
|
||||||
|
recv_capsule = read_message(fd)
|
||||||
|
|
||||||
|
if recv_capsule.decode('utf-8') != capsule:
|
||||||
|
send_message(fd, b'EXIT')
|
||||||
|
read_message(fd)
|
||||||
|
return Result.DOWN, 'Corrupted flag'
|
||||||
|
|
||||||
|
send_message(fd, b'EXIT')
|
||||||
|
read_message(fd)
|
||||||
|
|
||||||
|
return Result.UP, 'UP'
|
||||||
|
|
||||||
|
except Exception as ex:
|
||||||
|
logger.error('[%s on PULL]: failed on PULL, reason: %s', endpoint, str(ex))
|
||||||
|
return Result.MUMBLE, ''
|
@ -0,0 +1 @@
|
|||||||
|
jwt==1.3.1
|
@ -0,0 +1,43 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
import struct
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger('service')
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Communication
|
||||||
|
"""
|
||||||
|
|
||||||
|
class InputOverflowException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InputUnderflowException(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def read_message(s, max_input_length=1024*16) -> bytes:
|
||||||
|
received_buffer = s.recv(8)
|
||||||
|
if len(received_buffer) < 8:
|
||||||
|
raise InputUnderflowException('Failed to receive data: the received length is less than 8 bytes long')
|
||||||
|
to_receive = struct.unpack('<Q', received_buffer[0:8])[0]
|
||||||
|
if to_receive > max_input_length:
|
||||||
|
raise InputOverflowException('Failed to receive data: requested to accept too much data')
|
||||||
|
received_buffer = b''
|
||||||
|
|
||||||
|
while len(received_buffer) < to_receive:
|
||||||
|
data = s.recv(to_receive - len(received_buffer))
|
||||||
|
if len(data) == 0:
|
||||||
|
raise InputUnderflowException('Failed to receive data: the pipe must have been broken')
|
||||||
|
received_buffer += data
|
||||||
|
if len(received_buffer) > max_input_length:
|
||||||
|
raise InputOverflowException('Failed to receive data: accepted too much data')
|
||||||
|
|
||||||
|
return received_buffer
|
||||||
|
|
||||||
|
|
||||||
|
def send_message(s, message: bytes):
|
||||||
|
send_buffer = struct.pack('<Q', len(message)) + message
|
||||||
|
s.sendall(send_buffer)
|
@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from .main import push, pull
|
After Width: | Height: | Size: 158 KiB |
After Width: | Height: | Size: 328 KiB |
After Width: | Height: | Size: 152 KiB |
After Width: | Height: | Size: 127 KiB |
After Width: | Height: | Size: 172 KiB |
After Width: | Height: | Size: 130 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 310 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 314 KiB |
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 200 KiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 99 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 170 KiB |
After Width: | Height: | Size: 135 KiB |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 113 KiB |
After Width: | Height: | Size: 198 KiB |
After Width: | Height: | Size: 108 KiB |
After Width: | Height: | Size: 198 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 133 KiB |
After Width: | Height: | Size: 122 KiB |
After Width: | Height: | Size: 193 KiB |
After Width: | Height: | Size: 101 KiB |
After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 94 KiB |
After Width: | Height: | Size: 205 KiB |
After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 101 KiB |
After Width: | Height: | Size: 266 KiB |
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 155 KiB |
After Width: | Height: | Size: 147 KiB |
After Width: | Height: | Size: 145 KiB |
After Width: | Height: | Size: 100 KiB |
After Width: | Height: | Size: 160 KiB |
After Width: | Height: | Size: 135 KiB |
After Width: | Height: | Size: 156 KiB |
After Width: | Height: | Size: 75 KiB |
After Width: | Height: | Size: 104 KiB |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 162 KiB |
After Width: | Height: | Size: 207 KiB |
After Width: | Height: | Size: 178 KiB |
After Width: | Height: | Size: 154 KiB |
After Width: | Height: | Size: 100 KiB |
After Width: | Height: | Size: 151 KiB |
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 220 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 131 KiB |
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 162 KiB |
After Width: | Height: | Size: 157 KiB |
After Width: | Height: | Size: 123 KiB |
After Width: | Height: | Size: 117 KiB |
After Width: | Height: | Size: 94 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 196 KiB |
After Width: | Height: | Size: 108 KiB |
After Width: | Height: | Size: 135 KiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 117 KiB |
After Width: | Height: | Size: 260 KiB |
After Width: | Height: | Size: 257 KiB |
After Width: | Height: | Size: 158 KiB |
After Width: | Height: | Size: 285 KiB |
After Width: | Height: | Size: 134 KiB |
After Width: | Height: | Size: 207 KiB |
After Width: | Height: | Size: 132 KiB |
After Width: | Height: | Size: 160 KiB |
After Width: | Height: | Size: 64 KiB |
After Width: | Height: | Size: 150 KiB |
After Width: | Height: | Size: 168 KiB |
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 129 KiB |
After Width: | Height: | Size: 202 KiB |
After Width: | Height: | Size: 190 KiB |
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 262 KiB |
After Width: | Height: | Size: 202 KiB |
After Width: | Height: | Size: 107 KiB |
After Width: | Height: | Size: 226 KiB |