adding validated services? patching forcad_local.py

This commit is contained in:
Your Name
2026-08-13 08:32:35 +07:00
parent d485f61169
commit e795614e45
1459 changed files with 420036 additions and 436 deletions

View File

@@ -0,0 +1,5 @@
target/
.git/
__pycache__/
*.pyc
data/

View File

@@ -0,0 +1,7 @@
/target
/data
*.pyc
__pycache__/
.cargo
vendor/
venv/

View File

@@ -0,0 +1,605 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "aes"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
]
[[package]]
name = "anyhow"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "cbc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
dependencies = [
"cipher",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "checker"
version = "0.1.0"
dependencies = [
"anyhow",
"crypto",
"hex",
"serde",
"serde_json",
"sysex",
"uuid",
]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
]
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto"
version = "0.1.0"
dependencies = [
"aes",
"anyhow",
"cbc",
"crypto-bigint",
"sha2",
]
[[package]]
name = "crypto-bigint"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96272c2ff28b807e09250b180ad1fb7889a3258f7455759b5c3c58b719467130"
dependencies = [
"num-traits",
"rand_core 0.6.4",
"subtle",
]
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "indexmap"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "inout"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"block-padding",
"generic-array",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "js-sys"
version = "0.3.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "num_enum"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c"
dependencies = [
"num_enum_derive",
"rustversion",
]
[[package]]
name = "num_enum_derive"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro-crate"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
dependencies = [
"toml_edit",
]
[[package]]
name = "proc-macro2"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
"rand_core 0.9.3",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.3",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.16",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom 0.3.4",
]
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "sysex"
version = "0.1.0"
dependencies = [
"anyhow",
"crypto",
"num_enum",
"rand",
"sha2",
"uuid",
]
[[package]]
name = "toml_datetime"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_edit"
version = "0.23.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d"
dependencies = [
"indexmap",
"toml_datetime",
"toml_parser",
"winnow",
]
[[package]]
name = "toml_parser"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e"
dependencies = [
"winnow",
]
[[package]]
name = "typenum"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "uuid"
version = "1.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
dependencies = [
"getrandom 0.3.4",
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76"
dependencies = [
"unicode-ident",
]
[[package]]
name = "winnow"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
name = "zerocopy"
version = "0.8.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View File

@@ -0,0 +1,17 @@
[package]
name = "checker"
edition = "2024"
version = "0.1.0"
[[bin]]
name = "checker"
path = "src/main.rs"
[dependencies]
anyhow = "1.0.100"
sysex = { path = "./service_src/crates/sysex" }
crypto = { path = "./service_src/crates/crypto" }
uuid = { version = "1.18.1", features = ["v4", "serde"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
hex = "0.4.3"

View File

@@ -0,0 +1,5 @@
убедиться, что в ../../services/sonobank лежит сам сервис (или поместить в service_src)
docker run -u $(id -u):$(id -g) -v ./:/app -v ./service_src:/app/service_src -w /app rust:1.91.1 cargo build --release
форкаду скормить полученный бинарь ./target/release/checker

View File

@@ -0,0 +1,9 @@
tasks:
- name: sonobank
checker: sonobank/checker
default_score: ???
checker_timeout: 30
puts: 1
gets: 1
places: 1
checker_type: pfr

Binary file not shown.

View File

@@ -0,0 +1 @@
detailed.log

View File

@@ -0,0 +1,160 @@
from pathlib import Path
import asyncio
import random
import string
import time
# run cargo build or cargo build --release before this
OK = 101
CORRUPT = 102
MUMBLE = 103
DOWN = 104
CHECKER_ERROR = 110
RELEASE = True
EXPECTED_CHECK_CODE = OK
EXPECTED_PUT_CODE = OK
EXPECTED_GET_CODE = OK
CHECKS_PER_SECOND = 5
CHECKS_PER_REPORT = 100
MAX_PARALLEL_CHECKS = CHECKS_PER_SECOND * 3
VULNS = 1
TIMEOUT = 30
HOST = "127.0.0.1"
assert VULNS == 1, "multiple vulns not supported."
check_period = 1 / CHECKS_PER_SECOND
if RELEASE:
checker_binary_path = Path("../target/release/checker")
else:
checker_binary_path = Path("../target/debug/checker")
detailed_log_filepath = Path("detailed.log")
def returncode_to_string(returncode):
if returncode == 101:
return "OK"
elif returncode == 102:
return "CORRUPT"
elif returncode == 103:
return "MUMBLE"
elif returncode == 104:
return "DOWN"
elif returncode == 110:
return "CHECKER_ERROR"
else:
return "UNEXPECTED"
def get_random_flag():
return "S" + "".join(random.choices(string.ascii_uppercase + string.digits, k=30)) + "="
output_lock = asyncio.locks.Lock()
async def write_to_detailed_log(entry, *, also_print=False):
async with output_lock:
with open(detailed_log_filepath, "a") as f:
f.write(entry + "\n")
if also_print:
print(entry)
assert checker_binary_path.exists(), "checker binary doesn't exist. run cargo build or cargo build --release before this."
async def do_all_checks_once():
flag = get_random_flag()
check_process = await asyncio.create_subprocess_exec(
checker_binary_path, "check", HOST,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
try:
stdout_data, stderr_data = await asyncio.wait_for(check_process.communicate(), timeout=TIMEOUT)
except asyncio.TimeoutError:
stdout_data, stderr_data = await check_process.stdout.read(), await check_process.stderr.read()
await write_to_detailed_log(
f"CHECK({flag}): Stuck for {TIMEOUT} seconds. Make sure the checker code has it's own timeouts. stdout={stdout_data} stderr={stderr_data}",
also_print=True
)
check_process.kill()
else:
returncode = check_process.returncode
await write_to_detailed_log(
f"CHECK({flag}): {returncode_to_string(returncode)} ({returncode}) stdout={stdout_data} stderr={stderr_data}",
also_print=(EXPECTED_CHECK_CODE is not None and returncode != EXPECTED_CHECK_CODE)
)
put_process = await asyncio.create_subprocess_exec(
checker_binary_path, "put", HOST, "", flag, "1",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
try:
stdout_data, stderr_data = await asyncio.wait_for(put_process.communicate(), timeout=TIMEOUT)
except asyncio.TimeoutError:
stdout_data, stderr_data = await put_process.stdout.read(), await put_process.stderr.read()
await write_to_detailed_log(
f"PUT({flag}): Stuck for {TIMEOUT} seconds. Make sure the checker code has it's own timeouts. stdout={stdout_data} stderr={stderr_data}",
also_print=True
)
put_process.kill()
failed = True
else:
returncode = put_process.returncode
await write_to_detailed_log(
f"PUT({flag}): {returncode_to_string(returncode)} ({returncode}) stdout(public)={stdout_data} stderr(private)={stderr_data}",
also_print=(EXPECTED_PUT_CODE is not None and returncode != EXPECTED_PUT_CODE)
)
flag_id = stderr_data
failed = returncode != OK
if failed:
await write_to_detailed_log(
f"GET({flag}): SKIPPED since PUT failed.",
also_print=(EXPECTED_GET_CODE is not None)
)
return
get_process = await asyncio.create_subprocess_exec(
checker_binary_path, "get", HOST, flag_id, flag, "1",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
try:
stdout_data, stderr_data = await asyncio.wait_for(get_process.communicate(), timeout=TIMEOUT)
except asyncio.TimeoutError:
stdout_data, stderr_data = await get_process.stdout.read(), await get_process.stderr.read()
await write_to_detailed_log(
f"GET({flag}): Stuck for {TIMEOUT} seconds. Make sure the checker code has it's own timeouts. stdout={stdout_data} stderr={stderr_data}",
also_print=True
)
get_process.kill()
else:
returncode = get_process.returncode
await write_to_detailed_log(
f"GET({flag}): {returncode_to_string(returncode)} ({returncode}) stdout(public)={stdout_data} stderr(private)={stderr_data}",
also_print=(EXPECTED_GET_CODE is not None and returncode != EXPECTED_GET_CODE)
)
async def main():
pending_tasks = []
checks_done = 0
while True:
if len(pending_tasks) > 0 and pending_tasks[0].done() and (checks_done + 1) % CHECKS_PER_REPORT == 0:
print(f"Already done {checks_done + 1} checks. {len(pending_tasks) - 1} running in parallel.")
while len(pending_tasks) > 0 and pending_tasks[0].done():
pending_tasks.pop(0)
checks_done += 1
if len(pending_tasks) <= MAX_PARALLEL_CHECKS:
pending_tasks.append(asyncio.create_task(asyncio.wait_for(do_all_checks_once(), timeout=TIMEOUT * 3 + 10)))
else:
print(f"Throttling checks since there are at least {MAX_PARALLEL_CHECKS} running")
await asyncio.sleep(check_period)
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -0,0 +1 @@
../../services/sonobank

View File

@@ -0,0 +1,331 @@
use anyhow::{bail, Context, Result};
use crypto::CryptoParams;
use crypto::U512;
use std::env;
use std::io;
use std::net::UdpSocket;
use std::panic;
use std::process;
use std::time::Duration;
use sysex::{parse_sysex_response, Request, Response};
use uuid::Uuid;
use std::io::Write as _;
const UDP_PORT: u16 = 5004;
const REQUEST_TIMEOUT: Duration = Duration::from_secs(20);
// ForcAD exit codes
const OK: i32 = 101;
const CORRUPT: i32 = 102;
const MUMBLE: i32 = 103;
const DOWN: i32 = 104;
const CHECKER_ERROR: i32 = 110;
fn main() {
// fcking rust exists with 101 on panic by default
panic::set_hook(Box::new(|panic_info| {
let stderr = std::io::stderr();
let mut handle = stderr.lock();
let msg = match panic_info.payload().downcast_ref::<&str>() {
Some(s) => *s,
None => "Box<Any> payload",
};
if let Some(location) = panic_info.location() {
let _ = writeln!(handle, "Panic at {}:{}: {}", location.file(), location.line(), msg);
} else {
let _ = writeln!(handle, "Panic: {}", msg);
}
process::exit(CHECKER_ERROR);
}));
let code = match real_main() {
Ok(c) => c,
Err(e) => {
print!("checker error: {:#}", e);
CHECKER_ERROR
}
};
std::process::exit(code);
}
fn real_main() -> Result<i32> {
let args = env::args().collect::<Vec<_>>();
if args.len() < 3 {
println!("Usage: {} check|put|get <host> [flag_id] [flag] [vuln]", args[0]);
return Ok(CHECKER_ERROR);
}
let mode = args[1].as_str();
let host = args[2].clone();
match mode {
"check" => {
match do_check(&host) {
Ok(()) => Ok(OK),
Err(e) => {
println!("{:#}", e);
if is_down_error(&e) {
Ok(DOWN)
} else {
Ok(MUMBLE)
}
}
}
}
"put" => {
// ForcAD: checker.py put host flag_id flag vuln
if args.len() < 6 {
println!("PUT: not enough arguments");
return Ok(CHECKER_ERROR);
}
if args[5] != "1" {
println!("PUT: unknown vuln {}", args[5]);
return Ok(CHECKER_ERROR)
}
let flag = args[4].clone();
match put_flag(&host, &flag) {
Ok((flag_id, public_msg)) => {
eprint!("{flag_id}");
print!("{public_msg}");
Ok(OK)
}
Err(e) => {
println!("{:#}", e);
if is_down_error(&e) {
Ok(DOWN)
} else {
Ok(MUMBLE)
}
}
}
}
"get" => {
if args.len() < 6 {
println!("GET: not enough arguments");
return Ok(CHECKER_ERROR);
}
if args[5] != "1" {
println!("GET: unknown vuln {}", args[5]);
return Ok(CHECKER_ERROR)
}
let flag_id = args[3].clone();
let flag = args[4].clone();
if flag.is_empty() {
println!("GET: flag empty");
return Ok(CHECKER_ERROR)
}
let (patch_id, priv_key) = match parse_flag_id(&flag_id) {
Ok(v) => v,
Err(e) => {
println!("invalid flag_id: {:#}", e);
return Ok(CHECKER_ERROR);
}
};
match get_and_check(&host, patch_id, priv_key, &flag) {
Ok(()) => Ok(OK),
Err(e) => {
println!("{:#}", e);
if is_down_error(&e) {
Ok(DOWN)
} else if is_flag_mismatch(&e) {
Ok(CORRUPT)
} else {
Ok(MUMBLE)
}
}
}
}
_ => {
println!("Unknown mode: {mode}");
Ok(CHECKER_ERROR)
}
}
}
fn is_down_error(e: &anyhow::Error) -> bool {
e.chain()
.any(|cause| cause.downcast_ref::<io::Error>().is_some())
}
fn is_flag_mismatch(e: &anyhow::Error) -> bool {
let s = format!("{:#}", e);
s.contains("flag mismatch") || s.contains("flag not found")
}
fn send_and_recv(host: &str, payload: &[u8], crypto_params: Option<&'static crypto::CryptoParams>) -> Result<Response> {
let addr = format!("{host}:{UDP_PORT}");
let sock = UdpSocket::bind("0.0.0.0:0").context("bind UDP socket")?;
sock.set_read_timeout(Some(REQUEST_TIMEOUT))
.context("set UDP timeout")?;
sock.connect(&addr)?;
sock.send(payload)
.context("send")?;
let mut buf = [0u8; 4096];
let n = sock.recv(&mut buf).context("recv")?;
let frame = &buf[..n];
let resp = parse_sysex_response(frame, crypto_params).context("parse_sysex_response")?;
Ok(resp)
}
fn get_crypto_params(host: &str) -> Result<crypto::CryptoParams> {
let resp = send_and_recv(host, &Request::GetCryptoParams.encode(), None)?;
match resp {
Response::GetCryptoParamsResp { modulus, base } => {
Ok(CryptoParams::new(modulus, base)?)
}
Response::Error(msg) => bail!("Diag error from service: {msg}"),
other => bail!("Unexpected response to DIAG: {:?}", other),
}
}
fn do_check(host: &str) -> Result<()> {
let resp = send_and_recv(host, &Request::Diag.encode(), None)?;
match resp {
Response::Diag => {}
Response::Error(msg) => bail!("Diag error from service: {msg}"),
other => bail!("Unexpected response to DIAG: {:?}", other),
}
let crypto_params: &'static crypto::CryptoParams = Box::leak(Box::new(get_crypto_params(host)?));
let lua_code =
r#"
function sample(t, note, velocity)
return t*note*velocity
end
"#;
let put_req = Request::Put {
data: lua_code.as_bytes().to_vec(),
};
let resp = send_and_recv(host, &put_req.encode(), Some(crypto_params))?;
let (patch_id, private_key) = match resp {
Response::PutResp { patch_id, private_key } => (patch_id, private_key),
Response::Error(msg) => bail!("PUT error from service: {msg}"),
other => bail!("Unexpected response to PUT: {:?}", other),
};
let render_req = Request::RenderHash {
patch_id,
note: 60,
velocity: 100,
};
let resp = send_and_recv(host, &render_req.encode(), Some(crypto_params))?;
match resp {
Response::RenderHashResp { .. } => {}
Response::Error(msg) => bail!("RenderHash error: {msg}"),
other => bail!("Unexpected response to RenderHash: {:?}", other),
}
let get_req = Request::Get { patch_id };
let resp = send_and_recv(host, &get_req.encode(), Some(crypto_params))?;
let encrypted_code = match resp {
Response::GetResp {
public_key: _,
encrypted_code,
} => encrypted_code,
Response::Error(msg) => bail!("GET error: {msg}"),
other => bail!("Unexpected response to GET: {:?}", other),
};
let decrypted = private_key
.decrypt(&encrypted_code)
.context("decrypt Lua code")?;
let lua_src = String::from_utf8(decrypted).context("Lua code is not UTF-8")?;
if lua_src != lua_code {
bail!("Lua code changed");
}
Ok(())
}
fn put_flag(host: &str, flag: &str) -> Result<(String, String)> {
let crypto_params: &'static crypto::CryptoParams = Box::leak(Box::new(get_crypto_params(host)?));
let lua_code = format!(
r#"
secret = "{flag}"
function sample(t, note, velocity)
return t*note*velocity
end
"#
);
let put_req = Request::Put {
data: lua_code.as_bytes().to_vec(),
};
let resp = send_and_recv(host, &put_req.encode(), Some(crypto_params))?;
let (patch_id, private_key) = match resp {
Response::PutResp { patch_id, private_key } => (patch_id, private_key),
Response::Error(msg) => bail!("PUT error from service: {msg}"),
other => bail!("Unexpected response to PUT: {:?}", other),
};
let flag_id = make_flag_id(patch_id, private_key.k())?;
let public_msg = patch_id.to_string();
Ok((flag_id, public_msg))
}
fn get_and_check(host: &str, patch_id: Uuid, priv_key_k: U512, flag: &str) -> Result<()> {
let crypto_params: &'static crypto::CryptoParams = Box::leak(Box::new(get_crypto_params(host)?));
let get_req = Request::Get { patch_id };
let resp = send_and_recv(host, &get_req.encode(), Some(crypto_params))?;
let encrypted_code = match resp {
Response::GetResp {
public_key: _,
encrypted_code,
} => encrypted_code,
Response::Error(msg) => if msg.contains("ot found") {
bail!("GET error, flag not found: {msg}")
} else {
bail!("GET error: {msg}")
},
other => bail!("Unexpected response to GET: {:?}", other),
};
let priv_key = crypto::PrivateKey::from_k(crypto_params, priv_key_k)?;
let decrypted = priv_key
.decrypt(&encrypted_code)
.context("decrypt Lua code")?;
let lua_src = String::from_utf8(decrypted).context("Lua code is not UTF-8")?;
if !lua_src.contains(flag) {
bail!("flag mismatch in decrypted Lua code");
}
Ok(())
}
#[derive(serde::Serialize, serde::Deserialize)]
struct FlagId {
patch_id: String,
k: String,
}
fn make_flag_id(patch_id: Uuid, priv_key: &U512) -> Result<String> {
let k_str = hex::encode(priv_key.to_be_bytes());
let obj = FlagId {
patch_id: patch_id.to_string(),
k: k_str,
};
let json = serde_json::to_string(&obj)?;
Ok(json)
}
fn parse_flag_id(flag_id: &str) -> Result<(Uuid, U512)> {
let obj: FlagId = serde_json::from_str(flag_id).context("flag_id JSON decode")?;
let patch_id = Uuid::parse_str(&obj.patch_id).context("bad patch_id in flag_id")?;
let priv_key = U512::from_be_hex(&obj.k);
Ok((patch_id, priv_key))
}