@@ -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 )
110152async 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 )
127207async 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
160242SERVICE_PORT = 4269
0 commit comments