3 points par GN⁺ 2025-04-04 | 1 commentaires | Partager sur WhatsApp
  • curl-impersonate est un build modifié de curl conçu pour effectuer des handshakes TLS et HTTP qui ressemblent à ceux de Chrome, Edge, Safari et Firefox, et peut être utilisé comme outil en ligne de commande ou comme bibliothèque de remplacement de libcurl
  • Les Client Hello et paramètres HTTP/2 des clients et bibliothèques HTTP classiques diffèrent fortement de ceux des vrais navigateurs, si bien que certains services web identifient les clients via le handshake TLS/HTTP et servent un contenu différent
  • L’implémentation repose sur NSS pour Firefox et BoringSSL pour les navigateurs de type Chrome, avec des modifications des extensions TLS, des options SSL, des paramètres HTTP/2, ainsi que l’usage de drapeaux non par défaut comme --ciphers, --curves et certains en-têtes
  • Les cibles prises en charge sont Chrome 99 à 116, Chrome 99 sur Android, Edge 99 et 101, Firefox 91 ESR à 117, Safari 15.3 et 15.5, avec pour chacune un wrapper script et un nom de cible
  • En ligne de commande, on exécute curl_chrome116, et pour l’intégration en bibliothèque, on peut utiliser curl_easy_impersonate() ou, sous Linux, LD_PRELOAD et CURL_IMPERSONATE pour l’appliquer à des applications existantes utilisant libcurl

Le problème que résout curl-impersonate

  • curl-impersonate est un projet qui propose un build spécial de curl capable d’imiter les quatre grands navigateurs : Chrome, Edge, Safari et Firefox
  • Il peut effectuer les handshakes TLS et HTTP à l’identique de ceux des vrais navigateurs
  • Il s’utilise de deux façons
    • comme outil en ligne de commande comparable à curl classique
    • comme bibliothèque pouvant être intégrée à la place de libcurl

Pourquoi c’est nécessaire

  • Lorsqu’un client HTTP se connecte à un site TLS, il commence par effectuer un handshake TLS
  • Le premier message du handshake TLS est le Client Hello, et celui produit par la plupart des clients et bibliothèques HTTP diffère fortement de celui des vrais navigateurs
  • Si le serveur utilise HTTP/2, un handshake HTTP/2 a aussi lieu en plus du handshake TLS, avec échange de plusieurs paramètres
  • Les paramètres HTTP/2 de la plupart des clients et bibliothèques HTTP diffèrent eux aussi de ceux des vrais navigateurs
  • Certains services web exploitent ces différences pour identifier le client qui se connecte et fournir un contenu différent selon le client
    • Cette méthode est appelée TLS fingerprinting et HTTP/2 fingerprinting
    • Le README indique que l’usage généralisé de ces techniques a rendu le web moins ouvert, moins respectueux de la vie privée et plus restrictif envers certains clients web

Fonctionnement

  • curl est fortement patché pour ressembler à un navigateur
  • Principales modifications
    • La version Firefox compile curl avec NSS, la bibliothèque TLS utilisée par Firefox, au lieu d’OpenSSL
    • La version Chrome est compilée avec BoringSSL, la bibliothèque TLS de Google
    • La manière dont curl configure plusieurs extensions TLS et options SSL est modifiée
    • La prise en charge de nouvelles extensions TLS est ajoutée
    • Les paramètres utilisés pour les connexions HTTP/2 sont modifiés
    • curl est exécuté avec des drapeaux non par défaut comme --ciphers, --curves et certains en-têtes -H
  • Résultat : du point de vue réseau, curl apparaît exactement comme un vrai navigateur
  • La description technique complète se trouve dans part a et part b

Navigateurs pris en charge et noms de cible

  • La liste des navigateurs pris en charge est également disponible dans browsers.json
  • Cibles prises en charge de type Chrome
    • Chrome 99, 100, 101, 104, 107, 110, 116 sur Windows 10
    • Chrome 99 sur Android 12
    • Edge 99, 101 sur Windows 10
    • Safari 15.3 sur MacOS Big Sur
    • Safari 15.5 sur MacOS Monterey
  • Cibles prises en charge pour Firefox
    • Firefox 91 ESR, 95, 98, 100, 102, 109, 117 sur Windows 10
  • Chaque cible prise en charge dispose d’un nom de cible et d’un wrapper script
    • Exemple : la cible chrome116 s’exécute via le wrapper script curl_chrome116
    • Exemple : la cible ff117 s’exécute via le wrapper script curl_ff117

Utilisation de base

  • Pour chaque navigateur pris en charge, un wrapper script permet d’exécuter curl-impersonate avec les en-têtes et drapeaux nécessaires
  • Exemple d’exécution
    curl_chrome116 https://www.wikipedia.org
    
  • Les drapeaux supplémentaires en ligne de commande sont transmis à curl
  • Certains drapeaux peuvent modifier la signature TLS de curl et le rendre détectable
  • Le wrapper script utilise un jeu d’en-têtes HTTP par défaut
    • Pour modifier les en-têtes, il est possible d’adapter le wrapper script selon l’usage visé
  • D’autres options sont disponibles dans l’utilisation avancée de libcurl-impersonate comme bibliothèque

Installation et mode de distribution

  • Pour des raisons techniques, curl-impersonate existe en deux versions
    • version chrome : utilisée pour imiter Chrome, Edge et Safari
    • version firefox : utilisée pour imiter Firefox
  • Des binaires précompilés pour Linux et macOS Intel sont disponibles sur GitHub releases
  • Avant d’utiliser les binaires précompilés, il faut installer NSS et les certificats 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
  • Le système a aussi besoin de zlib
    • zlib est presque toujours présent, mais peut manquer sur certains systèmes minimaux
  • Les binaires Linux précompilés sont construits pour Ubuntu
    • Sur d’autres distributions, en cas d’erreur de validation des certificats, il peut être nécessaire d’indiquer à curl l’emplacement des certificats CA
    curl_chrome116 https://www.wikipedia.org --cacert /etc/ssl/certs/ca-bundle.crt
    
  • Pour compiler depuis les sources, voir INSTALL.md

Docker et paquets

  • Des images Docker basées sur Alpine Linux et Debian sont proposées sur Docker Hub
  • Les images Docker incluent les binaires et tous les wrapper scripts
  • Exemples
    # Version 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
    
    
    # Version 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
    
  • Les utilisateurs d’Archlinux peuvent utiliser des paquets AUR
  • Une formule Homebrew non officielle pour Mac est disponible uniquement pour Chrome
    brew tap shakacode/brew
    brew install curl-impersonate
    

Utilisation avancée de libcurl-impersonate

  • libcurl-impersonate.so est une version de libcurl compilée avec les mêmes modifications que curl-impersonate en ligne de commande
  • Une fonction API supplémentaire est fournie
    CURLcode curl_easy_impersonate(struct Curl_easy *data, const char * target,
                                   int default_headers);
    
  • En l’appelant avec un nom de cible comme chrome116, les options et en-têtes normalement définis par le wrapper script sont configurés en interne
  • Si default_headers vaut 0, la liste intégrée des en-têtes HTTP n’est pas configurée
    • L’utilisateur doit alors fournir lui-même les en-têtes via l’option CURLOPT_HTTPHEADER standard de libcurl
  • curl_easy_impersonate() configure plusieurs options libcurl
    • CURLOPT_HTTP_VERSION
    • CURLOPT_SSLVERSION, CURLOPT_SSL_CIPHER_LIST, CURLOPT_SSL_EC_CURVES, CURLOPT_SSL_ENABLE_NPN, CURLOPT_SSL_ENABLE_ALPN
    • CURLOPT_HTTPBASEHEADER, une option non standard propre au projet
    • CURLOPT_HTTP2_PSEUDO_HEADERS_ORDER, CURLOPT_HTTP2_NO_SERVER_PUSH, des options HTTP/2 non standard propres au projet
    • CURLOPT_SSL_ENABLE_ALPS, CURLOPT_SSL_SIG_HASH_ALGS, CURLOPT_SSL_CERT_COMPRESSION, CURLOPT_SSL_ENABLE_TICKET, des options TLS non standard propres au projet
    • CURLOPT_SSL_PERMUTE_EXTENSIONS, une option TLS non standard propre au projet
  • Si l’on appelle ensuite curl_easy_setopt() sur l’une de ces options, la valeur définie par curl_easy_impersonate() est écrasée

Application à des apps libcurl existantes

  • Si l’application utilise déjà libcurl, il est possible sous Linux de remplacer la bibliothèque existante à l’exécution via LD_PRELOAD
  • Le comportement d’usurpation peut être appliqué automatiquement en définissant la variable d’environnement CURL_IMPERSONATE
    LD_PRELOAD=/path/to/libcurl-impersonate.so CURL_IMPERSONATE=chrome116 my_app
    
  • CURL_IMPERSONATE a deux effets
    • curl_easy_impersonate() est appelée automatiquement à chaque création d’un nouveau handle curl via curl_easy_init()
    • curl_easy_impersonate() est aussi appelée automatiquement après curl_easy_reset()
  • Pour un contrôle fin des en-têtes HTTP, on peut désactiver la liste d’en-têtes intégrée avec CURL_IMPERSONATE_HEADERS=no et les définir soi-même
    LD_PRELOAD=/path/to/libcurl-impersonate.so CURL_IMPERSONATE=chrome116 CURL_IMPERSONATE_HEADERS=no my_app
    
  • La méthode LD_PRELOAD ne fonctionne pas avec curl lui-même
    • parce que l’outil curl écrase les paramètres TLS
    • pour curl, il faut utiliser les wrapper scripts

Structure du dépôt et contribution

  • Le dépôt contient deux dossiers principaux
    • chrome : scripts et patchs pour construire la version Chrome de curl-impersonate
    • firefox : scripts et patchs pour construire la version Firefox de curl-impersonate
  • Exemples de fichiers dans le répertoire Firefox
    • Dockerfile : utilisé pour construire curl-impersonate avec toutes les dépendances
    • curl_ff91esr, curl_ff95, curl_ff98 : wrapper scripts lançant l’outil avec les bons drapeaux
    • curl-impersonate.patch : patch principal qui fait utiliser à curl les mêmes extensions TLS que Firefox, avec compilation statique contre libnghttp2 et libnss
  • tests/signatures est une base de données YAML des signatures de navigateurs connus pouvant être imitées
  • Les patchs réels de curl sont maintenus dans un dépôt séparé forké depuis curl upstream

1 commentaires

 
GN⁺ 2025-04-04
Avis sur Hacker News
  • Il existe un fork activement maintenu avec pas mal d’améliorations par rapport à l’original : https://github.com/lexiforest/curl-impersonate
    Il y a aussi des bindings pour les utilisateurs de Python : https://github.com/lexiforest/curl_cffi

    • Qu’un programme qui « fait passer curl pour un navigateur » soit sponsorisé par un service de contournement de détection de bots paraît, d’une certaine façon, naturel
    • Il existe aussi un module qui l’intègre complètement à la bibliothèque requests de Python : https://github.com/el1s7/curl-adapter
    • C’est étrange de devoir suivre des technologies « avancées » qui changent plus vite que leur ombre juste pour émettre une simple requête ayant l’air de provenir de l’un des trois grands navigateurs web « certifiés »
      Paradoxalement, cette requête serait moins coûteuse pour le serveur qu’un navigateur certifié ; on se demande si c’est ce genre de dystopie dont on nous avertissait dans les années 90. Je me demande qui ici saurait nommer l’entreprise qui a créé cette situation tout en se présentant comme un bon contributeur de la communauté open source/hacker
  • J’espère que Ladybird prendra de l’ampleur à l’avenir. Pour l’instant, il utilise le cURL officiel pour le réseau, et cette approche pourrait poser des difficultés
    Par exemple, à ma connaissance, cURL a encore plusieurs limitations et ne sait pas gérer WebSocket au-dessus de h2. En revanche, si un moteur de navigateur en croissance apparaît, le trafic normal pourrait lui aussi avoir la même empreinte que cURL de base, ce qui pourrait faire disparaître cette voie de fingerprinting

    • J’espère que l’utilisation de cURL par Ladybird deviendra l’occasion d’améliorer cURL lui-même sur des points comme, par exemple, WebSocket au-dessus de h2 mentionné plus haut
      C’est aussi un bon banc d’essai pour voir quelles fonctionnalités manquent à cURL au regard des workflows réels d’un navigateur
    • Sur le point « s’il existe un moteur de navigateur en croissance, cette voie de fingerprinting pourrait disparaître », d’après ce que j’ai vu chez CloudFlare et autres, c’est presque l’inverse
      Ces derniers mois, le niveau de fingerprinting et d’« exploitation » des comportements définis par l’implémentation a énormément augmenté, probablement dans une tentative de tuer les autres moteurs de navigateur. Les acteurs établis n’aiment pas du tout la concurrence
      Le camp d’en face essaie de présenter cela comme un « DDoS de bots IA », mais je me demande dans quelle mesure ils ne sont pas eux-mêmes à l’origine du problème
    • Quand j’ai discuté avec ces personnes [0], nous avons évoqué plusieurs différences d’implémentation étranges utilisées pour construire des signatures, y compris des éléments de la pile TCP que les applications en espace utilisateur ne peuvent pas contrôler
      J’aime bien ce curl, mais je crains que, lorsqu’un composant est chargé de servir de trompe-l’œil pour « suivre le rythme », cela n’accumule une dette de « compatibilité » difficile à maintenir
      Idéalement, on devrait simplement pouvoir dire : « bonjour, je suis curl, laisse-moi entrer »
      Bien sûr, le problème vient des serveurs trop pointilleux sur le code vestimentaire, et ce problème existe aussi à cause des fraudeurs qui entrent déguisés ; c’est donc une boucle de type poule et œuf
      [0] https://cybershow.uk/episodes.php?id=39
    • J’espère que cela amènera Ladybird à prendre en charge les URL ftp
    • Ladybird n’a pas les ressources pour concurrencer les navigateurs actuels. Il a bénéficié d’une bonne communication, mais il manque d’avantages ou de raison d’être face à Chromium
      Et le fait qu’il ait été reconçu en C++, dont l’insécurité est démontrable, représente aussi un gros risque de sécurité
  • Est-ce qu’ils ont aussi défini IP_TTL pour faire correspondre la valeur TTL à la plateforme qu’ils essaient d’usurper ?
    Sinon, il est possible de faire un certain fingerprinting même au niveau IP. Si la valeur TTL au niveau IP est inférieure à 64, il est clair que cela ne tourne pas sur un Windows moderne, ou alors sur un Windows moderne dont le TTL par défaut a été modifié. En effet, le TTL par défaut des paquets sur Windows moderne commence à 128, tandis que la plupart des autres plateformes commencent à 64
    Comme les autres plateformes n’ont pas de problème pour communiquer sur Internet, les paquets IP provenant d’un Windows moderne apparaîtront toujours, côté distant, avec un TTL d’au moins 64, et probablement légèrement supérieur à 64. Cela dit, le fingerprinting au niveau IP est difficile, mais pas impossible

    • Ce n’est difficile que lorsqu’on utilise un PaaS/IaaS qui ne donne pas accès à bas niveau à la pile TCP/IP. Si vous exploitez vos propres serveurs, le fingerprinting de toutes sortes de propriétés TCP/IP est très simple
      https://en.wikipedia.org/wiki/TCP/IP_stack_fingerprinting
    • La valeur TTL des paquets reçus ne varie-t-elle pas selon l’état du réseau ? Le serveur peut-il reconstituer la valeur d’origine du client ?
    • Pourquoi le TTL a-t-il été conçu pour diminuer plutôt que pour augmenter ? En général, ne s’attend-on pas à ce que l’entité qui route le trafic décide s’il doit être routé et comment ?
  • Attendez, si le handshake TLS a une apparence différente, ne pourrait-on pas filtrer au niveau de nginx le trafic qui prétend être un navigateur web mais qui est en réalité un script Python/PHP ?
    Par exemple, le cas où il utilise un user-agent Chrome sans être un vrai navigateur. Comme la majorité du trafic de bots malveillants correspond probablement à ça, ce serait bien de pouvoir simplement le bloquer

    • Cloudflare utilise les empreintes TLS JA3/JA4, qui sont des hachages de plusieurs paramètres du handshake TLS
      Le fonctionnement est détaillé ici : https://github.com/FoxIO-LLC/ja4/blob/main/technical_details..., et un module Nginx est également fourni : https://github.com/FoxIO-LLC/ja4-nginx-module
    • En gros, c’est ce que font les entreprises de sécurité comme Cloudflare, en y ajoutant encore plus de fingerprinting, comme des challenges JavaScript qui vérifient l’interpréteur JavaScript et le DOM
    • C’est possible, et de vrais sites le font, mais c’est vraiment nul. Ce n’est pas fiable et ça finit par bloquer tous ceux qui n’utilisent pas la dernière version de Chrome sur un Windows récent
      Si vous n’êtes pas réellement en train de subir une attaque, il ne faut pas utiliser de liste d’autorisation d’empreintes TLS
    • L’outil de l’article semble précisément destiné à éviter ce genre de blocage
  • C’est un excellent outil, mais le fait qu’un client soit un navigateur ou non ne devrait pas avoir d’importance. C’est amer de constater qu’un tel outil est nécessaire dans la réalité

    • Il y a environ 6 mois, je suis allé sur un site d’enchères gouvernemental qui exigeait Internet Explorer. C’était vraiment Internet Explorer, et le site était encore actif, avec des données d’enchères à jour
      J’ai ajouté une extension de changement d’agent utilisateur à Chrome, je l’ai réglée sur IE et j’ai réessayé : ça a fonctionné, et toutes les fonctionnalités du site marchaient parfaitement. C’était donc à la fois amer et agaçant. L’agence gouvernementale en question a sans doute créé son site il y a 25 ans et ne l’a jamais mis à jour depuis
    • Vous ne pouvez accéder au site qu’en utilisant un logiciel que nous avons approuvé. Tout le reste est considéré comme malveillant. Vos papiers, s’il vous plaît !
      Ce genre de gatekeeping me laisse aussi un goût amer. D’après ce que j’ai compris, les navigateurs personnalisés faits maison ou les agents utilisateur personnalisés ne fonctionneront jamais sur les sites du type Cloudflare tant qu’ils n’auront pas assez d’influence, d’argent, d’utilisateurs, etc. pour les convaincre
  • Cet outil est assez efficace pour des opérations de red team lorsqu’on le combine avec de petits scripts bash et GNU parallel
    Il m’a été utile pour cartographier, dans des plages d’adresses définies, des endpoints HTTPS qui ne répondent correctement qu’à de vrais navigateurs, ou seulement si la configuration SNI est correcte. Les options curl classiques comme -H pour maquiller les en-têtes restent utilisables telles quelles

  • Le post Show HN de l’époque : https://news.ycombinator.com/item?id=30378562

    • À l’époque, en 2022, c’était réservé à Firefox
  • Chaque fois que ce genre de chose apparaît ici, j’ai toujours des sentiments ambivalents. D’un côté, il est bon de voir qu’il reste encore un peu d’esprit de rébellion et d’indépendance chez les gens
    De l’autre, comme pour d’autres projets où la « liberté » est traitée comme de l’instabilité, attirer une attention non désirée pourrait empirer la situation pour les personnes qui en dépendent. Écrire un navigateur est difficile, et les acteurs établis continuent de rendre la tâche encore plus difficile

    • On pourrait croire que les développeurs de navigateurs veulent que les navigateurs soient identifiables par empreinte, mais c’est plutôt quelque chose qui apparaît naturellement parce que des personnes différentes implémentent les choses différemment
      Je ne vois pas ça comme une question d’esprit de rébellion. Le fait qu’un logiciel puisse être identifié par empreinte nuit à la protection de la vie privée et à la diversité logicielle
  • Les temps plus simples où les sites web autorisaient les bots s’ils les acceptaient, et bloquaient l’agent utilisateur s’ils ne les aimaient pas, me manquent un peu

    • À l’époque, les sites web n’étaient pas aussi gourmands en ressources qu’aujourd’hui. Je vois la réaction négative envers les bots plutôt comme un effet secondaire d’attentes devenues excessivement lourdes vis-à-vis de l’expérience web
  • S’il ne s’agit que de 3 patchs et d’un wrapper shell, Daniel pourrait bien se mettre à coder. Personnellement, je pense que cela devrait absolument entrer dans le curl mainline