3 puntos por GN⁺ 2025-04-04 | 1 comentarios | Compartir por WhatsApp
  • curl-impersonate es una compilación modificada de curl para realizar handshakes TLS y HTTP que parecen hechos por Chrome, Edge, Safari y Firefox, y puede usarse como herramienta CLI o como biblioteca de reemplazo de libcurl
  • El Client Hello y la configuración de HTTP/2 de los clientes y bibliotecas HTTP comunes difieren mucho de los navegadores reales, por lo que algunos servicios web identifican al cliente por el handshake TLS/HTTP y entregan contenido diferente
  • La implementación usa NSS para Firefox y BoringSSL para la familia Chrome, con cambios en extensiones TLS, opciones SSL y configuración HTTP/2, además de aplicar flags no predeterminados como --ciphers, --curves y headers
  • Los objetivos compatibles incluyen Chrome 99~116, Chrome 99 para Android, Edge 99 y 101, Firefox 91 ESR~117, y Safari 15.3 y 15.5, y cada objetivo tiene su propio wrapper script y nombre de target
  • En la línea de comandos se ejecuta como curl_chrome116, y para integración por biblioteca puede usarse curl_easy_impersonate() o aplicarse a apps existentes que usan libcurl mediante LD_PRELOAD y CURL_IMPERSONATE en Linux

El problema que resuelve curl-impersonate

  • curl-impersonate es un proyecto que compila curl de forma especial para que pueda imitar a los cuatro navegadores principales: Chrome, Edge, Safari y Firefox
  • Puede realizar handshakes TLS y HTTP idénticos a los de los navegadores reales
  • Tiene dos formas de uso
    • Una herramienta de línea de comandos parecida a curl normal
    • Una biblioteca que puede integrarse en lugar de libcurl

Por qué hace falta

  • Cuando un cliente HTTP se conecta a un sitio web con TLS, primero realiza un handshake TLS
  • El primer mensaje del handshake TLS es Client Hello, y el Client Hello que generan la mayoría de clientes y bibliotecas HTTP difiere mucho del de los navegadores reales
  • Si el servidor usa HTTP/2, además del handshake TLS también se realiza un handshake HTTP/2, donde se intercambian varias configuraciones
  • La configuración de HTTP/2 de la mayoría de clientes y bibliotecas HTTP también difiere de la de los navegadores reales
  • Algunos servicios web aprovechan estas diferencias para identificar al cliente que se conecta y servir contenido distinto según el cliente
    • Este método se conoce como TLS fingerprinting y HTTP/2 fingerprinting
    • El README señala que el uso extendido de estas técnicas ha hecho que la web sea menos abierta, menos privada y más restrictiva para ciertos clientes web

Cómo funciona

  • curl está bastante parcheado para que se vea como un navegador
  • Los principales cambios son los siguientes
    • La versión de Firefox compila curl con NSS, la biblioteca TLS que usa Firefox, en lugar de OpenSSL
    • La versión de Chrome se compila con BoringSSL, la biblioteca TLS de Google
    • Se cambia la forma en que curl configura varias extensiones TLS y opciones SSL
    • Se añade soporte para nuevas extensiones TLS
    • Se cambia la configuración usada en conexiones HTTP/2
    • Se ejecuta curl con flags no predeterminados como --ciphers, --curves y algunos headers -H
  • Como resultado, desde la perspectiva de la red, curl se ve igual que un navegador real
  • La explicación técnica completa está en part a y part b

Navegadores compatibles y nombres de target

  • La lista de navegadores compatibles también está disponible en browsers.json
  • Targets compatibles de la familia Chrome
    • Chrome 99, 100, 101, 104, 107, 110, 116 en Windows 10
    • Chrome 99 en Android 12
    • Edge 99 y 101 en Windows 10
    • Safari 15.3 en MacOS Big Sur
    • Safari 15.5 en MacOS Monterey
  • Targets compatibles de Firefox
    • Firefox 91 ESR, 95, 98, 100, 102, 109 y 117 en Windows 10
  • Cada target compatible tiene un nombre de target y un wrapper script
    • Ejemplo: el target chrome116 se ejecuta con el wrapper script curl_chrome116
    • Ejemplo: el target ff117 se ejecuta con el wrapper script curl_ff117

Uso básico

  • Para cada navegador compatible hay un wrapper script que ejecuta curl-impersonate con los headers y flags necesarios
  • Ejemplo de ejecución
    curl_chrome116 https://www.wikipedia.org
    
  • Si agregas flags de línea de comandos, se pasan a curl
  • Algunos flags pueden cambiar la firma TLS de curl y hacer que se detecte
  • El wrapper script usa un conjunto predeterminado de headers HTTP
    • Si quieres cambiar los headers, puedes modificar el wrapper script según tu objetivo
  • Más opciones están incluidas en el uso avanzado de libcurl-impersonate como biblioteca

Instalación y forma de distribución

  • Por razones técnicas, curl-impersonate tiene dos versiones
    • Versión chrome: se usa para imitar Chrome, Edge y Safari
    • Versión firefox: se usa para imitar Firefox
  • Los binarios precompilados para Linux y macOS Intel se ofrecen en GitHub releases
  • Antes de usar los binarios precompilados, hay que instalar NSS y los certificados CA
    • Ubuntu: sudo apt install libnss3 nss-plugin-pem ca-certificates
    • Red Hat/Fedora/CentOS: yum install nss nss-pem ca-certificates
    • Archlinux: pacman -S nss ca-certificates
    • macOS: brew install nss ca-certificates
  • El sistema también necesita zlib
    • zlib casi siempre está presente, pero puede faltar en algunos sistemas mínimos
  • Los binarios precompilados para Linux están construidos para sistemas Ubuntu
    • Si aparece un error de verificación de certificados en otra distribución, puede ser necesario indicar a curl dónde están los certificados CA
    curl_chrome116 https://www.wikipedia.org --cacert /etc/ssl/certs/ca-bundle.crt
    
  • Para compilar desde código fuente, consulta INSTALL.md

Docker y paquetes

  • Hay imágenes Docker basadas en Alpine Linux y Debian disponibles en Docker Hub
  • Las imágenes Docker incluyen los binarios y todos los wrapper scripts
  • Ejemplo
    # Versión Firefox, Alpine Linux
    docker pull lwthiker/curl-impersonate:0.6-ff
    docker run --rm lwthiker/curl-impersonate:0.6-ff curl_ff109 https://www.wikipedia.org
    
    
    # Versión Chrome, Alpine Linux
    docker pull lwthiker/curl-impersonate:0.6-chrome
    docker run --rm lwthiker/curl-impersonate:0.6-chrome curl_chrome110 https://www.wikipedia.org
    
  • Los usuarios de Archlinux pueden usar paquetes de AUR
  • Hay una fórmula no oficial de Homebrew para Mac, solo para Chrome
    brew tap shakacode/brew
    brew install curl-impersonate
    

Uso avanzado de libcurl-impersonate

  • libcurl-impersonate.so es una libcurl compilada con los mismos cambios que curl-impersonate de línea de comandos
  • Tiene una función API adicional
    CURLcode curl_easy_impersonate(struct Curl_easy *data, const char * target,
                                   int default_headers);
    
  • Si se llama con un nombre de target como chrome116, configura internamente las opciones y headers que antes establecía el wrapper script
  • Si default_headers es 0, no configura la lista integrada de headers HTTP
    • En ese caso, el usuario debe proporcionar los headers directamente con la opción normal de libcurl CURLOPT_HTTPHEADER
  • curl_easy_impersonate() configura varias opciones de libcurl
    • CURLOPT_HTTP_VERSION
    • CURLOPT_SSLVERSION, CURLOPT_SSL_CIPHER_LIST, CURLOPT_SSL_EC_CURVES, CURLOPT_SSL_ENABLE_NPN, CURLOPT_SSL_ENABLE_ALPN
    • CURLOPT_HTTPBASEHEADER, una opción no estándar del proyecto
    • CURLOPT_HTTP2_PSEUDO_HEADERS_ORDER, CURLOPT_HTTP2_NO_SERVER_PUSH, opciones HTTP/2 no estándar del proyecto
    • CURLOPT_SSL_ENABLE_ALPS, CURLOPT_SSL_SIG_HASH_ALGS, CURLOPT_SSL_CERT_COMPRESSION, CURLOPT_SSL_ENABLE_TICKET, opciones TLS no estándar del proyecto
    • CURLOPT_SSL_PERMUTE_EXTENSIONS, una opción TLS no estándar del proyecto
  • Si después llamas a curl_easy_setopt() con alguna de las opciones anteriores, sobrescribes el valor configurado por curl_easy_impersonate()

Aplicarlo a apps existentes con libcurl

  • Si la aplicación ya usa libcurl, en Linux se puede reemplazar la biblioteca existente en tiempo de ejecución con LD_PRELOAD
  • Configurando la variable de entorno CURL_IMPERSONATE se aplica la suplantación automáticamente
    LD_PRELOAD=/path/to/libcurl-impersonate.so CURL_IMPERSONATE=chrome116 my_app
    
  • CURL_IMPERSONATE tiene dos efectos
    • Cuando se crea un nuevo handle de curl con curl_easy_init(), se llama automáticamente a curl_easy_impersonate()
    • Después de curl_easy_reset(), también se llama automáticamente a curl_easy_impersonate()
  • Si necesitas un control fino sobre los headers HTTP, puedes desactivar la lista integrada de headers con CURL_IMPERSONATE_HEADERS=no y configurarlos tú mismo
    LD_PRELOAD=/path/to/libcurl-impersonate.so CURL_IMPERSONATE=chrome116 CURL_IMPERSONATE_HEADERS=no my_app
    
  • El método con LD_PRELOAD no funciona con curl mismo
    • Porque la herramienta curl sobrescribe la configuración TLS
    • Para curl hay que usar el wrapper script

Estructura del repositorio y contribuciones

  • El repositorio tiene dos carpetas principales
    • chrome: scripts y parches para construir la versión curl-impersonate de Chrome
    • firefox: scripts y parches para construir la versión curl-impersonate de Firefox
  • El directorio de Firefox, por ejemplo, incluye los siguientes archivos
    • Dockerfile: se usa para compilar curl-impersonate con todas las dependencias
    • curl_ff91esr, curl_ff95, curl_ff98: wrapper scripts para ejecutarlo con los flags correctos
    • curl-impersonate.patch: el parche principal que hace que curl use las mismas extensiones TLS que Firefox y permite compilación estática con libnghttp2 y libnss
  • tests/signatures es una base de datos YAML de firmas conocidas de navegadores que se pueden imitar
  • Los parches reales de curl se mantienen en un repositorio separado derivado de upstream curl

1 comentarios

 
GN⁺ 2025-04-04
Opiniones en Hacker News
  • Hay un fork con bastantes mejoras respecto del original y que se mantiene activamente: https://github.com/lexiforest/curl-impersonate
    También hay bindings para usuarios de Python: https://github.com/lexiforest/curl_cffi

    • En cierto modo es natural que un programa para “hacer que curl parezca un navegador” reciba patrocinio de un servicio de evasión de detección de bots
    • También hay un módulo que lo integra por completo con la biblioteca requests de Python: https://github.com/el1s7/curl-adapter
    • Es raro tener que seguir el ritmo de tecnologías “avanzadas” que cambian más rápido de lo que uno puede girar la cabeza, solo para hacer una simple solicitud que parezca uno de los tres principales navegadores web “certificados”.
      Paradójicamente, esa solicitud le impondría menos carga al servidor que un navegador certificado; uno se pregunta si esta es la distopía sobre la que nos advertían en los 90. Me pregunto quién aquí se animaría a nombrar a una empresa que, mientras se posiciona como buena contribuidora de la comunidad open source/hacker, creó esta situación.
  • Ojalá Ladybird gane impulso en el futuro. Actualmente usa cURL oficial para networking, pero ese enfoque puede tener obstáculos.
    Por ejemplo, entiendo que cURL todavía tiene algunas limitaciones y no puede manejar WebSocket sobre h2. En cambio, si surge un motor de navegador en crecimiento, el tráfico normal también tendría la misma huella digital que cURL básico, y esa vía de fingerprinting podría desaparecer.

    • Ojalá el uso de cURL por parte de Ladybird sirva para mejorar el propio cURL en aspectos como el WebSocket sobre h2 mencionado, por ejemplo.
      También es un buen banco de pruebas para ver qué funcionalidades le faltan a cURL tomando como referencia flujos de trabajo reales de un navegador.
    • Sobre la parte de que “si hay un motor de navegador en crecimiento, esta vía de fingerprinting podría desaparecer”, por lo que he visto en CloudFlare y similares, es casi lo contrario.
      En los últimos meses aumentó mucho el nivel de fingerprinting y de “abuso” de comportamientos definidos por la implementación, probablemente como intento de matar otros motores de navegador. A los incumbentes no les gusta en absoluto la competencia.
      La otra parte intenta presentarlo como “DDoS de bots de IA”, pero me pregunto cuánto de eso fue creado por ellos mismos.
    • Cuando hablé con esta gente [0], trataron varias diferencias de implementación extrañas para crear firmas, incluyendo elementos del stack TCP que una app en espacio de usuario no puede controlar.
      Me gusta este curl, pero me preocupa que, cuando algún componente asume el papel de hacer trucos para “ponerse al día”, se acumule una carga de “compatibilidad” difícil de mantener.
      Lo ideal sería poder decir simplemente: “Hola, soy curl, déjame entrar”.
      Claro que el problema está en los servidores exigentes con el código de vestimenta, y ese problema a su vez existe por los estafadores que entran disfrazados, así que es un ciclo tipo el huevo y la gallina.
      [0] https://cybershow.uk/episodes.php?id=39
    • Ojalá que gracias a esto Ladybird llegue a soportar URL ftp.
    • Ladybird no tiene los recursos para competir con los navegadores actuales. Tuvo buena publicidad, pero le faltan ventajas frente a Chromium o una razón de existir.
      Además, el hecho de que se haya rediseñado en C++, que demostrablemente no es seguro, implica un gran riesgo de seguridad.
  • ¿Habrán configurado también IP_TTL para que coincida con el valor TTL de la plataforma que intentan suplantar?
    Si no, también se puede hacer cierto fingerprinting en la capa IP. Si el valor TTL en la capa IP es menor que 64, queda claro que no se está ejecutando en Windows moderno, o que es un Windows moderno con el TTL predeterminado modificado. Esto se debe a que el TTL predeterminado de los paquetes en Windows moderno empieza en 128, mientras que en la mayoría de las otras plataformas empieza en 64.
    Como las demás plataformas también se comunican por Internet sin problemas, un paquete IP proveniente de Windows moderno siempre se verá del lado remoto con un TTL de 64 o más, probablemente un poco por encima de 64. Dicho esto, el fingerprinting en la capa IP es difícil, pero no imposible.

    • Solo es difícil cuando se usa PaaS/IaaS que no ofrece acceso de bajo nivel al stack TCP/IP. Si operas tu propio servidor, hacer fingerprinting de todo tipo de propiedades TCP/IP es muy fácil.
      https://en.wikipedia.org/wiki/TCP/IP_stack_fingerprinting
    • ¿El valor TTL de los paquetes recibidos no varía según el estado de la red? ¿Puede el servidor reconstruir el valor original del cliente?
    • ¿Por qué TTL se diseñó para disminuir en vez de aumentar? ¿No se espera normalmente que quien enruta el tráfico decida si lo enruta y cómo?
  • Un momento: si el handshake TLS se ve diferente, ¿no se podría filtrar a nivel de nginx el tráfico que dice ser un navegador web pero en realidad es un script de Python/PHP?
    Por ejemplo, cuando usa un user agent de Chrome pero no es un navegador real. Como la mayor parte del tráfico de bots maliciosos entraría ahí, sería bueno poder bloquearlo sin más.

    • Cloudflare usa huellas TLS JA3/JA4, que son hashes de varios parámetros del handshake TLS.
      El funcionamiento está detallado en https://github.com/FoxIO-LLC/ja4/blob/main/technical_details..., y también ofrecen un módulo para Nginx: https://github.com/FoxIO-LLC/ja4-nginx-module
    • Básicamente eso es lo que hacen empresas de seguridad como Cloudflare, y además agregan mucho más fingerprinting, como desafíos JavaScript que verifican el intérprete de JavaScript/DOM.
    • Se puede, y de hecho hay sitios que lo hacen, pero es pésimo. La confiabilidad es baja y termina bloqueando a cualquiera que no use el Chrome más reciente en el Windows más reciente.
      Si no estás sufriendo un ataque real en este momento, no deberías usar una lista de permitidos de huellas TLS.
    • La herramienta del artículo parece estar pensada exactamente para evitar ese tipo de bloqueos.
  • Es una herramienta genial, pero no debería importar si el cliente es un navegador o no. Es triste que en la realidad se necesiten herramientas así.

    • Hace unos 6 meses entré a un sitio de subastas gubernamentales que exigía Internet Explorer. Era realmente Internet Explorer, y el sitio seguía activo, así que los datos de las subastas estaban actualizados.
      Agregué una extensión de user agent en Chrome, lo cambié a IE y volví a intentar; funcionó, y todas las funciones del sitio también andaban bien. Por eso me dio tristeza y también me molestó. Probablemente esa agencia gubernamental hizo el sitio hace 25 años y no lo actualizó desde entonces.
    • Solo puedes entrar al sitio si usas el software que aprobamos. Todo lo demás se considera malicioso. ¡Muéstrenos sus papeles!
      Este tipo de gatekeeping también me parece triste. Según entiendo, los navegadores personalizados o user agents hechos por uno mismo nunca funcionarán en sitios tipo Cloudflare hasta que consigan suficiente influencia, dinero, cantidad de usuarios o algo similar para convencerlos.
  • Esta herramienta es bastante buena para trabajos de red team si se combina con pequeños scripts bash y GNU parallel.
    Me resultó útil para mapear endpoints HTTPS dentro de rangos de direcciones definidos en el alcance, que por varias razones solo respondían a navegadores adecuados o solo respondían si la configuración de SNI era correcta. También se pueden usar tal cual opciones normales de curl, como -H, para falsificar headers.

  • Publicación de Show HN de aquel entonces: https://news.ycombinator.com/item?id=30378562

    • En ese momento, en 2022, era solo para Firefox.
  • Cada vez que algo así aparece aquí, siempre tengo sentimientos encontrados. Por un lado, me gusta que indique que todavía queda algo de rebeldía e independencia entre la gente.
    Por otro lado, como ocurre con otros proyectos en los que “la libertad es inestable”, si atrae atención no deseada, la situación podría empeorar para quienes dependen de esto. Crear navegadores es difícil, y los actores dominantes existentes siguen haciéndolo más difícil.

    • Suena como si los desarrolladores de navegadores quisieran que los navegadores fueran identificables por huella digital, pero eso se parece más a algo que ocurre de forma natural porque distintas personas lo implementan de distintas maneras.
      No lo veo como un problema de rebeldía. Que el software se vuelva identificable por huella digital erosiona la privacidad y la diversidad del software.
  • Extraño un poco aquellos tiempos simples en los que, si a un sitio web le parecían bien los bots, los permitía, y si no, bloqueaba el user agent.

    • En esa época, los sitios web no consumían tantos recursos como ahora. Creo que la reacción negativa contra los bots es más bien un efecto secundario de que las expectativas sobre la experiencia web se volvieron demasiado pesadas.
  • Si solo son 3 parches y un wrapper de shell, Daniel podría animarse a programarlo. Personalmente, creo que esto debería entrar sí o sí en el curl mainline.