Skip to content

Commit bd06d5f

Browse files
author
Robert Quander
committed
Added more stability and updated checker
1 parent 1011c5d commit bd06d5f

File tree

10 files changed

+13517
-121
lines changed

10 files changed

+13517
-121
lines changed

checker/src/checker.py

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ async def login_user(self, username: str, password: str):
5555
follow_redirects=True
5656
)
5757
text = resp.text
58-
if resp.status_code != 200 or "success" not in text.lower():
58+
if resp.status_code not in (200, 302) or "Make a post" not in text:
59+
self.logger.error(f"Login response: {text}")
5960
raise MumbleException("Failed to login user")
6061

6162
async def create_post(self, text: str) -> str:
@@ -84,7 +85,8 @@ async def get_post(self, post_id: str) -> str:
8485

8586
resp = await self.client.get(f"/profile/{post_id}")
8687
if resp.status_code != 200:
87-
raise MumbleException("Failed to retrieve post")
88+
self.logger.error(f"Failed to retrieve post {post_id}, status {resp.status_code}")
89+
raise MumbleException("Failed to retrieve relevant information")
8890

8991
match = re.search(r'<p>DEBUG:(.*?)</p>', resp.text)
9092
r=match.group(1)
@@ -93,6 +95,37 @@ async def get_post(self, post_id: str) -> str:
9395
assert d, "No debug comment found in HTML"
9496

9597
return d
98+
99+
async def event_post(self, flag: str) -> tuple[str, str]:
100+
data = {
101+
"title": "CTF BBQ",
102+
"description": "Come hungry.",
103+
"date": f"{''.join(random.choices('0123456789', k=4))}-{str(random.randint(1, 12)).zfill(2)}-{str(random.randint(5, 27)).zfill(2)}",
104+
"location": "The Cloud",
105+
"notes": flag,
106+
}
107+
108+
resp = await self.client.post("/create-event", data=data, follow_redirects=True)
109+
html = resp.text
110+
111+
if resp.status_code not in (200, 302) or "Event Created Successfully!" not in html:
112+
self.logger.error(f"Unexpected response during object creation:\n{html}")
113+
raise MumbleException("Service returned unexpected content after submission")
114+
115+
event_id_match = re.search(r"/event/([A-F0-9\-]{36})", html)
116+
event_id = event_id_match.group(1) if event_id_match else None
117+
118+
key_match = re.search(r"<pre[^>]*>([a-f0-9]{32})</pre>", html, re.IGNORECASE)
119+
full_key = key_match.group(1) if key_match else None
120+
121+
if not event_id or not full_key:
122+
self.logger.error("Could not extract required tokens from service response")
123+
raise MumbleException("Service response malformed or missing expected fields")
124+
125+
return event_id, full_key
126+
127+
128+
96129

97130

98131
@checker.putflag(0)
@@ -105,6 +138,15 @@ async def putflag(task: PutflagCheckerTaskMessage, db: ChainDB, logger: LoggerAd
105138
await db.set("userdata", (username, password, post_id))
106139
return username # from here the users can get the attack info for the exploit
107140

141+
@checker.putflag(1)
142+
async def putflag_v2(task: PutflagCheckerTaskMessage, db: ChainDB, logger: LoggerAdapter, client: AsyncClient):
143+
conn = HTTPConnection(logger, client)
144+
username = 'Dr' + ''.join(random.choices(string.ascii_letters, k=8))
145+
password = ''.join(random.choices(string.ascii_letters, k=10))
146+
await conn.register_user(username, password)
147+
event_id, event_key = await conn.event_post(task.flag)
148+
await db.set("userdata", (username, password, event_id, event_key))
149+
return username
108150

109151
@checker.getflag(0)
110152
async def getflag(task: GetflagCheckerTaskMessage, db: ChainDB, logger: LoggerAdapter, client: AsyncClient):
@@ -120,8 +162,46 @@ async def getflag(task: GetflagCheckerTaskMessage, db: ChainDB, logger: LoggerAd
120162
normalized_content = unicodedata.normalize("NFC", content)
121163

122164
assert_in(normalized_flag, normalized_content, "Flag not found in post")
165+
166+
@checker.getflag(1)
167+
async def getflag_v2(task: GetflagCheckerTaskMessage, db: ChainDB, logger: LoggerAdapter, client: AsyncClient):
168+
conn = HTTPConnection(logger, client)
169+
170+
try:
171+
_, _, event_id, event_key = await db.get("userdata")
172+
except KeyError:
173+
logger.error("No event data found in database")
174+
raise MumbleException("Missing data for this flag (Err 5)")
175+
176+
user = ''.join(random.choices(string.ascii_letters, k=10))
177+
pw = ''.join(random.choices(string.ascii_letters + string.digits, k=10))
178+
logger.debug(f"Created user {user} with password {pw} for flag access with key {event_key} for event {event_id}") # SECRET
179+
await conn.register_user(user, pw)
180+
181+
await client.get(f"/event/{event_id}", follow_redirects=True)
182+
resp = await client.post(f"/event/{event_id}",
183+
data={"key": event_key},
184+
follow_redirects=True)
185+
if resp.status_code not in (200, 302):
186+
logger.warning(f"Key ({event_key}) submission response:\n{resp.text}") # SECRET
187+
raise MumbleException("Could not submit key")
188+
189+
resp = await client.post("/event-accept",
190+
data={"eventID": event_id},
191+
follow_redirects=True)
192+
if resp.status_code not in (200, 302):
193+
logger.warning(f"Accept invite response:\n{resp.text}") # SECRET
194+
raise MumbleException("Path not found (Err 6)")
123195

196+
resp = await client.get(f"/event/{event_id}", follow_redirects=True)
197+
logger.debug(f"Final event page:\n{resp.text} at {event_id}") # SECRET
124198

199+
normalized_flag = unicodedata.normalize("NFC", task.flag)
200+
unescaped_html = html.unescape(resp.text)
201+
normalized_page = unicodedata.normalize("NFC", unescaped_html)
202+
assert_in(normalized_flag, normalized_page, "Flag not found (Err 7)")
203+
204+
125205

126206
@checker.putnoise(0)
127207
async def putnoise(task: PutnoiseCheckerTaskMessage, db: ChainDB, logger: LoggerAdapter, client: AsyncClient):
@@ -141,7 +221,7 @@ async def getnoise(task: GetnoiseCheckerTaskMessage, db: ChainDB, logger: Logger
141221
try:
142222
username, password, post_id, original_text = await db.get("userdata")
143223
except KeyError:
144-
raise MumbleException("No post found for this flag (putflag never called)")
224+
raise MumbleException("Nothing found for this flag (putflag never called)")
145225

146226
content = await conn.get_post(post_id)
147227
assert_in(original_text, content, "Noise content not found")
@@ -155,6 +235,8 @@ async def havoc(task: HavocCheckerTaskMessage, logger: LoggerAdapter, client: As
155235
assert_in("Facepalm", content, "Unexpected service index page")
156236

157237

238+
239+
158240
# === Exploit ===
159241

160242
SERVICE_PORT = 4269

0 commit comments

Comments
 (0)