lundi 8 juillet 2013

SIGINT CTF 2013: RSA

Quelques explications sur le problème RSA du SIGINT CTF 2013:

Les deux fichiers suivants sont donnés:

genrsa.py
#!/usr/bin/env python

from time import time
from os import system
from Crypto.PublicKey import RSA


SEED = int(time())


def randfunc(n):
    def rand():
        global SEED
        ret = SEED*0x1333370023004200babe004141414100e9a1192355de965ab8cc1239cf015a4e35 + 1
        SEED = ret
        return (ret >> 0x10) & 0x7fff
    ret = ""
    while len(ret) < n:
        ret += chr(rand() & 0xff)
    return ret

if __name__ == "__main__":

    keypair = RSA.generate(1024, randfunc)


    with open("pub", "w") as pubfile, open("id_rsa", "w") as privfile:
        privfile.write(keypair.exportKey())
        pubfile.write(keypair.publickey().exportKey())

    system("ssh-keygen -m PKCS8 -i -f pub > id_rsa.pub && rm pub")

Et le fichier authorized_keys

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC+K6w1yodieqyryJnUYHw/ZuycabT0Ehwg4XFqZYfh/euE4QIXPJ23widXJUKIq8Gqwi5M/Pa+7/gAPeVcrcF65pUkeIYeZBXoAeDj0EqpFxiHdSB/K1Ovt/lIFmBG3hy+MVJLYfz6lBRxQwj+CJRkFX2Xf/5JyZWSK5UwXOlh0w==

On a donc la partie publique d'une clef RSA générée avec un RNG faible.

La première observation est que ce RNG fait grandir SEED de manière arbitrairement grande; ce RNG est donc très, très lent. Cependant, on observe qu'en sortie, seuls les bits 16-24 de SEED sont utilisés (on à SEED >> 0x10 & 0xff). Autrement dit, ce générateur non borné est congru a un autre modulo 2^24. Ou, pour voir plus simplement les choses: après multiplication par la grande constante, un bit changé dans la seed en entrée ne peut affecter que des bits de poids plus fort. on peut donc joyeusement ignorer les bits au dela du 24e.

.

On peut donc remplacer la grande constante 0x1333...5a4e35, par sa réduction modulo 2^25, soit 0x15a4e35. On peut également réduire SEED à chaque iteration, avec SEED &= 0x1ffffff. Ceci a pour effet d'accélérer considérablement le RNG.

Pour le bruteforce, l'espace des seeds est quand même assez grand, nous allons donc essayer de la deviner. Le header de la réponse HTTP du fichier authorized_keys nous indique:

Last-Modified: Fri, 05 Jul 2013 17:50:05 GMT

La génération de la clef est donc antérieure à cette date. On va commencer à bruteforcer en partant de cette valeur, et en descendant.

Pour ne pas perdre de temps, on lit le code de PyCrypto concernant la génération de clefs RSA, et on s'aperçoit que ce code appelle pubkey.getStrongPrime une première fois, avec la moitié de la taille souhaitée, puis autant de fois que nécessaire pour obtenir le deuxième nombre. Pour gagner un peu de temps, plutôt que de chercher a générer la clef exacte, nous allons seulement générer le premier nombre premier p, et tester si ce nombre divise le n connu; si c'est le cas, le résultat de la division nous donnera le deuxième nombre q.

On commence par extraire le e et le n de la clef ssh, avec

ssh-keygen -e -f authorized_keys -m PKCS8 |openssl rsa -pubin -text

Ceci nous révèle l'exposant 65537 (comme prévu suivant le code de génération) et le module 0xBE2BAC35CA87627AACABC899D4607C3F66EC9C69B4F4121C20E1716A6587E1FDEB84E102173C9DB7C22757254288ABC1AAC22E4CFCF6BEEFF8003DE55CADC17AE6952478861E6415E801E0E3D04AA917188775207F2B53AFB7F948166046DE1CBE31524B61FCFA9414714308FE089464157D977FFE49C995922B95305CE961D3

.

On lance le bruteforce avec le script d'attaque suivant:

#!/usr/bin/env python

from time import time
from os import system
from Crypto.PublicKey import pubkey

target = 0xBE2BAC35CA87627AACABC899D4607C3F66EC9C69B4F4121C20E1716A6587E1FDEB84E102173C9DB7C22757254288ABC1AAC22E4CFCF6BEEFF8003DE55CADC17AE6952478861E6415E801E0E3D04AA917188775207F2B53AFB7F948166046DE1CBE31524B61FCFA9414714308FE089464157D977FFE49C995922B95305CE961D3

SEED2 = 0

def randfunc2(n):
    def rand():
        global SEED2
        ret = SEED2 * 0x15a4e35 + 1
        SEED2 = ret & 0xffffffff
        return (ret >> 0x10) & 0x7fff
    ret = ""
    while len(ret) < n:
        ret += chr(rand() & 0xff)
    return ret.encode('latin1')


if __name__ == "__main__":
    x = 1373046605
    while True:
        SEED2 = x
        x -= 1
        prime = pubkey.getStrongPrime(512, 65537, 1e-12, randfunc2)
        if target % prime == 0:
                print("BROKEN!!! p={0}".format(prime));
                exit(0);
        if x % 10 == 0:

En quelque minutes, on obtient les valeurs p = 13097286606179453667665592444299109782484218865253457545521978739889248320232481682880143106432871469494586765663594908396375009598486558938138835723794021 et q = 10196183246368760603869192593971202143897281417220455881063414616103901438182656326076501376638806928762094749150020638960102206987607293047096627515275223

On utilise Crypto.PublicKey.RSA.construct() pour construire la clef, on l'exporte dans un fichier, elle est utilisable directement par openssh.

jeudi 2 mai 2013

Un élusif problème de DNS

J'ai compris la cause récente du problème récurrent et aléatoire de DNSSEC sur ma zone principale. Comme la plupart du temps, c'est une erreur très bête.

Le programme dnssec-signzone prend en entrée une zone DNS non-signée et produit un fichier de zone augmenté d'enregistrements contenant les signatures. C'est le fichier signé qui est ensuite chargé dans le serveur de noms.

dnssec-signzone dispose d'une option, -N, pour contrôler le numéro de séquence du SOA de la zone. Les valeurs possibles sont "keep" pour conserver le SOA sans modification, "unixtime" pour remplacer le n° de séquence par le temps unix, et "increment" pour incrémenter le numéro de série de la zone.

En effet, lorsqu'on active la signature d'une zone, il est nécessaire de pousser la nouvelle version de la zone, contenant les signatures, et donc d'incrémenter le n° de série.

Ayant auparavant utilisé le format classique de n° de série (année-mois-jour + deux chiffres de série), le timestamp unix ne me convenait pas, j'ai donc utilisé le mode "increment".

Les signatures étant limitées dans le temps, j'ai mis un cron pour resigner périodiquement ma zone.

Lo and Behold, dnssec-signzone en mode incrément, ne contrôle pas le numéro de séquence de l'ancienne zone signée, seulement celle de la zone source. Et comme j'ai deux fichiers séparés, seule la première signature après une mise a jour de la zone disposait d'une signature fraîche. Une fois le premier round de signatures expirées, les suivantes n'étaient jamais envoyées vers les serveurs esclaves, causant des problèmes aléatoires de signatures invalides. DUH.

Moralité: utiliser le mode Unix Timestamp, ou incrémenter le n° de séquence a la main.

jeudi 6 septembre 2012

Le Charpentier et le Tonneau

Il était une fois un beau petit village, peuplé de bien braves gens, dans un magnifique pays. Ce village devait sa prospérité à sa rivière, qui irriguait les champs, charriait du poisson à profusion, et guérissait des maladies grâce à ses merveilleux sels minéraux.

Les gens habitant la rive étaient très satisfaits de leur vie tranquille, mais ceux qui habitaient les collines voisines un peu moins, car ils n'avaient pas d'eau à disposition. Personne ne souhaite aller chercher de l'eau à pied, c'est trop loin. Le chef du village décide donc qu'il en est assez, et que les habitants des collines, eux aussi, doivent pouvoir profiter de l'eau.

Or, un voyageur de passage, lors de son bref séjour, a conté au chef les vertus du tonneau, une invention formidable d'une contrée lointaine qui permet de transporter des liquides à dos de bétail. Le chef y voit là une aubaine pour son épineux problème de transport d'eau.

Personne au village n'est tonnelier. Enfin si, il y a Jean-Paul qui habite derrière le moulin, et qui passe sa journée à lire des livres (ce sale paresseux). Il possède bien un livre sur les tonneaux, mais personne ne pense à le lui demander, parce que ma foi, Jean-Paul, il est bizarre.

Le Chef convoque donc les villageois et leur expose son projet. Parmi les villageois, il y a Roger le Charpentier. Puisque les tonneaux sont, d'après les dires du voyageur, faits de bois, Roger explique que ce travail lui est naturellement dévolu. Le Chef agrée, lui remets quelques croquis soutirés au voyageur, et donne carte blanche à Roger pour la réalisation d'un tonneau.

Roger assemble donc ses collaborateurs, et ensemble ils sélectionnent des planches dans leur réserve et les assemblent de leur mieux en un objet qui ressemble à un tonneau.

Jean-Paul, qui passe par là, radote sur ce qu'il a lu dans les livres, à propos de la fabrication des tonneaux, de l'importance de choisir le bon bois, de couper dans le bon sens, et de cuire le bois pour assurer l'étanchéité du tonneau. Tout le monde rigole: il est fou, ce Jean-Paul ! comme si fabriquer un tonneau pouvait être si compliqué. Et s'il est aussi compétent en tonneaux, il n'a qu'à en faire un lui-même, d'abord.

Le travail de Roger terminé, le Chef apporte une demi-amphore de vin et la verse dans le tonneau. Le vin coule par les fentes du bois au fond de la barrique.

Roger se rue vers le tonneau en criant "mais c'est rien, c'est rien du tout", et bouche le plus gros trou du fond du tonneau avec son chewing-gum. Puis il y verse une deuxième amphore. Le vin coule par un deuxième trou. Roger va chercher un autre paquet de chewing-gum.

Au bout de dix semaines de test avec une amphore, Roger assure au Chef que tous les trous du fond sont bouchés. Le Chef convoque le village pour une démonstration. On immerge le tonneau dans la rivière pour y prélever une bonne quantité d'eau. L'eau jaillit par une centaine de trous sur les cotés.

Certains parmi les villageois sont contrariés de l'échec du tonneau après tant d'efforts. Roger est un bon charpentier, mais ils préfèreraient voir le travail confié a un tonnelier. Mais contre toute attente, le Chef refuse hostilement l'ingérence de quiconque dans le travail de Roger: en effet, le Chef voit grand, et il est persuadé que le projet réussi, les villages voisins s'arracheront la précieuse technologie du tonneau. Il est donc hors de question de laisser quiconque entrevoir le processus de fabrication. Roger est donc chargé de boucher un a un les trous du tonneau avec du chewing-gum.

Le tonneau achevé, une grande cérémonie de lancement est organisée, le précieux tonneau est rempli de la merveilleuse eau, chargé en grande pompe sur un âne, et le cortège part en tournée. Il passera par les collines, puis par toutes les maisons du village (parce que les riverains aussi devraient pouvoir en profiter, du tonneau !). De liesse, certains villageois jettent leurs seaux par les fenêtres. Vive le tonneau, si pratique !

Lorsque le chargement arrive aux collines, le tonneau est vide. Les trous visibles avaient pourtant été bouchés, mais las, l'ensemble n'en était pas étanche pour autant.

Les villageois des collines, et les riverains imprudents, meurent de soif devant leur tonneau vide.

Fin.

mercredi 5 septembre 2012

TLDs compatibles IPV6

Suite a un message aperçu sur Twitter, un petit script de survey sur la compatibilité IPv6 des NS des TLD (annoncée, pas testée):

#!/bin/bash

for tld in `curl http://data.iana.org/TLD/tlds-alpha-by-domain.txt |grep -v '#'`
do
    echo -n "$tld"
    YES=0
    NO=0
    TOTAL=0
    for dns in `dig +short -t ns $tld`
    do
        let TOTAL++
        if
            dig -t aaaa $dns 2>&1 |grep "ANSWER: 0" >/dev/null
        then 
            let NO++
        else 
            let YES++
        fi
    done
    echo " $YES $NO $TOTAL"
done

Quelques résultats:

Nombre de TLD: 315
TLD sans aucun NS avec AAAA: 47
TLD avec AAAA pour tous les NS: 55
TLD avec AAAA pour certains NS: 213


mercredi 11 juillet 2012

Merci la poste

Sans oublier que le facteur a encore eu la flemme de monter 3 étages a pied, et a préféré se dire que personne n'etait la et laisser un avis de passage.


dimanche 8 avril 2012

Passage d'une zone en DNSSEC avec BIND et Gentoo

DNSSEC c'est le bien. Il a le potentiel de protéger vos communications bien mieux que le fiasco anarchique et surévalué des autorités de certification actuelles.

Or jusqu'à il y a peu de temps, Gandi offre enfin la possibilité d'annoncer ses clefs en amont, et d'avoir ainsi "the real thing". Ce n'est pas trop compliqué a faire.

D'abord, vérifier que net-dns/bind et net-dns/bind-tools sont a jour. (9.7 était problématique chez moi)

Créer un répertoire pour les clefs, puis générer deux clefs pour chaque zone (une ZSK et une KSK):

mkdir /etc/bind/keys
dnssec-keygen -K /etc/bind/keys -a RSASHA256 -b 2048 -f KSK xolus.net
dnssec-keygen -K /etc/bind/keys -a RSASHA256 -b 2048 xolus.net

On se retrouve avec quatre nouveaux fichiers, les parties privées (.private) et publiques (.key) de la ZSK et de la KSK. Les fichiers .key sont au format standard RR de BIND. La KSK est celle ayant un enregistrement de type DNSKEY 257.

On ajoute les deux clefs à la zone:

cat /etc/bind/keys/Kxolus.net*.key >>/etc/bind/pri/xolus.net.zone

Par la suite, et a chaque modification de la zone, il faudra la resigner avec

cd /etc/bind
dnssec-signzone -K keys -o xolus.net pri/xolus.net.zone

Ceci crée un nouveau fichier de zone, xolus.net.zone.signed, qui inclut une signature pour chaque record de la zone. Reste a éditer /etc/bind/named.conf pour utiliser le fichier ".signed" à la place. On recharge ensuite BIND avec
rndc reload

puis on valide la signature d'une entrée au hasard, avec dig:

dig +sigchase +trusted-key=<(grep -v ';' /etc/bind/keys/Kxolus.net.+008+28711.key) -t aaaa xolus.net
Ici on a utilisé la KSK comme racine de confiance. dig est pénible et s'étrangle sur les commentaires placés par défaut dans les fichiers par dnssec-keygen. Si cela fonctionne, vous pouvez maintenant envoyer votre KSK à votre registrar qui se chargera d'ajouterles DS correspondants à la zone. Pour la vraie vérification, commençons par récupérer la clef racine:
dig -t DNSKEY . |grep " 257 " >>/etc/trusted-key.key
Cette récupération est insécure, mais vous pouvez vérifier que la clef correspond à
.                       IN      DNSKEY  257 3 8 AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjF FVQUTf6v58fLjwBd0YI0Ez
rAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoX bfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaD X6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpz W5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relS Qageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulq QxA+Uk1ihz0=
Au demeurant, elle devrait déja être présente en dur dans /etc/bind/bind.keys, alors vérifiez que ça correspond. Une fois la clef racine dans /etc/trusted-key.key, un
dig +sigchase -t AAAA xolus.net
Devrait réussir si la validation en amont a fonctionné. La zone est maintenant DNSSEC-enabled. Reste maintenant a activer DNSSEC du côté du resolver local. Avec BIND on ajoute simplement
dnssec-validation auto;

dans named.conf.

Reste encore a couvrir les zones dynamiques, et le mécanisme d'auto-signature intégré a BIND.

jeudi 15 mars 2012

shatag v0.3

Après une longue session de codage intensif, j'ai le plaisir d'annoncer la sortie de la v0.3 de shatag (https://bitbucket.org/maugier/shatag.) Les nouvelles fonctionnalités sont:
  • un démon utilisant inotify pour recalculer les tags automatiquement
  • un client et un serveur HTTP avec un protocole restful léger
  • des backends modulables, ce qui permet le fonctionnement (inefficace) sur les OS sans extended attributes.

(Pour rappel, c'est un logiciel qui permet de garder trace des hashes sha256 des fichiers présents sur un filesystem, et de les comparer.)
 
Also check me out on Mastodon