aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLouis Burda <quent.burda@gmail.com>2022-10-27 20:29:33 +0200
committerLouis Burda <quent.burda@gmail.com>2022-10-27 20:29:33 +0200
commit545dea7ac4e6bdacec2e456132f4dd009556112b (patch)
tree1c3809f9e99692f641da60a606dd5da118731383
parentb4f2da2ca448e173d2ff638d71018c50eca0982d (diff)
downloadbambi7-service-catchbox-545dea7ac4e6bdacec2e456132f4dd009556112b.tar.gz
bambi7-service-catchbox-545dea7ac4e6bdacec2e456132f4dd009556112b.zip
Try to improve sqlite performance and add error logging
-rw-r--r--checker/docker-compose.yml7
-rw-r--r--checker/local.sh2
-rw-r--r--checker/src/checker.py67
-rw-r--r--checker/src/gunicorn.conf.py2
-rw-r--r--checker/test.sh2
-rw-r--r--service/Dockerfile1
-rw-r--r--service/docker-compose.yml2
-rw-r--r--service/www/index.php9
8 files changed, 57 insertions, 35 deletions
diff --git a/checker/docker-compose.yml b/checker/docker-compose.yml
index 93f72af..225d1ca 100644
--- a/checker/docker-compose.yml
+++ b/checker/docker-compose.yml
@@ -3,19 +3,20 @@ services:
catchbox-checker:
build: .
ports:
- - 9091:9091
+ - 9092:9092
environment:
- MONGO_ENABLED=1
- MONGO_HOST=catchbox-mongo
- - MONGO_PORT=27017
+ - MONGO_PORT=9093
- MONGO_USER=catchbox_checker
- MONGO_PASSWORD=catchbox_checker
catchbox-mongo:
image: mongo
+ command: mongod --port 9093
volumes:
- ./data:/data/db
ports:
- - 27017:27017
+ - 9093:9093
environment:
MONGO_INITDB_ROOT_USERNAME: catchbox_checker
MONGO_INITDB_ROOT_PASSWORD: catchbox_checker
diff --git a/checker/local.sh b/checker/local.sh
index 8312210..7fe254d 100644
--- a/checker/local.sh
+++ b/checker/local.sh
@@ -5,7 +5,7 @@ docker-compose up -d catchbox-mongo
pushd src
export MONGO_ENABLED=1
export MONGO_HOST=localhost
-export MONGO_PORT=27017
+export MONGO_PORT=9093
export MONGO_USER=catchbox_checker
export MONGO_PASSWORD=catchbox_checker
gunicorn -c gunicorn.conf.py checker:app
diff --git a/checker/src/checker.py b/checker/src/checker.py
index 962b073..c98b818 100644
--- a/checker/src/checker.py
+++ b/checker/src/checker.py
@@ -26,7 +26,7 @@ from subprocess import Popen, PIPE
import string
-from typing import Optional
+from typing import Any, Optional
import random
@@ -51,11 +51,23 @@ def str2epoch(text: str) -> int:
date = dateutil.parser.parse(text + " UTC")
return int(date.timestamp())
+def parse_html(logger: LoggerAdapter, r: Response) -> BeautifulSoup:
+ try:
+ return BeautifulSoup(r.text, "html.parser")
+ except:
+ logger.error(f"Invalid html from {r.request.method} {r.request.url.path}\n" \
+ + r.text)
+ raise MumbleException(f"Invalid html ({r.request.method} {r.request.url.path})")
+
def assert_status_code(logger: LoggerAdapter, r: Response,
- status_code: int, action: str) -> None:
- if (r.status_code != status_code):
- logger.warn(f"Bad service response for {action}:\n{r.text}")
- raise MumbleException(status[0].upper() + status[1:] + " failed")
+ code: int, errmsg: Optional[str], extra: Any = "") -> None:
+ if r.status_code != code:
+ logger.error(f"Bad service response for " \
+ + f"{r.request.method} {r.request.url.path}:\n" \
+ + f"Extra info: {str(extra)}\n{r.text}")
+ if errmsg is None:
+ errmsg = f"{r.request.method} {r.request.url.path} failed"
+ raise MumbleException(msg)
@checker.putflag(0)
async def putflag_file(task: PutflagCheckerTaskMessage, logger: LoggerAdapter,
@@ -63,12 +75,12 @@ async def putflag_file(task: PutflagCheckerTaskMessage, logger: LoggerAdapter,
username, password = noise(10, 20), noise(20, 30)
data = { "action": "register", "username": username, "password": password }
r = await client.post("/index.php", data=data)
- assert_status_code(logger, r, 200, "register")
+ assert_status_code(logger, r, 200, "Register failed", extra=data)
filename, content = noise(20, 30), task.flag
data = { "action": "upload", "filename": filename, "content": content }
r = await client.post("/index.php", data=data)
- assert_status_code(logger, r, 200, "file upload")
+ assert_status_code(logger, r, 200, "File upload", extra=data)
await db.set("info", (username, password, filename))
@@ -76,7 +88,7 @@ async def putflag_file(task: PutflagCheckerTaskMessage, logger: LoggerAdapter,
@checker.getflag(0)
async def getflag_file(task: GetflagCheckerTaskMessage,
- client: AsyncClient, db: ChainDB) -> None:
+ logger: LoggerAdapter, client: AsyncClient, db: ChainDB) -> None:
try:
username, password, filename = await db.get("info")
except KeyError:
@@ -84,24 +96,24 @@ async def getflag_file(task: GetflagCheckerTaskMessage,
data = { "action": "login", "username": username, "password": password }
r = await client.post("/index.php", data=data)
- assert_status_code(logger, r, 200, "login")
+ assert_status_code(logger, r, 200, "Login failed", extra=data)
r = await client.get(f"/index.php?f={filename}")
- assert_status_code(logger, r, 200, "file download")
+ assert_status_code(logger, r, 200, "File download failed", extra=filename)
assert_in(task.flag, r.text, "Flag missing")
@checker.putflag(1)
-async def putflag_report(task: PutflagCheckerTaskMessage, logger: LoggerAdapter,
- client: AsyncClient, db: ChainDB) -> str:
+async def putflag_report(task: PutflagCheckerTaskMessage,
+ logger: LoggerAdapter, client: AsyncClient, db: ChainDB) -> str:
username, password = noise(10, 20), noise(20, 30)
data = { "action": "register", "username": username, "password": password }
r = await client.post("/index.php", data=data)
- assert_status_code(logger, r, 200, "register")
+ assert_status_code(logger, r, 200, "Register failed", extra=data)
data = { "action": "report", "content": task.flag }
r = await client.post("/index.php", data=data)
- assert_status_code(logger, r, 200, "upload")
+ assert_status_code(logger, r, 200, "Upload failed", extra=data)
await db.set("info", (username, password))
@@ -117,10 +129,10 @@ async def getflag_report(task: GetflagCheckerTaskMessage,
data = { "action": "login", "username": username, "password": password }
r = await client.post("/index.php", data=data)
- assert_status_code(logger, r, 200, "login")
+ assert_status_code(logger, r, 200, "Login failed", extra=data)
r = await client.get(f"/index.php?r")
- assert_status_code(logger, r, 200, "report download")
+ assert_status_code(logger, r, 200, "Report download failed")
assert_in(task.flag, r.text, "Flag missing")
@@ -130,12 +142,12 @@ async def putnoise_file(task: PutnoiseCheckerTaskMessage,
username, password = noise(10, 20), noise(20, 30)
data = { "action": "register", "username": username, "password": password }
r = await client.post("/index.php", data=data)
- assert_status_code(logger, r, 200, "register")
+ assert_status_code(logger, r, 200, "Register failed", extra=data)
filename, content = noise(20, 30), noise(20, 30)
data = { "action": "upload", "filename": filename, "content": content }
r = await client.post("/index.php", data=data)
- assert_status_code(logger, r, 200, "file upload")
+ assert_status_code(logger, r, 200, "File upload failed", extra=data)
await db.set("info", (username, password, filename, content))
@@ -150,12 +162,12 @@ async def getnoise_file(task: GetnoiseCheckerTaskMessage,
data = { "action": "login", "username": username, "password": password }
r = await client.post("/index.php", data=data)
- assert_status_code(logger, r., 200, "login")
+ assert_status_code(logger, r, 200, "Login failed", extra=data)
r = await client.get("/index.php?q=files")
- assert_status_code(logger, r, 200, "files query")
+ assert_status_code(logger, r, 200, "Files query failed")
- soup = BeautifulSoup(r.text, "html.parser")
+ soup = parse_html(logger, r)
files = [v.select("a") for v in soup.select("ul.filelist > li")]
assert_equals(all([len(v) == 2 for v in files]), True, "noise missing")
@@ -164,12 +176,13 @@ async def getnoise_file(task: GetnoiseCheckerTaskMessage,
assert_equals(type(urls[filename]), str, "noise missing")
r = await client.get(f"/index.php?f={filename}")
- assert_status_code(logger, r, 200, "file download")
+ assert_status_code(logger, r, 200, "File download failed", extra=filename)
assert_in(noise, r.text, "Noise missing")
anon = await di.get(AsyncClient)
r = await anon.get(urls[filename])
- assert_status_code(logger, r, 200, "public file retrieve")
+ assert_status_code(logger, r, 200, "Public url invalid",
+ extra={"filename": filename, "url": urls[filename]})
assert_in(noise, r.text, "Noise missing")
@checker.exploit(0)
@@ -182,9 +195,9 @@ async def exploit_file_creat(task: ExploitCheckerTaskMessage,
_, flaguser, _, flagfile = task.attack_info.split()
r = await client.get("/?q=users")
- assert_status_code(logger, r, 200, "query users")
+ assert_status_code(logger, r, 200, "Users listing failed")
- soup = BeautifulSoup(r.text, "html.parser")
+ soup = parse_html(logger, r)
users = [v.children for v in soup.select("ul.userlist > li")]
times = { a.text.strip(): str2epoch(b.text.strip()) for a,b in users }
@@ -222,12 +235,12 @@ async def exploit_report_path(task: ExploitCheckerTaskMessage,
username, password = noise(10, 20), noise(20, 30)
data = { "action": "register", "username": username, "password": password }
r = await client.post("/index.php", data=data)
- assert_status_code(logger, r, 200, "register")
+ assert_status_code(logger, r, 200, "Register failed", extra=data)
filepath = f"../../reports/{reportfile}"
data = { "action": "upload", "filename": filepath, "content": "exploit2!" }
r = await client.post("/index.php", data=data)
- assert_status_code(logger, r, 200, "path traversal")
+ assert_status_code(logger, r, 200, "Path traversal", extra=data)
r = await client.get(f"/index.php?f={filepath}")
if flag := searcher.search_flag(r.text):
diff --git a/checker/src/gunicorn.conf.py b/checker/src/gunicorn.conf.py
index 45f993f..3e9f0e4 100644
--- a/checker/src/gunicorn.conf.py
+++ b/checker/src/gunicorn.conf.py
@@ -2,7 +2,7 @@ import multiprocessing
worker_class = "uvicorn.workers.UvicornWorker"
workers = min(8, multiprocessing.cpu_count())
-bind = "0.0.0.0:9091"
+bind = "0.0.0.0:9092"
timeout = 90
keepalive = 3600
preload_app = True
diff --git a/checker/test.sh b/checker/test.sh
index bc41e46..cc2b7b8 100644
--- a/checker/test.sh
+++ b/checker/test.sh
@@ -4,7 +4,7 @@ fallback=$(ip addr | grep "scope global" | cut -d' ' -f6 | cut -d'/' -f1 | head
ADDRESS=${ADDRESS:-$fallback}
export ENOCHECKER_TEST_CHECKER_ADDRESS=$ADDRESS
-export ENOCHECKER_TEST_CHECKER_PORT=9091
+export ENOCHECKER_TEST_CHECKER_PORT=9092
export ENOCHECKER_TEST_SERVICE_ADDRESS=$ADDRESS
export ENOCHECKER_TEST_SERVICE_PORT=9090
diff --git a/service/Dockerfile b/service/Dockerfile
index b7febe0..8ec6d1e 100644
--- a/service/Dockerfile
+++ b/service/Dockerfile
@@ -4,6 +4,7 @@ RUN apt-get update
RUN apt-get install -y nginx php-fpm php-sqlite3 sqlite3 cron
COPY nginx.conf /etc/nginx/nginx.conf
+
COPY entrypoint.sh /root
RUN chmod +x /root/entrypoint.sh
diff --git a/service/docker-compose.yml b/service/docker-compose.yml
index c4bb659..b739309 100644
--- a/service/docker-compose.yml
+++ b/service/docker-compose.yml
@@ -7,5 +7,5 @@ services:
hostname: catchbox
container_name: catchbox-service
ports:
- - "9090:80"
+ - 9090:80
diff --git a/service/www/index.php b/service/www/index.php
index 33726c9..ef4777a 100644
--- a/service/www/index.php
+++ b/service/www/index.php
@@ -28,8 +28,15 @@ srand(time());
function load() {
global $db;
- if ($db === null)
+ if ($db === null) {
+ /* https://phiresky.github.io/blog/2020/sqlite-performance-tuning/ */
$db = new SQLite3("db.sqlite");
+ $db->exec("PRAGMA journal_mode = WAL");
+ $db->exec("PRAGMA synchronous = normal");
+ $db->exec("PRAGMA temp_storage = memory");
+ $db->exec("PRAGMA mmap_size = 30000000000");
+ $db->exec("PRAGMA page_size = 32768");
+ }
return $db;
}