def register(self) -> (str, str, requests.cookies.RequestsCookieJar): username = random_username() password = random_string(20) # Register response = self.http_post(route="/register", data={"username": username, "pw": password}, allow_redirects=False,) cookies = response.cookies if "buggy-cookie" not in cookies.keys(): self.logger.debug(f"Failed register for user {username}") raise BrokenServiceException("Cookies missing") assert_equals(302, response.status_code, "Registration failed") assert_equals( response.next.url, response.url.replace("register", ""), "Registration failed", ) # Logout response = self.http_get(route="/logout", cookies=cookies) if response.cookies: self.logger.debug(f"Failed logout for user {username}") raise BrokenServiceException("Logout failed") assert_equals(302, response.status_code, "Logout failed") assert_equals(response.next.url, response.url.replace("logout", ""), "Logout failed") # Login response = self.http_post(route="/login", data={"username": username, "pw": password}) if "buggy-cookie" not in response.cookies.keys(): self.logger.debug(f"Failed login for user {username}") raise BrokenServiceException("Cookies missing") assert_equals(302, response.status_code, "Login failed") assert_equals(response.next.url, response.url.replace("login", ""), "Login failed") self.logger.debug("registered and logged in") return username, password, response.cookies
def get_planet(self): try: planet_id = self.team_db[self.flag] except KeyError: raise BrokenServiceException("can't retrieve planet") # try: # index = self.team_db[self.team] # # index = teamIndexes[self.team] # except KeyError: # self.team_db[self.team] = 0 # index = self.team_db[self.team] # # teamIndexes[self.team] = 0 # # index = 0 # print("TEAM: {}, counter: {}, prime: {}".format(self.team,str(index), primes[index]), flush=True) ticket = sympy.nextprime(random.randint(100000000, 10000000000)) data = {'ticket': "-" + str(ticket), 'id': planet_id} # teamIndexes[self.team] = index + 1 resp = self.http_get('/getPlanet', data) print("\n \n \n \n " + self.team + " \n \n \n index " + str(ticket) + " \n \n" + resp.text, flush=True) self.debug("\n \n \n \n " + self.team + " \n \n \n index " + str(ticket) + " \n \n" + resp.text) try: resp_dict = json.loads(resp.text) except: raise BrokenServiceException("can't retrieve planet") print("planet retrieved with id: " + resp_dict['planetId'], flush=True) self.debug("planet retrieved with id: " + resp_dict['planetId']) return resp_dict
def follow_user(self, user_to_follow, idx=0): ret = self._get_request(self.FOLLOW_USER_URL.replace("USERNAME", user_to_follow), useCookies=True, idx=idx) if not '''1337 WORKS FOR ME''' in ret: raise BrokenServiceException("Wrong HTTP status code") if not '''Content Type: 1337/5P34K''' in ret: raise BrokenServiceException("Wrong Content Type header")
def getflag(self): # type: () -> None try: data = self.team_db[self.flag] except KeyError as ex: raise BrokenServiceException( "Inconsistent Database: Couldn't get data for team/flag ({})". format(self.flag)) r = self.http_get("/recipe/" + data["tag"]) try: if data['flag_destination'] == 'base_ingredient': flag = re.findall( '<input class=\"input\" type=\"text\" name=\"base_ingredient\"[\\n\\t\s]+value=\"([^\"]+)\">', r.text, re.S)[0] elif data['flag_destination'] == "potion": flag = re.findall( '<code class="language-javascript">([^<]+)</code>', r.text, re.S)[0] except Exception: raise BrokenServiceException( "Could not fetch flag from {} field.".format( data['flag_destination'])) if flag != self.flag: raise BrokenServiceException( "Incorrect flag in {} field (expected '{}', but found '{}')". format(data['flag_destination'], self.flag, flag))
def post_settings(self, bio="Hello, I'm using shittr!", public=True, idx=0): ret = self._post_request(self.SETTINGS_URL, {'public': 'on' if public else '', 'bio': bio}, useCookies=True, follow=True, idx=idx) if not '''1337 WORKS FOR ME''' in ret: raise BrokenServiceException("Wrong HTTP status code") if not '''Content Type: 1337/5P34K''' in ret: raise BrokenServiceException("Wrong Content Type header")
def post_shit(self, text, private=False,idx=0): ret = self._post_request(self.SHIT_URL, {'private': 'on' if private else '', 'post': text}, useCookies=True, follow=True, idx=idx) if not '''1337 WORKS FOR ME''' in ret: raise BrokenServiceException("Wrong HTTP status code") if not '''Content Type: 1337/5P34K''' in ret: raise BrokenServiceException("Wrong Content Type header")
def login(self, user, pw, idx=0): ret = self._post_request(self.LOGIN_URL, {'username': user, 'password': pw}, useCookies=True, idx=idx) if not '''1337 WORKS FOR ME''' in ret: raise BrokenServiceException("Wrong HTTP status code") if not '''Content Type: 1337/5P34K''' in ret: raise BrokenServiceException("Wrong Content Type header")
def logout(self,idx=0): ret = self._get_request(self.LOGOUT_URL, useCookies=True, idx=idx) if not '''1337 WORKS FOR ME''' in ret: raise BrokenServiceException("Wrong HTTP status code") if not '''Content Type: 1337/5P34K''' in ret: raise BrokenServiceException("Wrong Content Type header")
async def authenticate(self, reader, writer, size=16): g = 2 x = 12074235067132104677358030448740169086211171545373284647579234906840326968311237601092259613113724502049948022317426840853777753513486274652991559584610574 prime = 7211120725388770757449064920117053258626350409292518732487977076334774457178724668781927073094705280276788047107026280406597259439312993043207548964593709 y = 3791681507150338158995503145950387058281565872405011862192137930605710794447294645064985636919183844757477896341909957083567025441343602771513168204170391 # g**x % prime size_packed = struct.pack(">H", size) p = b"ENO/ZKP/" + size_packed + b"/" + b'\x00' * (size + 1) writer.write(self.xor(p)) r = self.xor(await reader.read(512)) if r[4:7] != b"ESQ": logger.debug("error1") raise BrokenServiceException("Wrong Packet") p = b"ENO/ACC/" + size_packed + b"/" + b"\x00\x01" + b'\x00' * (size + 1) writer.write(self.xor(p)) r = self.xor(await reader.read(512)) if r[4:7] != b"AWS": logger.debug("error2") raise BrokenServiceException("Wrong Packet") p = b"ENO/ACC/\x01\x00/" + b"\x00\x02" + b'\x00' * 255 writer.write(self.xor(p)) r = self.xor(await reader.read(512)) if r[4:7] != b"ACC": logger.debug("error3") raise BrokenServiceException("Wrong Packet") for i in range(0, 64): # send random r r = random.randint(0, prime - 1) c = pow(g, r, prime) c = construct.BytesInteger(64).build(c) p = b"ENO/ZKP/\x01\x00/" + b"\x00\x02" + c + b'\x00' * (255 - len(c)) writer.write(self.xor(p)) re = self.xor(await reader.read(512)) opt = re[self.PAYLOAD_OFFSET + 16:self.PAYLOAD_OFFSET + 17] # disclose info if opt == b"0": r = construct.BytesInteger(64).build(r) p = b"ENO/ZKP/\x01\x00/" + b"\x00\x02" + r + b'\x00' * (255 - len(r)) else: v = (x + r) % (prime - 1) v = construct.BytesInteger(64).build(v) p = b"ENO/ZKP/\x01\x00/" + b"\x00\x02" + v + b'\x00' * (255 - len(v)) writer.write(self.xor(p)) re = self.xor(await reader.read(512)) if re[4:7] == b"DNY": raise BrokenServiceException( "Unable to authenticate with hardcoded secret") break elif re[4:7] == b"ZKP" and b"SUCCESS" in re: logger.debug("Authenticated successfully") break
def putnoise_register_twice_error(self): user, pw = self.get_or_create_account() ret = self._post_request(self.REG_URL, {'username': user, 'password': pw}, useCookies=False, follow=True) if not '''n0p3, user already exists''' in ret: raise BrokenServiceException("Registration error not fired") if not '''HTTP/1.0 4242 IT BURNS!!!''' in ret: raise BrokenServiceException("Wrong HTTP status code")
def putnoise_login_wrong_error(self): user, pw = self.get_or_create_account() pw = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(random.randint(6,12))) ret = self._post_request(self.LOGIN_URL, {'username': user, 'password': pw}, useCookies=True) if not '''n0p3, wrong credentials''' in ret: raise BrokenServiceException("Login with wrong password error not fired") if not '''HTTP/1.0 4242 IT BURNS!!!''' in ret: raise BrokenServiceException("Wrong HTTP status code")
def getflag(self): # type: () -> None """ This method retrieves a flag from the service. Use self.flag to get the flag that needs to be recovered and self.round to get the round the flag was placed in. On error, raise an EnoException. :raises EnoException on error :return this function can return a result if it wants if nothing is returned, the service status is considered okay. the preferred way to report errors in the service is by raising an appropriate enoexception """ try: nc = self.connect(port=port) except: raise OfflineException("Connection failed [getflag]") self.info('starting the putflag, calling check_bookings') self.check_bookings(nc) self.info('check_bookings worked') try: nc.read_until(b"================\n") nc.read_until(b"================\n") nc.read_until(b"================\n") nc.write(b"3\n") nc.read_until(b"Enter the unique id of your ticket") nc.write(bytes(self.team_db[self.flag][2] + "\n", 'utf-8')) nc.read_until(b"\n") nc.read_until(b"\n") nc.read_until(b"\n") nc.read_until(b"\n") nc.read_until(b"\n") nc.read_until(b"\n") nc.read_until(b"\n") nc.read_until(b"\n") nc.read_until(b"\n") flag2 = nc.read_until(b"\n").decode('utf-8').replace('\n','') self.info('the new flag was retrieved from the service') except: nc.close() raise BrokenServiceException("Unable to get flag from the service [getflag]") if flag2.strip() != self.flag.strip(): nc.close() raise BrokenServiceException("The flags dont mach! [getflag]") nc.close()
def put_crypto(self, t, mode): self.debug("Putflag - Cryptomat") #cryptomat-flag self.debug("Going to Cryptomat") self.goto_cryptomat(t) t.write("o\n") self.readline_expect_multiline( t, string_dictionary["cryptomat_os_update_mode"]) if (mode == "OFB"): t.write("💣\n".encode("utf-8")) elif (mode == "CBC"): t.write("🧀\n".encode("utf-8")) self.readline_expect_multiline( t, string_dictionary["cryptomat_os_update_1"]) try: t.write(self.rsa_sign_message(self.flag) + "\n") except: raise BrokenServiceException("rsa error") self.readline_expect_multiline( t, string_dictionary["cryptomat_os_update_accept_format"]) self.readline_expect_multiline( t, string_dictionary["cryptomat_os_update_accept_signature"]) self.readline_expect_multiline(t, "Updating...") self.readline_expect_multiline(t, "Updated")
def get_ticket(self) -> None: # TODO: Check order? self.logger.debug("Starting getflag - ticket") try: (hash, username, password) = self.team_db[sha256ify(self.flag)] except KeyError as e: self.logger.warning(f"flag info missing, {e}") return Result.MUMBLE except ValueError as e: self.logger.warning(f"cannot get creds, {e}") return Result.MUMBLE response = self.http_post(route="/login", data={"username": username, "pw": password}) cookies = response.cookies if "buggy-cookie" not in cookies.keys(): self.logger.debug(f"Failed login for user {username}") raise BrokenServiceException("Cookies missing") assert_equals(302, response.status_code, "Login failed") assert_equals(response.next.url, response.url.replace("login", ""), "Login failed") response = self.http_get(route=f"/tickets/{hash}", cookies=cookies) assert_equals(200, response.status_code, "Login failed") assert_in(self.flag, response.text, "Flag missing") response = self.http_get(route=f"/profile", cookies=cookies) assert_equals(200, response.status_code, "Profile failed") assert_in("buggy-team", response.text, "Profile failed") assert_in("enjoy your stay!", response.text, "Profile failed") assert_in("questions or feedback?", response.text, "Profile failed") assert_in("Tickets: (1)", response.text, "Profile failed") assert_in("orders: (1)", response.text, "Profile failed") self.logger.debug("Done getflag - ticket")
def havoc(self): try: un = ''.join( random.choice(string.ascii_lowercase + string.digits) for _ in range(8)) pw = ''.join( random.choice(string.ascii_lowercase + string.digits) for _ in range(8)) self.register(un, pw) self.login(un, pw) name = ''.join( random.choice(string.ascii_lowercase + string.digits) for _ in range(8)) declination = str(round(random.uniform(0.6, 155.5), 3)) rightAscension = str(round(random.uniform(0.6, 155.5), 3)) flag = ''.join( random.choice(string.ascii_lowercase + string.digits) for _ in range(30)) data = { 'name': name, 'declination': declination, 'rightAscension': rightAscension, 'flag': flag } self.add_planet2(data) self.check_planet_list(name) self.check_planet_details(name, declination, rightAscension) except: raise BrokenServiceException("something wrong with the service")
def replay(self, topic: str, private=True) -> str: self.debug(f'Replaying topic "{topic}"...') if private: topic_prefix = "- " else: topic_prefix = "+ " self.debug(f"Connecting to the websocket...") socket = websocket.create_connection( f"ws://{self.address}:{self.port}{self.subscribe_endpoint}" ) self.debug(f"Receiving greeting...") greeting = socket.recv() if self.greeting != greeting: raise BrokenServiceException( f'Endpoint "/subscribe" greeted us with: {greeting}' ) self.debug(f'Requesting topic "{topic}" to be replayed...') # Request to replay the topic with the flag. socket.send(f"REPLAY:{topic_prefix}{topic}") # Receive all the messages related to the requested topic. messages = socket.recv() self.debug(f'Replayed messages are: "{messages}"') # XXX: Is it alright to just close the socket here? Could it be # that the socket is not closed if we abort earlier? socket.close() return messages
def must_replay(self, topic): response = self.replay(topic) if response == "Unknown Topic.": raise BrokenServiceException( f'Unable to replay topic "{topic}": topic unknown' ) return response
def join_any_table(self, t): t.write("j\n") tables = t.read_until(string_dictionary["table_2"] + "\n") tables = tables.decode("utf-8") tables = tables.split("\n") if tables[0] == string_dictionary["table_1"]: self.debug("join_any_table - tables available.. joining") table_identifier = tables[random.randint(1, len(tables) - 3)] t.write(table_identifier + "\n") elif tables[0] == string_dictionary["table_3"]: self.debug("join_any_table - no tables available.. creating") identifier = generate_random_string(20) name = random.choice( ['1337', '1', '1234', 'test', 'name', 'lol', 'wow']) if self.balance <= 1: minimum = "1" else: minimum = "%d" % (random.randint(1, self.balance)) passphrase = random.choice( ['1234', 'password', 'password1', '1', 'test', '1234567890']) self.create_table(t, identifier, name, minimum, passphrase) else: self.debug( "join_any_table - got: %s expected: table_1 or table_3" % (tables[0])) raise (BrokenServiceException( "join_any_table - join_table was tempered with."))
def login_on_service(self, user): user_for_this_round = user user_for_login = self.generate_login_object_from_user( user_for_this_round) request = self.http_post("/login", data=user_for_login) if request.status_code == "500": raise BrokenServiceException("We couldn't login on the service!")
def get_song_with_hashid(self, hash_id): params = {"search_option", "hash_id"} request = self.http_get(f"/song/{hash_id}?search_option=hash_id", stream=True) if request.status_code == 500 or request.status_code == 404: raise BrokenServiceException( "We couldn't download the song from the server") return request
def must_get_message(self, topic, message): response = self.must_replay(topic) if response.find(message) == -1: raise BrokenServiceException( f'Message "{message}" missing from replay of topic "{topic}"' ) self.debug(f'Message "{message}" got')
def must_list_topics(self): self.debug(f"Listing topics...") response = self.list_topics() if response.status_code != 200: raise BrokenServiceException( f"Unexpected status code from the /topics endpoint: {response.status_code}" ) self.debug(f"Topics listed") return response.text
def getflag_user_bio(self): self._get_request(self.LOGIN_URL, useCookies=True) user, pw = self.get_or_create_account(existing=True) self.info("Logging in as {} / {}".format(user, pw)) self.login(user, pw) ret = self._get_request(self.SETTINGS_URL, useCookies=True) if not '''1337 WORKS FOR ME''' in ret: raise BrokenServiceException("Wrong HTTP status code") if not '''Content Type: 1337/5P34K''' in ret: raise BrokenServiceException("Wrong Content Type header") if not '''<textarea name='bio' class="form-control">{}</textarea>'''.format(self.flag) in str(ret): raise BrokenServiceException("Flag not found in /settings")
def get_account(self, idx=0): try: self.info(self.team_db[self.flag]) user = self.team_db[self.flag]['user'+str(idx)] pw = self.team_db[self.flag]['pw'+str(idx)] self.info("I am {} / {}") return user, pw except KeyError: raise BrokenServiceException("Key error -> Flag not found, service down")
def getflag(self): self.http_get("/") self.login(self.team_db['USERNAME'], self.team_db['PASSWORD']) planet = self.get_planet() if planet['flag'] == self.flag: print("the flag is still there :)", flush=True) self.debug("the flag is still there :)") else: raise BrokenServiceException("Incorrect flag {}".format(self.flag))
def check_planet_list(self, name): resp = self.http_get("/") print("checking planets list \n " + resp.text, flush=True) self.debug("checking planets list \n " + resp.text) if name not in resp.text: raise BrokenServiceException("planet list not showing") else: print("planet name is displayed : " + name + "\n", flush=True) self.debug("planet name is displayed : " + name + "\n")
def must_publish_to_new_topic(self, topic, message, private=True): if private: self.debug(f'Publishing message "{message}" to private topic "{topic}"') response = self.add_private_topic(topic) else: self.debug(f'Publishing message "{message}" to public topic "{topic}"') response = self.add_public_topic(topic) if response.status_code != 200: raise BrokenServiceException(f'Could not add topic "{topic}"') publish_response = self.publish(topic, message) if ( publish_response.status_code != 200 or publish_response.text is "Error returned by notify_subscribers." ): raise BrokenServiceException( f'Could not publish message "{message}" to topic "{topic}"' ) self.debug(f'Message "{message}" published')
def getnoise(self): # type: () -> None try: nc = self.connect(port=port) except: raise OfflineException("Connection failed [getnoise]") try: nc.read_until(b"================\n") nc.read_until(b"================\n") nc.read_until(b"================\n") nc.write(b"3\n") nc.read_until(b"Enter the unique id of your ticket") nc.write(bytes(self.team_db[self.noise][2] + "\n", 'utf-8')) nc.read_until(b"\n") nc.read_until(b"\n") nc.read_until(b"\n") nc.read_until(b"\n") nc.read_until(b"\n") nc.read_until(b"\n") nc.read_until(b"\n") nc.read_until(b"\n") nc.read_until(b"\n") flag2 = nc.read_until(b"\n").decode('utf-8').replace('\n','') except: nc.close() raise BrokenServiceException("Unable to get noise from the service [getnoise]") if flag2.strip() != self.flag.strip().replace('\n',''): nc.close() raise BrokenServiceException("The noises dont mach! [getnoise]") """ This method retrieves noise in the service. The noise to be retrieved is inside self.flag The difference between noise and flag is, tht noise does not have to remain secret for other teams. This method can be called many times per round. Check how often using flag_idx. On error, raise an EnoException. :raises EnoException on error :return this function can return a result if it wants if nothing is returned, the service status is considered okay. the preferred way to report errors in the service is by raising an appropriate enoexception """ nc.close()
def exploit(self): desired_topic = generate_topic(self.flag) exploitable_topic = "topics" all_topics = self.must_replay(exploitable_topic) self.debug(f'Received topics are: "{all_topics}"') if all_topics.find(desired_topic) == -1: raise BrokenServiceException( f'Topic "{desired_topic}" missing from replay "{all_topics}" of topic "{exploitable_topic}"' ) self.must_get_message(desired_topic, self.flag) self.debug("Service exploited")
def getflag_private_private_post(self): self._get_request(self.LOGIN_URL, useCookies=True) user, pw = self.get_or_create_account(existing=True) self.info("Logging in as {} / {}".format(user, pw)) self.login(user, pw) if random.randint(0,512) % 2 == 0: ret = self._get_request(self.DIARRHEA_URL, useCookies=True) else: ret = self._get_request(self.MYPROFILE_URL.replace("USERNAME", user), useCookies=True) if not '''1337 WORKS FOR ME''' in ret: raise BrokenServiceException("Wrong HTTP status code") if not '''Content Type: 1337/5P34K''' in ret: raise BrokenServiceException("Wrong Content Type header") if not '''<div class='content'>{}</div>'''.format(self.flag) in str(ret): raise BrokenServiceException("Flag not found in /@{}".format(user))