Tritium: una herramienta en go para password spraying

Hoy en día existen muchas herramientas para hacer password spraying mediante pre-autenticación de Kerberos, pero hoy he visto una escrita en Go llamada Tritium bastante interesante y una buena candidata para añadir al arsenal de todo buen pentester. 

Tal y como rezan en su Github incorporan las siguientes funcionalidades:

  • previene el bloqueo de usuarios del dominio
  • integra la enumeración de nombres de usuario con el proceso de spray de contraseñas (ambas son funcionalidades separadas)
  • capacidad de hacer spray de contraseñas de forma recursiva en lugar de ejecutar un simple spray
  • puede hacer resume/continuar el ataque e ignora las cuentas previamente comprometidas

Tritium permite lo mencionado anteriormente y más. Por ejemplo la enumeración de usuarios ya no desperdiciará un intento de login previo porque lo usará para generar un archivo de usuarios válidos. Tritium también le da al usuario la capacidad de pasarle un archivo de contraseñas para hacer spray de forma recursiva. Y sobretodo tiene la funcionalidad como comentábamos de que detecta si un dominio está bloqueando cuentas al guardar el estado y detiene el ataque si se bloquean 3 cuentas consecutivas.

Repo: https://github.com/S4R1N/Tritium
Instalación: go get S4R1N/Tritium

Uso:

 ./Tritium -h

        ___________      .__  __  .__               
        \__    ___/______|__|/  |_|__|__ __  _____  
          |    |  \_  __ \  \   __\  |  |  \/     \ 
          |    |   |  | \/  ||  | |  |  |  /  Y Y  \
          |____|   |__|  |__||__| |__|____/|__|_|__/ v 0.4
                                                                                          

          Author: S4R1N, alfarom256
 


 Required Params:

 -d            The full domain to use (-domain targetdomain.local)
 -dc           Domain controller to authenticate against (-dc washingtondc.targetdomain.local)
 -dcf          File of domain controllers to authenticate against 
 -u            Select single user to authenticate as (-user jsmith) 
 -uf           User file to use for password spraying (-userfile ~/home/users.txt)
 -p            Password to use for spraying (-password Welcome1)

 Optional: 

 -help         Print this help menu
 -o            Tritium Output file (default spray.json)
 -w            Wait time between authentication attempts [Default 1] (-w 0)    
 -jitter       % Jitter between authentication attempts      
 -rs           Enable recursive spraying 
 -ws           Wait time between sprays [Default 3600] (-ws 1800)
 -pwf          Password file to use for recursive 
 -res          Continue a password spraying campaign
 -rf           Tritium Json file 

CVE-2020-9484: RCE mediante deserialización en Apache Tomcat con PersistentManager

Hoy vamos a ver la explotación de la vulnerabilidad CVE-2020-9484 que publicó Jarvis Threedr3am, de pdd security research, a comienzos del verano pasado y que permite ejecución remota de código a través de deserialización en versiones de Apache Tomcat anteriores a abril de 2020. 

Eso sí, para que el servidor Tomcat objetivo sea vulnerable además los administradores tienen que haber configurado el uso de PersistentManager editando el archivo conf/context.xml, ya que por defecto Tomcat se ejecutará con StandardManager

  • StandardManager mantendrá las sesiones en la memoria. Si tomcat se cierra correctamente, almacenará las sesiones en un objeto serializado en el disco (llamado "SESSIONS.ser" por defecto). 
  • PersistentManager hace lo mismo, pero con un extra: hacer swapping de sesiones inactivas/idle. Si una sesión ha estado inactiva durante x segundos, se moverá al disco. Es una forma de reducir el uso de memoria. 

Cuando Tomcat recibe una solicitud HTTP con una cookie JSESSIONID, le pedirá al Manager que verifique si esa sesión ya existe. Como se controla el JSESSIONID ¿qué pasa por ejemplo si lo seteamos a un valor como "JSESSIONID=../../../../../../tmp/12345"? 

  • Tomcat solicita al Manager que verifique si existe una sesión con el ID de sesión "../../../../../../tmp/12345"
  • Primero verificará si tiene esa sesión en la memoria. 
  • Si no es así y está configurado PersistentManager verificará si tiene la sesión en el disco.
  • Verificará en el directorio + sessionid + ".session", por lo que se evalúa como “./session/../../../../../../tmp/12345.session“ 
  • Si el archivo existe, lo deserializará y analizará la información de la sesión.

Entonces, ya ha os habéis imaginado que necesitamos cargar un objeto serializado malicioso en un path interno de la máquina para que podamos recuperarlo a través de la cookie JSESSIONID y poder conseguir RCE. 

Taller de exploiting: ret2libc en Linux x64

Seguimos con exploiting en Linux, con el mismo código en la entrada anterior pero esta vez activando el bit NX (no-execute), es decir, la protección que nos marcará el stack como no ejecutable:
#include <stdio.h>
#include <unistd.h>

int vuln() {
    char buf[80];
    int r;
    r = read(0, buf, 400);
    printf("\nHas pasado %d bytes. buf es %s\n", r, buf);
    puts("No shell!");
    return 0;
}

int main(int argc, char *argv[]) {
    vuln();
    return 0;
}
Para ello compilamos el código esta vez sin '-z execstack':
$ gcc -fno-stack-protector ejercicio2x64.c -o ejercicio2x64
No nos olvidamos de desactivar ASLR (de momento) para realizar el ejercicio:
$ sudo sysctl -w kernel.randomize_va_space=0
Y cambiarle los permisos necesarios también:
$ sudo chown root ret2libc
$ sudo chmod 4755 ret2libc
Comprobamos las protecciones del programa compilado:
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial

Ya tenemos el binario para trabajar con él, pero recordar que ya no podremos apuntar la dirección de retorno a nuestro shellcode directamente en la pila, por lo que tendremos que utilizar la técnica return to libc o ret2libc. 

¿Qué es ret2libc? 

Cada vez que escribimos un programa en C utilizamos funciones como printf, scanf, put, etc. y todas esas funciones estándar de C se han compilado en un solo archivo que es la librería estándar de C o libc, el cual es independiente del binario (programa compilado). 

Ret2libc es una técnica que se basa en ejecutar código que no se encuentra en la pila sino en un sector de la memoria de libc, que es ejecutable. Es decir, el código utilizado para vulnerar el programa son funciones dentro de esta librería. 

Resumiendo: podemos modificar la dirección de retorno para que apunte a libc, que cuenta con funciones muy útiles como system() para, por ejemplo, obtener una shell. 

El resumen del payload más común sería el siguiente: 

Junk + RET + POP RDI + shell + System Address 

Lo más visto del 2020 en Hackplayers

Último día del año y tradicional post con las 50 entradas del blog más leídas durante el año, este 2020 tildado como maldito por la pandemia de este infame virus que nos azota desde hace ya muchos meses.

Así que no puedo despedir este año sin desear que este nuevo que entra sea el de la recuperación y el principio de la vuelta a la normalidad. Sabemos que las próximas semanas serán duras y que probablemente nos enfrentaremos a un nueva ola así que a todos mucha precaución y paciencia.

Ya sabéis que a principios de año y ajenos todavía a lo que se venía, tuvimos la suerte de poder celebrar en condiciones normales y disfrutar nuestra conferencia h-c0n, pero que en 2021 no la podremos celebrar dadas las circunstancias.

Eso sí, diluiremos nuestra participación en otras actividades a lo largo del año: en otros eventos online con nuestros amigos, jugando a CTFs, debatiendo y compartiendo info en grupos y redes sociales, llorando o riendo... allí estaremos alguno de los locos de Hackplayers pues, como decía Goethe, la locura, a veces, no es otra cosa que la razón presentada bajo diferente forma.   

Cuidaros mucho.

  1. El "texto de la muerte" para los usuarios de WhatsApp en Android
  2. Crackear la contraseña de cualquier usuario de Windows sin ningún privilegio (cuenta de invitado incluida)
  3. Listado de códigos secretos de Android
  4. El abc para desproteger un Excel con contraseña
  5. Cómo fingir ser un hacker
  6. Grupos de Telegram sobre hacking y seguridad informática en español
  7. 100 grupos de hackers famosos
  8. 15 sitios para practicar hacking (legalmente) este verano
  9. Truco para *petar* un grupo de WhatsApp (bomba de emoticonos)
  10. 8 servicios proxy gratuitos para evitar restricciones y mantener el anonimato y la privacidad
  11. Introducción a Social-Engineering Toolkit (SET)
  12. WiFite: crackear redes wifi para dummies
  13. hackplayers: Blogroll en español
  14. Cómo clonar la tarjeta SD de la Raspberry Pi
  15. iDict, una herramienta de fuerza bruta para obtener contraseñas de usuarios de iCloud
  16. Tutorial para modificar un APK añadiéndole un payload msf
  17. Una lista de películas y series que todo hacker debe ver
  18. Recursos y herramientas para el descubrimiento de subdominios
  19. Bitcracker: la primera herramienta opensource para crackear volúmenes cifrados con BitLocker
  20. Cómo identificar el proceso que está usando un puerto TCP determinado
  21. hackplayers: Retos de Hackplayers
  22. Xencrypt: un crypter en PowerShell
  23. Descarga gratis los 100 mejores libros de hacking de Raj Chandel
  24. Listado de herramientas online para escanear sitios web potencialmente maliciosos
  25. Cómo hacerte un IMSI Catcher sencillo por sólo 11€
  26. androrat: un troyano RAT para Android
  27. SAD DNS: un nuevo ataque que puede hacer tambalear los cimientos de Internet
  28. HTTP-revshell: controla el equipo de la víctima a través de un canal encubierto
  29. Métodos para ocultar el historial de bash
  30. SearchOrg: script para obtener información de empresas mediante su dominio
  31. Principales vulnerabilidades en un Directorio Activo
  32. Aplicación Android para robar y leer chats de WhatsApp en Android (PoC)
  33. Zerologon desatado: la vulnerabilidad que permite comprometer cualquier controlador de dominio de Windows fácilmente
  34. hackplayers: Participa en Hackplayers
  35. El "texto de la muerte" para los usuarios de Apple
  36. Listado de sandboxes de análisis de malware gratuitos y online
  37. Algunos google dorks para espiar cámaras web en Internet
  38. De cómo colarse en el metro de forma elegante... (NFC hack)
  39. Comprometido por sólo copiar y pegar texto de una página maliciosa (obtención de una shell mediante pastejacking)
  40. SIGRed: vulnerabilidad crítica en el servidor DNS de Windows (CVE-2020-1350)
  41. Taller para escalar privilegios en Windows/Linux
  42. Ataques DoS "Slow HTTP" mediante SlowHTTPTest
  43. 13 herramientas para desofuscar código para reversers
  44. Hacking Wireless con Airgeddon (by @OscarAkaElvis #hc0n2019)
  45. Android Hacking 101 - Introducción
  46. Herramientas SQL Injection
  47. El viejo truco de las sticky keys sigue funcionado
  48. whatsapp-phishing o cómo robar la sesión de un usuario de WhatsApp Web
  49. Evadir un portal cautivo mediante un túnel DNS
  50. SIEM, ¿Durmiendo con el enemigo?

Taller de exploiting: baby BOF en Linux x64

Voy a retomar los ejercicios de exploiting en Linux, esta vez en arquitectura de 64 bits. Básicamente se trata de lo mismo que en 32 bits pero con unos "pequeños" cambios, principalmente:   

  • Los registros de propósito general se han ampliado a 64 bits. Así que ahora tenemos RAX, RBX, RCX, RDX, RSI y RDI. 
  • El puntero de instrucción (instruction pointer), el puntero de base (base pointer) y el puntero de pila (stack pointer) también se han ampliado a 64 bits como RIP, RBP y RSP respectivamente. 
  • Se han proporcionado registros adicionales: R8 a R15. 
  • Los punteros tienen un ancho de 8 bytes. 
  • Push/pop en la pila tienen 8 bytes. 
  • El tamaño máximo de dirección canonical/userspace es de 48bits, los menos significativos: 0x00007FFFFFFFFFFF. 
  • Los parámetros de las funciones se pasan a través de registros.   

Dicho ésto, vamos a empezar con el clásico smashing stack explotando el binario a partir del siguiente código:  

#include <stdio.h>
#include <unistd.h>

int vuln() {
    char buf[80];
    int r;
    r = read(0, buf, 400);
    printf("\nHas pasado %d bytes. buf es %s\n", r, buf);
    puts("No shell!");
    return 0;
}

int main(int argc, char *argv[]) {
    vuln();
    return 0;
}
 Lo compilamos sin las protecciones correspondientes:  
$ gcc -fno-stack-protector -z execstack ejercicio1x64.c -o ejercicio1x64
Le ponemos el SUID para obtener posteriormente una root shell:  
$ sudo chown root ejercicio1x64
$ sudo chmod 4755 ejercicio1x64
Y no olvidemos desactivar temporalmente ASLR para el ejercicio:  
echo 0 > /proc/sys/kernel/randomize_va_space 
Probamos la ejecución normal del programa:  
$ ./ejercicio1x64 
AAAAAAAAAAAAAAAAAAAAAAAAAAAa
 
Has pasado 29 bytes. buf es AAAAAAAAAAAAAAAAAAAAAAAAAAAa

Ya tenemos el binario así que vamos manos a la obra :-P

Sitúan a un hacker español como el mejor valorado de 2020

Alejandro Tapia Vacas (arriba en la foto) es español y posiblemente un desconocido para la prensa, pero ahí dónde lo veis ha estado en los puestos más altos de los CTFs más importantes del mundo: Defcon, NorthSec, VolgaCTF, Nuit du Hack, PHD, RuCTF y un largo etcétera... no ha faltado a las citas más importantes dónde los hackers compiten entre sí midiendo sus destrezas y en todas ellas ha ido sumando un score impresionante de 28 (https://ctftime.org), lo que ha hecho ponerse en cabeza de la clasificación global y ser reconocido internacionalmente por la UHA (United Hackers Association).

Autor de varios artículos, también en este blog, a sus 26 años ya trabaja en una multinacional reconocida como penetration tester a la vez que compagina su trabajo con la participación en competiciones de forma individual o con su grupo P1k0rd3p3l0t4$. Asiduo también en grupos de Telegram como el propio de Hackplayers y HTB hispano (donde le conocimos) ha tenido tiempo incluso de mejorar aún más su ranking en plataformas de bug bounty como HackerOne y Bugcrowd, a través de las cuales ya ha conseguido superar la barrera de los 150€. Sin duda y pese a la pandemia este ha sido su año así que sirva también este post como otro pequeño gran homenaje. ¡Enhorabuena Ale!


Este artículo fue publicado el día 28 de Diciembre de 2020, Día de los Santos Inocentes. Y no... nuestro amigo Jordi triunfa pero en otro tipo de industria...

 

¡Feliz navidad!

import turtle

def main():
    window = turtle.Screen()
    my_turtle = turtle.Turtle()
    screen = my_turtle.getscreen()
    screen.title("¡Feliz navidad!")
    screen.bgcolor("Blue")
    my_turtle.color("green")
    my_turtle.pensize(5)
    my_turtle.begin_fill()
    my_turtle.forward(100)
    my_turtle.left(150)
    my_turtle.forward(90)
    my_turtle.right(150)
    my_turtle.forward(60)
    my_turtle.left(150)
    my_turtle.forward(60)
    my_turtle.right(150)
    my_turtle.forward(40)
    my_turtle.left(150)
    my_turtle.forward(100)
    my_turtle.left(60)
    my_turtle.forward(100)
    my_turtle.left(150)
    my_turtle.forward(40)
    my_turtle.right(150)
    my_turtle.forward(60)
    my_turtle.left(150)
    my_turtle.forward(60)
    my_turtle.right(150)
    my_turtle.forward(90)
    my_turtle.left(150)
    my_turtle.forward(133)
    my_turtle.end_fill()
    my_turtle.color("red")
    my_turtle.pensize(1)
    my_turtle.begin_fill()
    my_turtle.right(90)
    my_turtle.forward(70)
    my_turtle.right(90)
    my_turtle.forward(33)
    my_turtle.right(90)
    my_turtle.forward(70)
    my_turtle.end_fill()
    my_turtle.speed(1)
    my_turtle.penup()
    my_turtle.color('yellow')
    my_turtle.goto(-28, 110)
    my_turtle.begin_fill()
    my_turtle.pendown()
    for i in range(5):
        my_turtle.forward(40)
        my_turtle.right(144)
    my_turtle.end_fill()
    def ball(trt, x, y, size=10, colour="red"):
        trt.penup()
        trt.setpos(x, y)
        trt.color(colour)
        trt.begin_fill()
        trt.pendown()
        trt.circle(size)
        trt.end_fill()
    ball(my_turtle, 95, -5)
    ball(my_turtle, -110, -5)
    ball(my_turtle, 80, 40, size=7, colour="yellow")
    ball(my_turtle, -98, 40, size=7, colour="yellow")
    ball(my_turtle, 70, 70, size=5)
    ball(my_turtle, -93, 70, size=5)
    def create_circle(turtle, x, y, radius, color):
        my_turtle.penup()
        my_turtle.color(color)
        my_turtle.fillcolor(color)
        my_turtle.goto(x, y)
        my_turtle.pendown()
        my_turtle.begin_fill()
        my_turtle.circle(radius)
        my_turtle.end_fill()
    create_circle(my_turtle, 230, 180, 60, "white")
    create_circle(my_turtle, 210, 180, 60, "#E731CE")
    my_turtle.speed(1)
    my_turtle.penup()
    msg = "¡Feliz navidad desde Hackplayers!"
    my_turtle.goto(0, -200)  
    my_turtle.color("white")
    my_turtle.pendown()
    my_turtle.write(msg, move=False, align="center", font=("Arial", 20, "bold"))
    my_turtle.hideturtle()
    window.mainloop()

if __name__ == "__main__":
    main()

 Ho ho ho! 2013 2014 2015 2016 2017 2018 2019 ...

CVE-2020-16875: ejecución remota de código en Microsoft Exchange

En septiembre de 2020 se publicó un parche para CVE-2020-16875 que afecta a Microsoft Exchange 2016 y 2019. La vulnerabilidad permite ejecución remota de código (RCE) a través de los cmdlets proporcionados por el endpoint HTTPS /ecp/DLPPolicy de un servidor Exchange.

La vulnerabilidad y la investigación originales fueron reportadas por Steven Seeley de Source Incite (@steventseeley):

https://srcincite.io/advisories/src-2020-0019/ (Aviso)
https://srcincite.io/pocs/cve-2020-16875.py.txt (PoC)

Para solucionar dicha vulnerabilidad se publicó el siguiente parche que filtraba los cmdlets correspondientes:

https://support.microsoft.com/en-us/help/4577352/security-update-for-exchange-server-2019-and-2016

Pero, como ya ha pasado otra veces, el análisis del parche condujo al bypass del mismo, y buena cuenta de ello dio el equipo de X41 D-Sec, que volvieron a conseguir inyectar cmdlets en un servidor remoto Exchange. Eso sí, recordar que se requiere un usuario válido con permisos para administrar las políticas de DLP.

El código del parche que previene la explotación es el siguiente:

internal static void ValidateCmdletParameters(string cmdlet,
    IEnumerable<KeyValuePair<string, string>> requiredParameters)
{
    if (string.IsNullOrWhiteSpace(cmdlet))
    {
        return;
    }
    Collection<PSParseError> collection2;
    Collection<PSToken> collection = PSParser.Tokenize(cmdlet,
        out collection2);
    if (collection2 != null && collection2.Count > 0)
    {
        throw new DlpPolicyParsingException(
            Strings.DlpPolicyNotSupportedCmdlet(cmdlet));
    }
    if (collection != null)
    {
        // #1 CHECKS IF THERE IS MORE THAN ONE COMMAND, BUT DOES NOT
        // RECOGNIZE .NET FUNCTIONS SUCH AS [Int32]::Parse("12")
        if ((from token in collection
        where token.Type == PSTokenType.Command 
        select token).ToList<PSToken>().Count > 1)
        {
            throw new DlpPolicyParsingException(
                Strings.DlpPolicyMultipleCommandsNotSupported(cmdlet));
        }
    }
    bool flag = false;
    foreach (KeyValuePair<string, string> keyValuePair in requiredParameters)
    {
        // #2 CHECKS IF THE cmdlet STRING(!!) STARTS WITH AN ALLOWED KEY
        if (cmdlet.StartsWith(keyValuePair.Key,
            StringComparison.InvariantCultureIgnoreCase))
        {
            // #3 CHECKS IF THE THE VALUES / PARAMETERS MATCH A CERTAIN
            // REGEX
            if (!Regex.IsMatch(cmdlet, keyValuePair.Value,
                RegexOptions.IgnoreCase))
            {
                throw new DlpPolicyParsingException(
                    Strings.DlpPolicyMissingRequiredParameter(cmdlet,
                        keyValuePair.Value));
            }
            flag = true;
        }
    }
    if (!flag)
    {
        throw new DlpPolicyParsingException(Strings.DlpPolicyNotSupportedCmdlet(
                                                                        cmdlet));
    }
}

Proyecto Freki: una interesante plataforma de análisis de malware

Freki es una plataforma de análisis de malware de código abierto y gratuita que tiene como objetivo facilitar el análisis y reversing de malware. Proporciona una API REST fácil de usar para diferentes proyectos y es de fácil implementación (a través de Docker). Además permite la incorporación de nuevas funciones por parte de la comunidad.

Podríamos decir que incluye las funcionalidades de VirusTotal y MalwareBazaar en una única instancia. Las versión actual incluye las siguientes características:
  • Extracción de hashes: MD5, SHA-1, SHA-256, SHA-384, SHA-512, CRC32 y SSDEEP
  • Consulta la API de VirusTotal para traerse los resultados de AV
  • Análisis estático de muestras de PE: headers, secciones, imports, capabilities y strings
  • Matching de patrones con reglas Yara
  • Gestión de usuarios: creación de cuentas para envíos de muestras y uso de API
  • Comentarios de la comunidad: los usuarios pueden comentar y discutir sobre las muestras
  • Descargar muestras: Todas las muestras están disponibles de forma gratuita

Instalación mediante docker (la forma más sencilla):
  • Clonar repositorio: git clone https://github.com/crhenr/freki.git
  • Instalar Docker y Docker Compose
  • Editar el fichero .env
  • Si estás ejecutándolo en producción se recomienda editar freki.conf y activar HTTPS
  • Ejecutar docker-compose up o make.

Recuperar contraseñas pixeladas en capturas de pantalla

Capturar pantallas con Greenshot, Shutter u otras herramientas y pixelar texto sensible suele estar a la orden del día. No es difícil encontrar en informes o incluso en posts en Internet pantallazos con esta información "ofuscada", incluso contraseñas. ¿Y si hubiera una herramienta que pudiera "despixelar" y descubrir ese texto en claro?

Lo habéis adivinado, Sipke Mellema ha diseñado un algoritmo y publicado una herramienta en Github para ver en claro el texto pixelado. Básicamente este algoritmo se parece bastante en su base a otros que despixelan imágenes: la técnica consiste en pixelar caracteres similares y verificar si coinciden. En su imagen de ejemplo podemos encontrar la fuente de notepad de Windows:

Esa captura de pantalla se usa como una imagen de búsqueda para bloques similares. Como veis se trata de una secuencia De Bruijn de los caracteres esperados, con combinaciones de 2 caracteres porque algunos bloques pueden superponerse. Encontrar coincidencias adecuadas requiere que exista el bloque exacto de la misma configuración de píxeles en la imagen de búsqueda. Cuando nos encontramos más letras consecutivas los bloques de múltiples coincidencias circundantes se comparan luego para que estén a la misma distancia geométrica que en la imagen pixelada. Estas coincidencias se tratan también como correctas.

Una vez que los bloques correctos no tengan más coincidencias geométricas, generará todos los bloques correctos directamente. Para bloques de múltiples coincidencias, genera el promedio de todas las coincidencias.

Su salida no es ni de lejos perfecta, pero funciona bastante bien. Veamos el uso de la herramienta y sus resultados. Partiendo de la siguiente imagen pixelada:

Ejecutamos la herramienta con la imagen de búsqueda de la secuencia De Brujin con caracteres del Bloc de notas de Windows 10 (incluida en el repo de la herramienta):

$ python3 depix.py -p images/testimages/testimage3_pixels.png -s images/searchimages/debruinseq_notepad_Windows10_closeAndSpaced.png -o output.png
INFO:root:Loading pixelated image from images/testimages/testimage3_pixels.png
INFO:root:Loading search image from images/searchimages/debruinseq_notepad_Windows10_closeAndSpaced.png
INFO:root:Finding color rectangles from pixelated space
INFO:root:Found 116 same color rectangles
INFO:root:86 rectangles left after moot filter
INFO:root:Found 1 different rectangle sizes
INFO:root:Finding matches in search image
INFO:root:Removing blocks with no matches
INFO:root:Splitting single matches and multiple matches
INFO:root:[10 straight matches | 76 multiple matches]
INFO:root:Trying geometrical matches on single-match squares
INFO:root:[15 straight matches | 71 multiple matches]
INFO:root:Trying another pass on geometrical matches
INFO:root:[17 straight matches | 69 multiple matches]
INFO:root:Writing single match results to output
INFO:root:Writing average results for multiple matches to output
INFO:root:Saving output image to: output.png

Y como veis el resultado es sorprendente, se puede leer perfectamente el texto en claro:

¿A qué ahora te pensarás dos veces pixelar texto en lugar de por ejemplo tacharlo completamente o superponer un rectángulo? ;)

Github: https://github.com/beurtschipper/Depix