Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
claude-awm — Contourne les filigranes de texte des LLM en injectant des sélecteurs de variantes Unicode ; inclut le générateur SynthID, le détecteur mean-g, les défenses par normalisation et des expériences sur l'entropie. | Kitploit
Outils/GitHubGitHub/aloshdenny/claude-awm
StéganographieProtection de la Vie PrivéeApprentissage AutomatiqueSécurité de l'IAAttaque Adversariale
GitHubaloshdenny/claude-awm

claude-awm

Contourne les filigranes de texte des LLM en injectant des sélecteurs de variantes Unicode ; inclut le générateur SynthID, le détecteur mean-g, les défenses par normalisation et des expériences sur l'entropie.

Voir le dépôt
173il y a 3 joursPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

claude-awm : peut-on effacer un filigrane textuel SynthID en éditant le texte ?

Salut, ici Alosh ✌🏻

Oui, mais une seule famille d’attaques fonctionne, et ce n’est pas celle que tout le monde suppose.

Les sélecteurs de variante Unicode (catégorie Mn, U+FE00 à U+FE0F et U+E0100 à U+E01EF) font passer le détecteur sous le seuil et l’y maintiennent. Chaque autre attaque par caractères invisibles que j’ai essayée est entièrement annulée par une ligne de normalisation d’entrée. Les sélecteurs de variante, eux, ne le sont pas, car ce sont des points de code porteurs de sens (présentation des emoji, variantes CJK) que NFKC ne peut pas et ne doit pas réduire.

Reproduit sur trois modèles, deux domaines :

modeldomainbaseline zafter vs16_30edit rate
gpt-oss-20bprose45.030.7257%
gpt-oss-20bcode37.240.6858%
Qwen3.8-27Bprose35.50-0.6757%

Le seuil est z = 2.33. Les trois tombent en dessous et y restent après normalisation (0.09, 0.45 et -0.78 respectivement). Le texte s’affiche de manière identique pour un lecteur humain.

La seconde vraie découverte n’a besoin d’aucune attaque : le texte à faible entropie est à peine filigrané dès le départ. La génération de code de Qwen3.8-27B a une référence propre de z = 4.31, déjà proche du seuil sans rien y faire.

ce que c’est

Anthropic (et Google DeepMind avant eux, dans l’article SynthID-Text) filigrane les textes générés en biaisant l’échantillonnage des jetons avec un tournoi à clé. Le signal réside dans quels jetons ont été choisis, pas dans un caractère caché. Je voulais savoir à quel point cela résiste à une personne motivée armée d’un rechercher-remplacer, alors j’ai construit le générateur ainsi qu’un détecteur mean-g non entraîné et je lui ai lancé des attaques.

Le seuil du détecteur est z = 2.33 (taux de faux positifs de 1 %). Au-dessus, le filigrane est détecté. J’ai généré du texte filigrané, je l’ai attaqué, re-tokenisé puis scoré. Chaque table de résultats comporte un témoin roundtrip (texte filigrané non attaqué) afin qu’on puisse vérifier que le détecteur fonctionne bien dans cette ligne. Deux fois au cours de cette étude, un bug a fait s’effondrer tous les z à zéro, et le témoin est la seule chose qui ait permis de le repérer.

portée de l’attaque : ce qui fonctionne réellement

La surface d’attaque se divise nettement par catégorie Unicode, ce qui s’est avéré être toute l’histoire.

Fonctionne (catégorie Mn, marques sans chasse) :

Ne fonctionne pas (catégorie Cf, caractères de format) :

Les caractères Cf sont de la pure mise en forme, sans contenu sémantique ; un normaliseur peut donc les supprimer en bloc sans rien perdre. Les sélecteurs de variante Mn portent un sens réel dans les textes emoji et CJK ; les supprimer systématiquement corromprait des documents légitimes. C’est cette asymétrie qui fait qu’une famille est défendable et l’autre non.

La portée pratique : il faut toucher environ 20 % ou plus des caractères pour faire passer une référence solide sous le seuil, mais les caractères insérés sont invisibles, donc le texte se lit normalement quel que soit le taux d’édition. L’identité visuelle est vérifiée, voir src/fidelity_check.py. Quatre familles n’insèrent que des caractères qui ne rendent rien ; les homoglyphes sont l’exception honnête (dépendants de la police, invisibilité non garantie).

ce qui ne fonctionne pas : les évidences

Ma crainte initiale était la version ennuyeuse : que les tirets cadratins, le remplacement de « and » par « & » et le retrait du markdown fonctionnent tout simplement, et que le filigrane se révèle être du théâtre de sécurité. Beaucoup de gens en ligne supposent exactement cela.

Ça ne fonctionne pas. Toute l’échelle d’éditions de surface sur gpt-oss-20b, jusqu’à 32 000 jetons :

Sur trois modèles (0.8B, 4B, 20B = 294 cellules), exactement une cellule a franchi le seuil : la suppression de 39 % de chaque mot à la longueur la plus courte sur le 4B. Ce n’est pas une attaque, c’est déchiqueter le document.

Deux choses m’ont surpris :

  • Le nombre d’éditions ne prédit pas les dégâts, c’est la géométrie des éditions qui le fait. Supprimer tout le markdown (13.6 % des jetons) n’a rien fait ; c’est même légèrement au-dessus de la référence. L’injection d’espaces éparses à 1.6 % a fait 25 fois plus de dégâts par édition. Les marqueurs markdown sont regroupés, donc leurs fenêtres de corruption se chevauchent et les longues portions de prose entre eux continuent de rejouer la graine du filigrane intacte. Les éditions dispersées qui désynchronisent le tokenizer touchent à chaque fois de nouvelles fenêtres.
  • La longueur aide le détecteur, pas l’attaquant. z croît comme sqrt(jetons). « Le tromper sur un long contexte » est à l’envers : 32k est le cas le plus difficile à attaquer, pas le plus facile.

Mécanisme complet et tables par attaque dans docs/FINDINGS.md.

Essayez la version interactive → De vrais échantillons de l’étude avec un bouton avant/après, plus un terrain de jeu pour appliquer la transformation d’attaque sur votre propre texte. Il ne vous dira pas si un texte collé arbitraire est réellement filigrané (cela nécessite une clé que nous n’avons pas), et il le précise ; voir site/ pour le script du générateur.

la découverte qui n’a besoin d’aucune attaque

Le filigrane repose sur l’incertitude par jeton du modèle. Là où le modèle est confiant quant au prochain jeton, le tournoi n’a aucune marge pour le biaiser, donc aucun signal n’est injecté. Cela signifie que la marque est faible sur les textes à faible entropie, et le code est à faible entropie.

Qwen3.5-4B, prose vs code, échantillons de 512 jetons, aucune attaque :

domainentropyz
prose1.19 bits/tok11.1
code0.55 bits/tok5.0

Rapport de z 0.45x, rapport d’entropie 0.46x, ils évoluent ensemble, ce qui est le mécanisme qui transparaît. 3 des 8 échantillons de code sont tombés sous le seuil de détection, ou à ce seuil, d’eux-mêmes. Le plus serré (un algorithme nu, 0.2 bit/jeton) a obtenu 1.7, un échec.

Cela devient plus extrême à l’échelle. z de référence sans aucune attaque :

modelprosecoderatio
gpt-oss-20b45.0337.240.83
Qwen3.8-27B35.504.310.12

La sortie de code de Qwen3.8-27B est si stéréotypée que le filigrane propre, non attaqué, se situe à z = 4.31, tout juste au-dessus du seuil de 2.33. Aucun adversaire requis.

Cela nous dit : un seuil de confiance unique pour tous les domaines n’est pas sûr, et les courts extraits de code sont presque impossibles à filigraner. Cela se généralise au JSON, à la configuration, à l’extraction structurée, au code passe-partout.

enseignement défensif

Si vous déployez ce genre de détecteur, normaliser l’entrée vous mène l’essentiel du chemin, mais pas tout :

  1. Supprimez les caractères de catégorie Cf. Élimine les caractères de largeur nulle, les bidi et les combinaisons. C’est la grande victoire.
  2. Normalisation NFKC. Traite les espaces insécables et les formes de compatibilité.
  3. Supprimez explicitement les plages de sélecteurs de variante. NFKC ne le fera pas à votre place, et c’est la faille actuellement ouverte.
  4. Conservez une carte de confusables Unicode (UTS-39) pour les homoglyphes. NFKC ne le fera pas non plus.

Les étapes 3 et 4 sont celles qu’un normaliseur naïf omet.

les limites / ce que je n’ai pas pu faire

Soyons honnêtes sur les lacunes.

  • Les chiffres code du 27B ne sont pas informatifs en tant que résultat d’attaque. La référence non attaquée y est de z = 4.31, on ne peut donc pas démontrer une attaque battant un détecteur déjà presque aveugle. J’ai conservé ces lignes mais je les ai étiquetées ; le signal pertinent est la référence, pas les deltas d’attaque.
  • La question de savoir si le résultat code du 27B est dû à l’entropie ou au style du modèle reste ouverte. Il faudrait une mesure d’entropie par jeton comme celle dont a bénéficié le 4B, que je n’ai pas exécutée pour ce modèle.
  • GLM-5.2 n’a produit aucune donnée. J’ai loué un pod 8xA100 et enchaîné cinq pannes d’infrastructure (commande de téléchargement dépréciée, ruptures ABI torch/torchvision, chargement du modèle dans la RAM hôte au lieu des GPU), brûlé environ 25 $ dont 19 $ sur un pod resté inactif parce que j’ai fait confiance à un téléchargement qui n’a jamais démarré, et je l’ai résilié sans rien. Kimi-K3 n’a jamais été tenté ; à ~1.5 To, même quantisé, il faut 20+ A100. La question à l’échelle de la frontière reste ouverte.
  • Trois checkpoints quantisés communautaires de Qwen3.8-27B n’ont pas pu être chargés (FP8 exigeant un dtype torch que nous n’avons pas, deux repackages AWQ/compressed-tensors avec des incompatibilités d’empaquetage). Je l’ai donc exécuté en bf16 sur un H100. Si vous reproduisez, ignorez les repackages.
  • Le détecteur est le scoreur mean-g non entraîné, pas le détecteur bayésien entraîné de l’article. Le détecteur bayésien serait probablement plus sensible, donc ces valeurs de z sont un plancher, mais je ne l’ai pas mesuré.
  • Ma revendication de fidélité des homoglyphes est « lecteur typique », pas prouvée. Le а cyrillique est de catégorie Ll, donc son invisibilité est une propriété de police, pas une garantie Unicode.
  • n est petit, 2 documents par cellule pour les échelles, 8 échantillons par domaine pour l’entropie. Suffisant pour les tailles d’effet présentes (elles sont grandes), pas suffisant pour des barres d’erreur serrées par attaque.
  • Je ne fournis pas de recette d’évasion ajustée, et c’est délibéré. Chaque attaque est rapportée ici avec le résultat de normalisation qui la neutralise ou non. Le but était de mesurer où se situe la frontière, pas d’empaqueter un contournement.

structure des fichiers

root@kitploit:~
src/synthid_robustness.py   generator + attack ladder + mean-g detector + normalizer
src/code_vs_prose.py        the entropy experiment (with per-token entropy tap)
src/fidelity_check.py       proves the stego attacks are visually identical
src/synthid_mlx.py          watermarking bridge for Apple Silicon (MLX), validated vs HF
src/prompts_code.py         prose / code / mixed prompt sets
src/build_report_data.py    assembles results/ into the tables in FINDINGS.md
results/                    the JSON this is all computed from
docs/FINDINGS.md            every table, the defense hierarchy, the bugs I caught

comment l’exécuter

root@kitploit:~
python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
root@kitploit:~
# generate watermarked docs + run the full attack ladder on a model
MODEL=Qwen/Qwen3.5-4B LENGTHS=1024,2048,4096,8192 N_DOCS=2 \
  DOCS=docs_4b.json OUT=res_4b.json python src/synthid_robustness.py

# the entropy experiment (code vs prose)
python src/code_vs_prose.py --model Qwen/Qwen3.5-4B --out res_cvp_4b.json

# the variation-selector / stego attacks, scored raw AND post-normalization
ATTACK_SET=desync DEFENSE=1 PROMPT_SET=prose MODEL=Qwen/Qwen3.5-4B \
  DOCS=docs_4b.json OUT=res_defense.json python src/synthid_robustness.py

PROMPT_SET accepte prose, code ou mixed. FAST_WM=1 utilise un pont de filigrane numpy (plus rapide sur les modèles à petit vocabulaire avec un CPU puissant), FAST_WM=0 utilise le processeur GPU de HF (beaucoup plus rapide sur les modèles à grand vocabulaire ; sur un H100, c’est la différence entre 0 % et 46 % d’utilisation GPU).

Le filigranage a besoin de la distribution complète du prochain jeton, il passe donc par transformers (MXFP4 natif CUDA, ou MPS/CPU). Ollama et llama.cpp ne peuvent pas le faire, ils n’exposent pas les logits en cours de génération. Sur Apple Silicon, src/synthid_mlx.py fait le pont entre la génération MLX et les calculs du filigrane ; il est validé comme étant bit-identique à la référence HF.


le rêve, paraît-il

(le meme qui a tout déclenché. Il s’avère qu’il faut des sélecteurs de variante, pas un rechercher-remplacer.)


Note : j’ai beaucoup utilisé Claude Code pour l’implémentation et pour relancer les expériences sur quatre machines (un Mac, ma propre 4090, une 3090 louée et un H100). La conception des expériences, les attaques que je voulais tester et les choix de cadrage sont les miens. Claude a insisté pour mesurer chaque attaque contre sa propre défense, c’est pourquoi les tables stégo ont une colonne brute et une colonne normalisée au lieu d’une seule colonne brute ; c’est ce qui a transformé « les caractères invisibles la brisent » en la véritable découverte, à savoir que seuls ceux de la catégorie Mn survivent à un normaliseur.

Télécharger l’outil
attackwhat it doesedit ratesurvives normalization?
vs16_30sélecteur de variante après ~30 % des caractères57%oui
vs16sélecteur de variante après ~10 % des caractères23%oui (z 3.46)
vs_suppsélecteurs du plan supplémentaire (U+E0100+)24%oui (z 3.40)
homoglyphа cyrillique pour a latin (catégorie Ll)9%oui, mais effet faible
attackraw znormalized zverdict
zwsp_30-0.0935.68entièrement annulé
combo0.9235.68entièrement annulé
bidi24.3735.68entièrement annulé
nbsp41.0446.51le fait à peine bouger
attackedit ratez @ 1kz @ 32k
roundtrip (témoin)0%25.7104.3
tiret cadratin en trait d’union~0%26.4113.7
supprimer tout le markdown13.6%27.2103.3
AmE → BrE + abréviations1.3%~28~100
supprimer 40 % de chaque mot38%4.925.4