Hervé Godquin

Aller au contenu | Aller au menu | Aller à la recherche

mardi 31 mars 2020

Threat landspace & operationnal cybersecurity during Covid-19 pandemic

Hello everyone !

Some of you know that next week I will quit the SoC & Incident Response team world for another adventure (still in cybersecurity). But I want to make a last blog post about best practice (for me) for blue (and purple) teams during this pandemic time !

First at all : the threat landspace

It is not a surprise many attackers use Covid-19 like a propagation vector for Phishing and malwares attacks. We can see too some "new" physicals attacks like send infect USB Key via UPS (is not link to Covid but we can imagine that some group will send infect UPS key to critical employees).

Of course some remote conferencing tools like zoom are used by attackers.

And unfortunately some attackers still use ransomware against hospitals

For active APT groups, we have APT41 and FIN7 for example.

For TTPs I suggest you to read this matrice about O365, this one about Azure and this one about SaaS application.

Companies transformations

Some companies weren't ready too full home working, so some uncrontrolled changes have been made. And we all know that not controlled transformations are egal to security vulnerabilites ;).

When you know that, in France for example, in many compagnies home working was only 1 day /week and just for some employees, and in some SMBs they had no homeworking at all, now imagine when the majority of your staff/employees need to homework, providers too and that all working days.

You can easily imagine that some homeworking services were created at last time and are not compliance with "the state of art" !

Blue/Purple Team/SoC what to do ?

For me main steps are :

  • Please do not stop vulnerability management : we still have new vulns ! Patching must continue. Please ensure to patch your VPN controllers, exposed servers and user's end point !
  • Check for anormally long SSH connections : it can be a reverse SSH tunnel (not necessary from an attackers, it can be from legit users for home working problems). You can do the same for VPN connection !
  • Check for remote control tools like teamviewer : you can use DNS logs, or some TI feeds can help, I know that emerging threat give list of GotoMyPc IPs (which include teamviewer, logmein, ...)
  • If you have AI/ML network security sensor like Vectra or Darktrace, please ensure that they still can capture the user trafic ! Your users are not even in your private network but they are thoughts VPN, please ensure that this network trafic is capture and send to your sensor.
  • If you have an on premise AV/EDR controller please ensure your users endpoints can contact it, even if the VPN is not mount !
  • Scan your public IPs to find new open port (like RDP for example :)).
  • Review firewall rules.
  • Review AD/LDAP sensitive groups.
  • Ensure that your SIEM manage correctly logs from cloud services (like O365/Azure) and check for abnormal behaviour !
  • Please ensure that all critical access have MFA/OTP.
  • Try to make security awarness to users about phishing and malwares.
  • In your DNS logs check for domains with key words zoom and covid, they seems to be hardly use to serve malwares :)
  • Prepare yourself, try to stay cool and have fun (as always but from home) !

And maybe is the good time for a red team exercice (or not :)).

Some Indicators of Compromise

Here some IoC relative to Covid-19 :

  • Fake AV :
    • antivirus-covid19[.]site
    • antivirus-covid19[.]site/update.exe
    • Hash : 146dd15ab549f6a0691c3a728602ce283825b361aa825521252c94e4a8bd94b4
  • Some phshing examples :
    • chase-covid19s[.]com
    • rogers-covid19[.]com
    • www.airbnb.id-covid19[.]com

And you can find many IoC link to covid-19 campaigns, if you want somes contact me, but if you work in a SoC/CERT you will have them.

Stay safe

That's all for me. Please stay safe online and IRL !

If you want to contact me about this subject :

dimanche 29 mars 2020

Introduction au chiffrement : partie 2

J'ai mis en ligne la deuxième vidéo concernant l'introduction au chiffrement. Ce coup-ci on parle de chiffrement symétrique !

Have fun !

lundi 23 mars 2020

Introduction au chiffrement : Bases mathématiques, Chiffre de Verman et RSA

J'ai fait un petit live stream sur les bases de la crypto : Disponible ici : https://www.youtube.com/watch?v=NKzeOAr44Xg

Un article arrive ici même avec plus d'informations.

mardi 25 février 2020

CERTFR-2020-AVI-107 : vulnérabilités PHP

Bonjour à toutes et à tous,

Certains ont peut-être vu cette alerte : CERTFR-2020-AVI-107.

En soit sur PHP ce genre de message : "De multiples vulnérabilités ont été découvertes dans PHP. Elles permettent à un attaquant de provoquer un problème de sécurité non spécifié par l'éditeur." me surprant toujours. En regardant rapidement, mais alors très rapidement, on peut voir que :

Concernant la CVE-2020-7063 : il s'agit d'une vulnérabilité dans Phar : (en gros c'est pour faire une archive d'une application PHP dans un fichier unique) qui semble-il ne garde pas les permissions lorsque l'on créé un tar. C'est assez emmerdant, mais cela reste un cas particulier. Source ici.

La CVE-2020-7061 : un heap-overflow sur Phar sur ... Windows ... Sérieusement qui fait ça ? genre du WAMP quoi ? Bon bref.

La CVE-2020-7062 : Null Pointer Dereference si dans votre php.ini il y a session.upload_progress.cleanup=0 ... source : ici.

Donc en gros : quqleus risques de crash, voir DoS donc, on ne peut pas exclure du RCE dans certains cas mais bon ... les droits concernant les archives ça peut être emmerdant. Enfin quoi qu'il en soit il faut patcher de toute façon :D.

Et sérieusement qui fait du PHP sous Windows ????

Du coup si je devais faire un pari vite fait je dirais :

CVE-2020-7061 : CVSS 3.1 score : 6.4 (enfin je mettrais 10 moi si vous mettez du PHP sur Windows sérieusement !!!)

CVE-2020-7062 : 7.5

CVE-2020-7063 : 6.6 aussi.

Voilà un article pas sérieux, écrit en 10 minutes sur un coin de table, mais juste pour dire que si on veut des informations sur des vulnérabilités PHP on peut les trouver facilement (bon après je sais que le CERT-FR a autre chose à faire ;)).

 

Have fun !

samedi 22 février 2020

SSH et SHA1

Avez-vous vu la dernière release note d'OpenSSH ? Si ce n'est pas le cas je vous conseille de la lire. Et après l'avoir lu si vous n'êtes toujours pas passé aux clés basées sur des courbes elliptiques je vous conseille de le faire vite. Car, je spoile un peu sur votre prochaine lecture :

It is now possible[1] to perform chosen-prefix attacks against the SHA-1 hash algorithm for less than USD$50K. For this reason, we will be disabling the "ssh-rsa" public key signature algorithm that depends on SHA-1 by default in a near-future release. This algorithm is unfortunately still used widely despite the existence of better alternatives, being the only remaining public key signature algorithm specified by the original SSH RFCs. The better alternatives include: * The RFC8332 RSA SHA-2 signature algorithms rsa-sha2-256/512. These algorithms have the advantage of using the same key type as "ssh-rsa" but use the safe SHA-2 hash algorithms. These have been supported since OpenSSH 7.2 and are already used by default if the client and server support them. * The ssh-ed25519 signature algorithm. It has been supported in OpenSSH since release 6.5. * The RFC5656 ECDSA algorithms: ecdsa-sha2-nistp256/384/521. These have been supported by OpenSSH since release 5.7.

Voilà c'est tout pour le moment, ça me permet de faire le premier article de la catégorie Crypto et de vous dire qu'un énorme article arrive sur ce sujet ici même :). En attendant pour la release note d'OpenSSH c'est ici.

Have fun !

dimanche 26 janvier 2020

"Entraînement" Call Of

Bonjour,

Pour une fois je ne parlerais pas de vulnérabilités mais de ... jeux vidéos, histoire de changer un peu.

Il fut un temps ou je jouais pas mal : d'abord sur PC à la bonne époque des LAN puis sur Xbox 360 beaucoup en multijoueur et pas mal sur Call Of MW/MW2/AW ... Je me démerdais pas mal, mais depuis, je joue beaucoup moins et du coup le niveau à grave baissé : en gros : je me prends une misère.

Du coup j'ai décidé de m’entraîner un peu histoire de pas trop passer pour un débile à chaque fois.

Alors je me suis fait une petite séance avec des bots en difficulté "Mixte" avec au programme :

 

  • Mêlée Générale standard;
  • Mêlée Générale : Réalisme + tir à la tête : autant dire que là : gros échec;
  • Match à mort par équipe Hardcore x 2.
Le tout sur différentes cartes.
Bilan, je vous laisse découvrir les vidéos mais sinon j'ai appris que :
  • Il faut que je retravaille la précision;
  • Certaines cartes sont trop petites pour ce genre de connerie;
  • Il faut que je change ma manette : celle là est trop abîmée pour certaines choses.
Pour voir le carnage de mon entraînement :
Sur mixer (jusqu'au 9 Février 2020 max) : https://mixer.com/psychovnr?vod=253395026
 
Bonne fin de WE à toutes et à tous !

 

jeudi 23 janvier 2020

Nouvelles vulnérabilités dans PHP

Deux nouvelles vulnérabilités semblent toucher PHP.

Voici l'advisory du CERT-FR : https://www.cert.ssi.gouv.fr/avis/CERTFR-2020-AVI-054/

Il semble qu'une des vulnérabilités touche mbstring et notamment la fonction mb_decode_mimeheader qui sert à décoer des en-tête MIME  : https://www.php.net/manual/fr/function.mb-decode-mimeheader.php.

Cette librairie fait partie de l'extension mbstring.

La librairie touchée semble être la libmbfl dans la fonction : mbfl_filt_conv_big5_wchar(int c, mbfl_convert_filter *filter).

Quand on regarde la pile d'exécution on peut voir que le chemin est le suivant (dans mbstring) :

mb_decode_mimeheader() -> mbfl_mime_header_decode() -> mime_header_decoder_collector() -> mbfl_filt_conv_qprintdec() -> mbfl_filter_output_pipe() -> mbfl_filt_conv_big5_wchar()

Autant vous dire que si vous cherchez à trouver cette vulnérabilité en faisant de l'analyse de code "à la mano" vous n'avez pas le cul sorti des ronces :)

Alors cette vulnérabilité a été trouvé comment ? Fuzzing ... Par reza : reza at iseclab dot org.

Vous pouvez tester si vous êtes vulnérables en faisant ceci :

./php -r 'mb_decode_mimeheader(file_get_contents("php://stdin"));' < global-overflow-cp950_pua_tbl.poc

Le fichier PoC est disponible ici : https://github.com/gaintcome/fuzz-php/blob/master/poc/mbstrings/global-overflow-cp950_pua_tbl.poc

(Au cas ou cela disparaîtrait j'en ai mis une copie ici : https://www.paranoiac-thoughts.com/netpsycho/public-scripts-and-confs/blob/master/global-overflow-cp950_pua_tbl.poc)

Pour voir la correction du la vulnérabilité : http://git.php.net/?p=php-src.git;a=blobdiff;f=ext/mbstring/libmbfl/filters/mbfilter_big5.c;h=5e1ca815da31ed68db2baae2b3d38699b546ca83;hp=f5ab8809ce8a799bd40bda74606f181fcef36c75;hb=2bcbc95f033c31b00595ed39f79c3a99b4ed0501;hpb=0f79b1bf301f455967676b5129240140c5c45b09

Alors pourquoi j'en parle : sérieusement le contrôle de MIME c'est courant, surtout dans les fonctionnalités d'upload, donc y a moyen de se faire plaisir.

De même, c'est vrai que PHP reste vaste (et inexploré ???) c'est donc une bonne piste pour s'entraîner au fuzzing et trouver des petites vulns :).

Have fun !

Comme d'habitude vous pouvez proposer des améliorations à cet article via : https://www.paranoiac-thoughts.com/netpsycho/blog_post/blob/master/20200123_nouvelles_vulnerabilites_php

mardi 14 janvier 2020

CVE-2019-19844 : Django account takeover, revue de code et bonne année

Déjà je tiens à vous souhaiter à toutes et à tous une excellente année 2020. Maintenant que ceci est dit passons au sujet qui nous intéresse : la description d'une petite vulnérabilité intéressante.

Aujourd'hui j'ai choisi la CVE-2019-19844 touchant Django. Cette vulnérabilité touche le formulaire de reset de mot de passe dont le code source (vulnérable) est disponible ici : https://github.com/django/django/blob/4cec3cc82a09b1a60a72e5437a7f0e9e0c7d203c/django/contrib/auth/forms.py

Voici les fonctions posant problèmes :

    def get_users(self, email):
        """Given an email, return matching user(s) who should receive a reset.
        This allows subclasses to more easily customize the default policies
        that prevent inactive users and users with unusable passwords from
        resetting their password.
        """
        active_users = UserModel._default_manager.filter(**{
            '%s__iexact' % UserModel.get_email_field_name(): email,
            'is_active': True,
        })
        return (u for u in active_users if u.has_usable_password())
    
    def save(self, domain_override=None,
             subject_template_name='registration/password_reset_subject.txt',
             email_template_name='registration/password_reset_email.html',
             use_https=False, token_generator=default_token_generator,
             from_email=None, request=None, html_email_template_name=None,
             extra_email_context=None):
        """
        Generate a one-use only link for resetting password and send it to the
        user.
        """
        email = self.cleaned_data["email"]
        if not domain_override:
            current_site = get_current_site(request)
            site_name = current_site.name
            domain = current_site.domain
        else:
            site_name = domain = domain_override
        for user in self.get_users(email):
            context = {
                'email': email,
                'domain': domain,
                'site_name': site_name,
                'uid': urlsafe_base64_encode(force_bytes(user.pk)),
                'user': user,
                'token': token_generator.make_token(user),
                'protocol': 'https' if use_https else 'http',
                **(extra_email_context or {}),
            }
            self.send_mail(
                subject_template_name, email_template_name, context, from_email,
                email, html_email_template_name=html_email_template_name,
            )

On peut avouer que comme ça la vulnérabilité n'est pas forcément très facile à détecter. En effet le problème est assez particulier, et montre que certains points de bonnes pratiques peuvent avoir des implications sécuritaires pouvant être importantes.

Les gros problèmes sont là : '%s__iexact' % UserModel.get_email_field_name(): email, et ici : return (u for u in active_users if u.has_usable_password()). J'avoue même comme cela ça ne saute pas aux yeux :). Du coup je vous invite à lire ceci : http://unicode.org/reports/tr36/#Recommendations_General.

Oui il s'agit bel et bien d'un problème d'encodage de caractères, en soit les petits curieux qui ont été voir la description de la CVE avaient sans doute trouvés :

Django before 1.11.27, 2.x before 2.2.9, and 3.x before 3.0.1 allows account takeover. A suitably crafted email address (that is equal to an existing user's email address after case transformation of Unicode characters) would allow an attacker to be sent a password reset token for the matched user account. (One mitigation in the new releases is to send password reset tokens only to the registered user email address.)

Je vous laisse découvrir le patch par vous même : https://github.com/django/django/commit/5b1fbcef7a8bec991ebe7b2a18b5d5a95d72cb70?diff=unified

Alors qu'est-ce que tout cela peut nous apprendre au final (à part qu'il faut patcheer son django). En soit ça vous donne de nouvelles idées de tests pour vos pentests / bug-bounties, mais surtout si vous faîtes de la revue de code cela est assez intéressant.

Premièrement car normalement ces fonctions devraient être revue "en profondeur" si on en croit l'OWASP :

"Are all the untrusted inputs validated? Input data is constrained and validated for type, length, format, and range."

Mais un peu plus que cela, car en effet en soit, tout est bon : ce qu'il manque c'est juste un respect des bonnes pratiques Unicode. Dites à votre avis, combien de développeurs, même voir d'auditeurs les connaissent ? Lors d'un audit de type revue de code au planning serré, cela est-il toujours contrôlé ? De même lors de pentest d'ailleurs ?

Donc voilà ce que cela nous apprends : Je suis sûr qu'il y a bien d'autres cas comme cela, alors à vos git pour les contrôles :).

J'avoue que je commence gentiment pour ce premier post de l'année, mais en regardant les dernières vulnérabilités dans le monde du libre, je n'avais pas trop l'inspiration, c'était soit Django, soit OpenSSL avec la CVE-2019-1551 qui je l'avoue ne m'a pas inspiré. Je vous laisse observer ce magnifique fichier PERL : https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff;h=419102400a2811582a7a3d4a4e317d72e5ce0a8f (y avait aussi un XSS dans wordpress mais bon ...).

Je vais tenter de préparer un nouvel article pour la semaine prochaine, sur une vulnérabilité un peu plus "costaud" ça sera sympatique.

 

Have fun !

P.S : comme toujours si vous voulez proposer des améliorations, corriger des fautes, passez par le gitlab des paranoiac-thoughts : https://www.paranoiac-thoughts.com/netpsycho/blog_post/blob/master/20200114_django

dimanche 8 décembre 2019

Git paranoiac-thoughts et articles

Bonjour,

Juste pour dire que j'ai remis en ligne un gitlab : https://www.paranoiac-thoughts.com (vous pouvez l'utiliser si vous désirez, mais bon pas trop d'espace). Et surtout si vous avez des améliorations pour mes articles n'hésitez pas à me les proposer. L'URL est ici : https://www.paranoiac-thoughts.com/netpsycho/blog_post

Have Fun !

dimanche 22 septembre 2019

Quelques nouvelles vulnérabilités dans PHP

Récemment quelques vulnérabilités ont été découvertes dans PHP.

Toutes concernent le traitement des images, j'ai néanmoins choisi de montrer l'une d'entre elle car c'est comment dire ... un classique au final.

Voici le code vulnérable :

                                                       if (*(p1 + 1) == '=') {

                                                                ++p1;

                                                                --str_left;

                                                        }

 

                                                        err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);

On voit tout de suite le problème ... que se passe-t-il si str_left = 0 ? ... forcément quelque chose de pas très bien, on le devine aisément.

Le problème est que str_left est un unsigned integer. Et ce vulnérabilité est comprise dans une boucle for qui en fin de boucle refait un str_left-- ... nous avons donc un énorme entier par la suite.

Et quand dans cette boucle nous avons des opérations de lecture nous nous retrouvons donc bien face à un : "Out-of-bounds read due to integer overflow".

La correction est assez simple :

                                                        * we can do at this point. */

                                                        if (*(p1 + 1) == '=') {

                                                                ++p1;

-                                                               --str_left;

+                                                               if (str_left > 1) {

+                                                                       --str_left;

+                                                               }

                                                        }

 

                                                        err = _php_iconv_appendl(pretval, encoded_word, (size_t)((p1 + 1) - encoded_word), cd_pl);

Pour plus d'informations : https://bugs.php.net/bug.php?id=78069

Concernant les autres vulnérabilités dans PHP : 

https://bugs.php.net/bug.php?id=77950

https://bugs.php.net/bug.php?id=77988

https://bugs.php.net/bug.php?id=78222

https://bugs.php.net/bug.php?id=78256

 

A vos patchs !

 

 

lundi 6 mai 2019

La superbe backdoor Américaine

On peut toujours parler des hypothétiques backdoors chinoises mais en tout cas les backdoors US sont bien réelles elles :).

Une petite clé SSH présentes par défaut sur les Nexus 9000. Elle donne les droits root bien évidemment.

Have Fun !

GRO packet of death

Ca rappelle le bon vieux temps :) Il semblerait qu'il soit possible de faire un DoS sur des kernels 5.0.x avec des paquets UDP avec des payloads null.

Sur le sujet : https://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git/commit/?id=4dd2b82d5adfbe0b1587ccad7a8f76d826120f37

Have fun !

jeudi 2 mai 2019

[CVE-2019-9928] Heap Overflow dans GStreamer

Un patch pour une vulnérabilité dans l'analyseur RTSP de Gstreamer vient de sortir, et en regardant de plus prêt on constate qu'il s'agit de l'histoire d'un classical fail pour le coup :

for (i = 0; session_id[i] != '\0'; i++) {

            if (session_id[i] == ';') {

              maxlen = i;

et un peu plus loin :

            strncpy (conn->session_id, session_id, maxlen);

Le patch ? remplacer le for par celui-ci :

          for (i = 0; i < maxlen && session_id[i] != '\0'; i++) {

Voilà c'était la classical vuln du soir :)

Have fun !

Etude de la vulnérabilité CVE-2019-10125 et combat pour le patching part. 2

Bonjour à toutes et à tous,

Je vais tenter ici d'étudier une vulnérabilité touchant le kernel Linux à savoir la CVE-2019-10125. Cette dernière a un score de 9.8 sur NVD, elle est qualifiée de 7.3 par RedHat, 5.5 par vulndb, "Medium" par Ubuntu, nous allons tenter de savoir pourquoi, et je vais dans la continuité parler de la difficulté de la mise en place des campagnes de patchs qui en découlent.

La description de la vulnérabilité est la suivante :

An issue was discovered in aio_poll() in fs/aio.c in the Linux kernel through 5.0.4. A file may be released by aio_poll_wake() if an expected event is triggered immediately (e.g., by the close of a pair of pipes) after the return of vfs_poll(), and this will cause a use-after-free.

Autant dire que c'est extrémement parlant pour le coup. Voilà vous avez compris je peux m'arrêter là ! Non je rigole, allons un peu plus loin car au final avec une explication pareil on peut avoir du mal à comprendre la note de 9.8.

Nous avons donc à faire à un use-after-free, pour ceux qui ne savent pas ce que c'est je vous invite à lire à minima ceci : https://www.owasp.org/index.php/Using_freed_memory

Nous savons que la vulnérabilité touche AIO, AIO c'est pour Asynchronous I/O c'est du POSIX et cela permet à un programme d'effectuer ... bah des I/O de manière asynchrone :) Merci super évidence Hervé !. En gros cela est utilise lorque l'on a un grand nombre d'opérations à traiter et qu'attendre un retour disque avec un read() bah ... ce n'est pas envisageable.

Petit exemple : pour lire le fichier /etc/shadow on ferait un truc de ce style :

fileDescripteur = open("/etc/shadow", O_RDONLY);
aio_context_t a = 0;
b = io_setup(128, &a);
char i[4096];
struct iocb icb = {.aio_fildes = fileDescripteur,
                   .aio_lio_opcode = IOCB_CMD_PREAD,
                   .aio_buf = (uint64_t)i,
                   .aio_nbytes = sizeof(i)};
struct iocb *iocbList[1] = {&icb};
b = io_submit(a, 1, iocbList);
struct io_event e[1] = {{0}};
b = io_getevents(a, 1, 1, e, NULL);
z = e[0].res;
printf("Données lues : %lld octets\n", z);
 

Si vous avez appréhendé le concept de use-after-free vous pouvez deviner que des opérations asynchrone, à gérer en C, c'est bien le genre d'endroit ou on oublie un petit cas de figure et ou le use-after-free arrive :).

Du coup qu'est-ce qui pose problème ?

Le problème semble venir de la fonction aio_poll(), regardons donc son code source (avant le patch) dans fs/aio.c :

Pour mon exemple j'ai pris le code source de la 5.0.2 que vous pouvez télécharger ici : https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.0.2.tar.gz.

Voici donc le code de la coupable :

static ssize_t aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb)
{
        struct kioctx *ctx = aiocb->ki_ctx;
        struct poll_iocb *req = &aiocb->poll;
        struct aio_poll_table apt;
        __poll_t mask;

        /* reject any unknown events outside the normal event mask. */
        if ((u16)iocb->aio_buf != iocb->aio_buf)
                return -EINVAL;
        /* reject fields that are not defined for poll */
        if (iocb->aio_offset || iocb->aio_nbytes || iocb->aio_rw_flags)
                return -EINVAL;

        INIT_WORK(&req->work, aio_poll_complete_work);
        req->events = demangle_poll(iocb->aio_buf) | EPOLLERR | EPOLLHUP;
        req->file = fget(iocb->aio_fildes);
        if (unlikely(!req->file))
                return -EBADF;

        req->head = NULL;
        req->woken = false;
        req->cancelled = false;

        apt.pt._qproc = aio_poll_queue_proc;
        apt.pt._key = req->events;
        apt.iocb = aiocb;

        apt.error = -EINVAL; /* same as no support for IOCB_CMD_POLL */

        /* initialized the list so that we can do list_empty checks */
        INIT_LIST_HEAD(&req->wait.entry);
        init_waitqueue_func_entry(&req->wait, aio_poll_wake);

        /* one for removal from waitqueue, one for this function */
        refcount_set(&aiocb->ki_refcnt, 2);

        mask = vfs_poll(req->file, &apt.pt) & req->events;
        if (unlikely(!req->head)) {
                /* we did not manage to set up a waitqueue, done */
                goto out;
        }

        spin_lock_irq(&ctx->ctx_lock);
        spin_lock(&req->head->lock);
        if (req->woken) {
                /* wake_up context handles the rest */
                mask = 0;

                apt.error = 0;
        } else if (mask || apt.error) {
                /* if we get an error or a mask we are done */
                WARN_ON_ONCE(list_empty(&req->wait.entry));
                list_del_init(&req->wait.entry);
        } else {
                /* actually waiting for an event */
                list_add_tail(&aiocb->ki_list, &ctx->active_reqs);
                aiocb->ki_cancel = aio_poll_cancel;
        }
        spin_unlock(&req->head->lock);
        spin_unlock_irq(&ctx->ctx_lock);

out:
        if (unlikely(apt.error)) {
                fput(req->file);
                return apt.error;
        }

        if (mask)
                aio_poll_complete(aiocb, mask);
        iocb_put(aiocb);
        return 0;
}
 

Bon autant dire que comme ça, ça ne nous aide pas trop, y a même pas un free qui traîne, par contre on trouve des fget() et des fput() et si on prend le temps de lire ce que disent les linuxiens on voit ceci dans un commentaire qui va avec le patch :

Al Viro root-caused a race where the IOCB_CMD_POLL handling of fget/fput() could cause us to access the file pointer after it had already been freed.

Bon c'est que l'on se rapproche tout de même. Mais au final on ne la voit toujours pas bien cette vulnérabilité qui a un 9.8 !

Avant d'expliquer la vulnérabilité en soit il faut décrire, entre autre, ce que sont : aio_kiocb et iocb.

Voici donc la définition de aio_kiocb :

struct aio_kiocb {
        union {
                struct kiocb            rw;
                struct fsync_iocb       fsync;
                struct poll_iocb        poll;
        };

        struct kioctx           *ki_ctx;
        kiocb_cancel_fn         *ki_cancel;

        struct iocb __user      *ki_user_iocb;  /* user's aiocb */
        __u64                   ki_user_data;   /* user's data for completion */

        struct list_head        ki_list;        /* the aio core uses this
                                                 * for cancellation */
        refcount_t              ki_refcnt;

        /*
         * If the aio_resfd field of the userspace iocb is not zero,
         * this is the underlying eventfd context to deliver events to.
         */
        struct eventfd_ctx      *ki_eventfd;
};
 

Et celle de iocb :

struct iocb {
        /* these are internal to the kernel/libc. */
        __u64   aio_data;       /* data to be returned in event's data */

#if defined(__BYTE_ORDER) ? __BYTE_ORDER == __LITTLE_ENDIAN : defined(__LITTLE_ENDIAN)
        __u32   aio_key;        /* the kernel sets aio_key to the req # */
        __kernel_rwf_t aio_rw_flags;    /* RWF_* flags */
#elif defined(__BYTE_ORDER) ? __BYTE_ORDER == __BIG_ENDIAN : defined(__BIG_ENDIAN)
        __kernel_rwf_t aio_rw_flags;    /* RWF_* flags */
        __u32   aio_key;        /* the kernel sets aio_key to the req # */
#else
#error edit for your odd byteorder.
#endif

        /* common fields */
        __u16   aio_lio_opcode; /* see IOCB_CMD_ above */
        __s16   aio_reqprio;
        __u32   aio_fildes;

        __u64   aio_buf;
        __u64   aio_nbytes;
        __s64   aio_offset;

        /* extra parameters */
        __u64   aio_reserved2;  /* TODO: use this for a (struct sigevent *) */

        /* flags for the "struct iocb" */
        __u32   aio_flags;

        /*
         * if the IOCB_FLAG_RESFD flag of "aio_flags" is set, this is an
         * eventfd to signal AIO readiness to
         */
        __u32   aio_resfd;
};

 

Afin de mieux comprendre la vulnérabilité je vous suggère aussi de lire la page man de io_submit : http://man7.org/linux/man-pages/man2/io_submit.2.html.

Lorsque l'on fait un io_submit() ce dernier alloue des instance aio_kiocb et les passe à aio_poll(), ensuite cette fonction fait son job, à savoir :

  • résoudre le descripteur de fichier;
  • change l'état woken;
  • passe les données à vfs_poll();
  • Gère l'état de la queue et le statut des aio_kiocb.

En soit la plus part des problèmes vous les verrez dans le fichier aio.c (fs/aio.c) :

En plus des erreurs qui faisaient le pointeur vers le fichier n'était pas le premier élément de certaines structures, la plus grosse erreur se situe aussi dans une fonction au nom assez évocatrice à savoir aio_complete_rw() qui avait le code suivant :

file_end_write(kiocb->ki_filp);

}

fput(kiocb->ki_filp);

aio_complete(iocb, res, res2);

}

Et des goto out_fput un peu partout comme dans la fonction : aio_prep_rw() :

ret = kiocb_set_rw_flags(req, iocb->aio_rw_flags);

if (unlikely(ret))

goto out_fput;

req->ki_flags &= ~IOCB_HIPRI; 

return 0;

 

out_fput:

fput(req->ki_filp);

return ret;

}

Le problème vient donc du fait que si juste après notre fget() et avant le retour de poll() un thread termine le descripteur, alors le fput() est fait sur une ressource libérée ... nous avons donc un Use-After-Free.

Du coup on comprend bien mieux la description :

An issue was discovered in aio_poll() in fs/aio.c in the Linux kernel through 5.0.4. A file may be released by aio_poll_wake() if an expected event is triggered immediately (e.g., by the close of a pair of pipes) after the return of vfs_poll(), and this will cause a use-after-free.

 

Bon là vous me dites : ok on a un super UAF en local, certes avec un "pont" ring kernel / ring user et tout, mais je vois pas pourquoi on se retrouve avec un score de 9.8.

Alors je vous le donne en mille : il est possible de gérer des sockets avec ça :).

Bon après IOCB_CMD_POLL n'étant pas si vieux : https://lwn.net/Articles/743714/ ... Trouver un cas réel et exploitable ça ne va pas nécessaire être de la tarte, mais c'est certainement possible.

Par exemple vous trouverez ici un code faisant cela : https://github.com/cloudflare/cloudflare-blog/blob/master/2019-01-io-submit/aio_poll.c#L63

Et du coup cela nous dit quoi au final ?

Alors la CVE-2019-10125 c'est quoi comme note : au fait si vous êtes paranoïac et théoriquement : oui c'est du 9.8, par contre je peux comprendre les notes de 5.5 et Medium d'une autre part : bien que pas impossible, l'exploitation de cette vulnérabilité semble ... plus de complexe (et bon généralement on trouve quelque chose de nettement plus simple à côté :)).

De même il est à noter que certaines distributions ne semblent même pas être vulnérables : https://deb.freexian.com/extended-lts/tracker/CVE-2019-10125.

Alors je n'ai pas spécialement envie de fixer une note concernant cette vulnérabilité de mon coté : ça n'a aucun intérêt, je suis plutôt à mener le combat du patching : donc patchez, on s'en moque de la criticité ;).

Et voilà où le bât blesse, dans beaucoup de circonstance, si un RCE à distance n'est pas démontrable facilement et avec des logiciels/middlewares bien utilisés il est assez dur de dire il faut patcher et en urgence. Ou même patcher de la production car en effet ceci ne parle au final pas beaucoup à un responsable de production :

 BUG: KASAN: use-after-free in perf_trace_lock_acquire+0x3ab/0x570
 Read of size 8 at addr ffff888379bfd4b0 by task syz-executor.1/4953

 CPU: 0 PID: 4953 Comm: syz-executor.1 Not tainted 4.19.24
 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1
 Call Trace:
  __dump_stack lib/dump_stack.c:77 [inline]
  dump_stack+0xca/0x13e lib/dump_stack.c:113
  print_address_description+0x79/0x330 mm/kasan/report.c:256
  kasan_report_error mm/kasan/report.c:354 [inline]
  kasan_report+0x18a/0x2e0 mm/kasan/report.c:412
  trace_event_get_offsets_lock_acquire include/trace/events/lock.h:13 [inline]
  perf_trace_lock_acquire+0x3ab/0x570 include/trace/events/lock.h:13
  trace_lock_acquire include/trace/events/lock.h:13 [inline]
  lock_acquire+0x202/0x310 kernel/locking/lockdep.c:3899
  __raw_spin_lock include/linux/spinlock_api_smp.h:142 [inline]
  _raw_spin_lock+0x2c/0x40 kernel/locking/spinlock.c:144
  spin_lock include/linux/spinlock.h:329 [inline]
  aio_poll fs/aio.c:1750 [inline]
  io_submit_one+0xb90/0x1b30 fs/aio.c:1853
  __do_sys_io_submit fs/aio.c:1919 [inline]
  __se_sys_io_submit fs/aio.c:1890 [inline]
  __x64_sys_io_submit+0x19b/0x500 fs/aio.c:1890
  do_syscall_64+0xc8/0x580 arch/x86/entry/common.c:290
  entry_SYSCALL_64_after_hwframe+0x49/0xbe
  ......
  Allocated by task 4953:
  set_track mm/kasan/kasan.c:460 [inline]
  kasan_kmalloc+0xa0/0xd0 mm/kasan/kasan.c:553
  kmem_cache_alloc_trace+0x12f/0x2d0 mm/slub.c:2733
  kmalloc include/linux/slab.h:513 [inline]
  kzalloc include/linux/slab.h:707 [inline]
  alloc_pipe_info+0xdf/0x410 fs/pipe.c:633
  get_pipe_inode fs/pipe.c:712 [inline]
  create_pipe_files+0x98/0x780 fs/pipe.c:744
  __do_pipe_flags+0x35/0x230 fs/pipe.c:781
  do_pipe2+0x87/0x150 fs/pipe.c:829
  __do_sys_pipe2 fs/pipe.c:847 [inline]
  __se_sys_pipe2 fs/pipe.c:845 [inline]
  __x64_sys_pipe2+0x55/0x80 fs/pipe.c:845
  do_syscall_64+0xc8/0x580 arch/x86/entry/common.c:290
  entry_SYSCALL_64_after_hwframe+0x49/0xbe

 Freed by task 4952:
  set_track mm/kasan/kasan.c:460 [inline]
  __kasan_slab_free+0x12e/0x180 mm/kasan/kasan.c:521
  slab_free_hook mm/slub.c:1371 [inline]
  slab_free_freelist_hook mm/slub.c:1398 [inline]
  slab_free mm/slub.c:2953 [inline]
  kfree+0xeb/0x2f0 mm/slub.c:3906
  put_pipe_info+0xb0/0xd0 fs/pipe.c:556
  pipe_release+0x1ab/0x240 fs/pipe.c:577
  __fput+0x27f/0x7f0 fs/file_table.c:278
  task_work_run+0x136/0x1b0 kernel/task_work.c:113
  tracehook_notify_resume include/linux/tracehook.h:193 [inline]
  exit_to_usermode_loop+0x1a7/0x1d0 arch/x86/entry/common.c:166
  prepare_exit_to_usermode arch/x86/entry/common.c:197 [inline]
  syscall_return_slowpath arch/x86/entry/common.c:268 [inline]
  do_syscall_64+0x461/0x580 arch/x86/entry/common.c:293
  entry_SYSCALL_64_after_hwframe+0x49/0xbe
  1. Tiens : A noter que sur securityfocus la vulnérabilité est bien noté comme étant Local.

Ni même à certains ingénieur système d'ailleurs ... Du coup comment faire ?

Créer un PoC démontrant la vulnérabilité et son exploitation (en local ET en remote), combien d'heure pour cela ?

Pas sûr que beaucoup d'équipes de cyber-sécurité internes aient le temps pour ce genre d'amusement (elles aimeraient bien j'en suis sûr, mais bon), ne serait-ce que pour trouver cet exemple de vulnérabilité, pour démontrer l'exemple, l'étude de cette dernière, regarder les fonctions touchées, les patchs, trouver des exemples d'utilisations, et écrire ces quelques lignes ... cela prend pas mal de temps, ou alors je suis rouillé c'est possible je veillis, mais faire un PoC d'exploitation en plus.

Et faire ça pour chaque patchs kernels/gLibC de ce type ?

Non une seule politique doit fonctionner : si je passe un coup d'OpenVAS/Qualys/Nessus/Rapid7 : le résultat doit être null, je sais c'est inatteignable mais bon c'est l'objectif à fixer tout de même.

En même temps il est souvent assez difficile de faire comprendre cela à des DSI, et même des RSSI, quand des vendeurs d'échecs de rêves leurs promettent une sécurité à 100% contre les menaces qui sont inconnues à longueur de journée. Non ! Non ! et non ! Ton anti-APT-de-la-mort-qui-tue ne sert à rien si le parc n'est pas patché. Et à lma rigueur s'il ne restait que la CVE-2019-10125 sur la plus part des systèmes ... Ca serait déjà le bonheur :)

Conclusions

Certaines vulnérabilités sont déjà assez dure à définir d'un point de vue de la criticité, mais leur exploitabilité in da real life n'est pas toujours facile à prouver, du moins dans le temps d'une personne travaillant sur de la production. Après certes on peut faire une petite application avec un thread qui déclenche le UAF à un moment définit mais cela ne prouvera jamais à un responsable de production, ou pire à un responsable métier, un cas d'exploitation réel.

D'ailleurs les différences de scores montre aussi d'autres problèmes : certaines ou certains ont pensé que cela pouvait être exploitable à distance pour se retrouver avec un 9.8 tandis que d'autres non (Medium, 5.5, 7.3). Assez difficile dans ce cas de justifier d'un patching en urgence de ce type de vulnérabilités. Cela montre donc la difficulté du travail d'évangelisation à faire autour du patching, qui malgré tout, et malgré le temps passé est toujours quelque chose de dur à mettre en oeuvre.

Voilà, désolé cela faisait bien longtemps que je n'avais pas fait un blogpost, j'espère que ce n'est pas trop fouilli et que ce vous a plu (et que je n'ai pas laissé trop de fautes d'orthographe). Promis la prochaine fois je parle d'une, oui je précise une car je vais faire des blogpost régulièrement sur les dernières vulnérabilités touchant Linux (du moins le kernel et la gLibC), vulnérabilité je mets un PoC d'exploitation :).

Mais bon après on est tout de même mieux à faire ça, que faire de l'IAM !

[CVE-2019-6974][CVE-2019-7221] : Kernel - kvm : 2 UAF à corriger et combat pour le patching part.1

Bonjour à toutes et à tous,

Ce soir je viens vous parler du RHSA-2019:0818 (https://access.redhat.com/errata/RHSA-2019:0818) qui corrige, entre autre, deux vulnérabilités de types Use-After-Free à savoir : CVE-2019-6974 et CVE-2019-7221. Ces deux vulnérabilités sont assez intéressantes, et assez simples, à étudier car elles font l'objet toutes les deux de publications par projectzero.

Vous allez me dire : "tu triches du coup, tu reprends le travail d'autres !" : oui pour le coup j'avoue, mais cela fait parti d'un ensemble, nous allons voir ici l'exemple de deux vulnérabilités assez bien documentées mais je vous prépare un prochain article montrant comment il est difficile de faire prendre en compte la menace d'une vulnérabilité, notamment dans le cadre d'une campagne de patching.

La CVE-2019-7221

La description générale :

A use-after-free vulnerability was found in the way the Linux kernel's KVM hypervisor emulates a preemption timer for L2 guests when nested (=1) virtualization is enabled. This high resolution timer(hrtimer) runs when a L2 guest is active. After VM exit, the sync_vmcs12() timer object is stopped. The use-after-free occurs if the timer object is freed before calling sync_vmcs12() routine. A guest user/process could use this flaw to crash the host kernel resulting in a denial of service or, potentially, gain privileged access to a system.

La description sur projectzero est disponible ici : https://bugs.chromium.org/p/project-zero/issues/detail?id=1760

La CVE-2019-6974

La description générale :

A use-after-free vulnerability was found in the way the Linux kernel's KVM hypervisor implements its device control API. While creating a device via kvm_ioctl_create_device(), the device holds a reference to a VM object, later this reference is transferred to the caller's file descriptor table. If such file descriptor was to be closed, reference count to the VM object could become zero, potentially leading to a use-after-free issue. A user/process could use this flaw to crash the guest VM resulting in a denial of service issue or, potentially, gain privileged access to a system.

On va un peu plus d'attarder sur cette vulnérabilité car elle vous permttra d'appréhender mon prochain article. Qui sera un peu plus long je vous l'assure.

Comme expliquer dans l'article de projectzero le code posant problème est le suivant :

kvm_ioctl_create_device() contains the following code:

dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;

dev->ops = ops;
dev->kvm = kvm;

mutex_lock(&kvm->lock);
ret = ops->create(dev, cd->type);
if (ret < 0) {
mutex_unlock(&kvm->lock);
kfree(dev);
return ret;
}
list_add(&dev->vm_node, &kvm->devices);
mutex_unlock(&kvm->lock);

if (ops->init)
ops->init(dev);

ret = anon_inode_getfd(ops->name, &kvm_device_fops, dev, O_RDWR | O_CLOEXEC);
if (ret < 0) {
mutex_lock(&kvm->lock);
list_del(&dev->vm_node);
mutex_unlock(&kvm->lock);
ops->destroy(dev);
return ret;
}

kvm_get_kvm(kvm);
cd->fd = ret;
 

De même on nous explique que :

This code:

1. creates a device that holds a reference to the VM object (with a borrowed
reference, the VM's refcount has not been bumped yet)
2. initializes the device
3. transfers the reference to the device to the caller's file descriptor table
4. calls kvm_get_kvm() to turn the borrowed reference to the VM into a real
reference

The ownership transfer in step 3 must not happen before the reference to the VM
becomes a proper, non-borrowed reference, which only happens in step 4.
After step 3, an attacker can close the file descriptor and drop the borrowed
reference, which can cause the refcount of the kvm object to drop to zero.

Oui je sais jusque là je me suis vraiment déchiré niveau éditorial :). Mais voilà on va rentrer dans le dur.

Alors le problème se situe donc quelque part par ici :

if (ops->init)

ops->init(dev);

ret = anon_inode_getfd(ops->name, &kvm_device_fops, dev, O_RDWR | O_CLOEXEC);

if (ret < 0) {

mutex_lock(&kvm->lock);

list_del(&dev->vm_node);

mutex_unlock(&kvm->lock);

Et ici :

return ret;

}

kvm_get_kvm(kvm);

cd->fd = ret;

return 0;

}

On peut donc constater qu'effectivement il manque quelques petites choses, et parfois des petites choses en trop :

Dans la première partie de code que je présente à aucun moment kvm_get_kvm() n'est appelé, il n'est appelé qu'à la fin du processus. De même aucun appel n'est fait vers kvm_put_kvm() fonction définit comme suit :

void kvm_put_kvm(struct kvm *kvm)
  {
  if (refcount_dec_and_test(&kvm->users_count))
  kvm_destroy_vm(kvm);
  }
 

EXPORT_SYMBOL_GPL(kvm_put_kvm);

Voilà cette vulnérabilité est assez "simple" à appréhender et à comprendre, d'ailleurs vous pouvez retrouver un exemple d'exploitation sur projectzero : https://bugs.chromium.org/p/project-zero/issues/detail?id=1765

Patching

Je ne vais pas vous présenter le patch vous pouvez le trouver vous même, mais vous parler de patching, en soit ce genre de vulnérabilité est sympathique, en effet, elle est simple à décrire, un PoC d'exploitation est présent. C'est publié par des personnes connues et tout le monde est d'accord sur l'impact, convaincre un responsable de production ou un resposable métier de pacher est bien plus facile avec ce genre de vulnérabilité, mais dans un prochain article à paraître (paru ici : https://www.herve-godquin.fr/index.php?post/2019/04/30/Etude-de-la-vuln%C3%A9rabilit%C3%A9-CVE-2019-10125) très bientôt vous verrez que ce n'est pas nécessairement le cas tout le temps.

Conclusion

Patchez !

mercredi 1 mai 2019

Nouveau blog

Bienvenue à toutes et à tous sur mon nouveau blog,

J'ai décidé de faire tout en un en créant ce nouveau blog à la place d'un site statique.

Je vais essayer que ce blog soit un peu plus actif et un peu plus axé cyber-sécurité que le dernier, bien que je continuerai à parler aussi de technique liée au streaming/VoD aussi de temps en temps.

Je vous laisse je vais préparer de nouveaux posts :).

Have fun !