This challenge requires us to solve 1,000 captchas in a row. Since this challenge was in the web category, I looked into the source code to see if there was anything to exploit. Unfortunately, there didn't seem to be anything vulnerable.
One thing I did notice was that there was a session cookie. After playing around with the cookies, I realized that the cookie represents a captcha image and the number of solved captchas in that session.
This means that if we mess up on a captcha, we can revert our session cookie to one that was working before. Essentially, this restores our progress in the case of any mistakes so we don't have to restart from 0.
Next, we need to figure out how to solve the captchas. Looking at the captchas, the characters all seemed to fit a standard font and size, so OCR libraries such as OpenCv or Python-tesseract should be able to read them. All we need is one line of code - `pytesseract.image_to_string(captcha, config='--oem 1 --psm 13')`. In our config parameter, we specify OEM 1 - neural net engine model and PSM 13 - one line of raw text.
However, there is too much color and noise in the captchas and the text is unrecognizable. So, we need to find a way to filter them out.
The first method I tried was to play around with different RGB thresholds.![](https://github.com/csn3rd/UTCTFWriteups/raw/main/thresh1.png)![](https://github.com/csn3rd/UTCTFWriteups/raw/main/thresh2.png)
This didn't work and the captchas could not get clean enough for any OCR programs to reliably read.
After putting the problem aside, one of my teammates realized that the background of each captcha was the same each time. This means that if we have the original background image, we can take the difference / xor of our captcha to get a clean image.
We were not able to find the original background with Google and other reverse image searches. As an alternative, we tried to get the original image by taking tons of captchas and finding the most common pixel at each position.
Result:
![](https://github.com/csn3rd/UTCTFWriteups/raw/main/background.png)
This worked somewhat well. When tested on our captcha training set, pytesseract was successful around a third of the time.
After taking a deeper look at the images after taking the xor, we found that some pixels were flipped. We manually patched some of these areas in our code to get cleaner results. This boosted the performance on our training set to around 90%.
Finally, we run our code on the actual challenge deployment. We send a GET request to get a captcha and we send a POST request to submit the solution. We keep track of best scores and their corresponding cookies. Anytime our captcha recognition code fails, we can backtrack and progress with a new captcha. Overall, it took around 80 minutes for my code to successfully solve 1,000 captches.
[Solution Script - rerecaptcha.py](https://github.com/csn3rd/UTCTFWriteups/blob/main/rerecaptcha.py)
[Solution Output - list of cookies](https://github.com/csn3rd/UTCTFWriteups/blob/main/captcha_cookies.txt)
Once we get the last cookie corresponding to 1,000 solves, we can send a GET request to get the flag.
Flag: `utflag{skibidi_bop_mm_dada_uLG7Jrd5hP}`