Skip to content
KitploitKITPLOIT
OutilsBlog
Log in
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é.

FluxContactConfidentialité© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2020-28018 — Exploit pour la faille use-after-free d'Exim (CVE-2020-28018) permettant une exécution de code à distance via des primitives de corruption mémoire, incluant lecture/écriture arbitraire et fuite de tas, avec une chaîne optionnelle d'élévation de privilèges locaux. | Kitploit
Outils/GitHubGitHub/dorkerdevil/cve-2020-28018
Escalade de PrivilègesCriminalistique MémoireAnalyse des VulnérabilitésExploitationOutil d'Accès à DistanceDéveloppement de Charges UtilesExploitation de Binaires
GitHub

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
dorkerdevil/cve-2020-28018

CVE-2020-28018

Exploit pour la faille use-after-free d'Exim (CVE-2020-28018) permettant une exécution de code à distance via des primitives de corruption mémoire, incluant lecture/écriture arbitraire et fuite de tas, avec une chaîne optionnelle d'élévation de privilèges locaux.

Voir le dépôt
7112il y a 5 ansPas encore vérifié

CVE-2020-28018: Use-after-free (UAF) dans Exim menant à une exécution de code à distance (RCE)

Introduction

Il existe une vulnérabilité de use-after-free (UAF) dans tls-openssl.c qui permet à des attaquants distants non authentifiés de corrompre des données mémoire internes, aboutissant ainsi à une exécution de code à distance.

Primitives :

  • Fuite de mémoire
  • Primitive de lecture arbitraire
  • Primitive Write-What-Where

En enchaînant toutes ces primitives, il est possible de contourner entièrement toutes les mitigations d'exploitation disponibles et d'aboutir finalement à une exécution de code à distance en tant qu'utilisateur exim.

Cette vulnérabilité a été publiée dans une vaste liste de vulnérabilités ; le rapport officiel de Qualys chaîne le Use-After-Free avec CVE-2020-28008 pour effectuer une élévation de privilèges locale (LPE) une fois la RCE obtenue.

Pré-requis

Exim doit être configuré / compilé de la manière suivante :

  • TLS est activé
  • OpenSSL est utilisé (au lieu de GnuTLS)
  • Exim est une des versions vulnérables
  • X_PIPE_CONNECT est désactivé

Vous pouvez utiliser le script checker.py pour vérifier si un serveur distant exécute une version vulnérable et possède certains prérequis nécessaires pour être exploitable.

[!] checker.py ne déclenche PAS la vulnérabilité, il vérifie simplement la version vulnérable, et teste si PIPELINING et TLS sont activés. Cela signifie que ce vérificateur ne vérifie pas le correctif, et peut donc générer des faux positifs.

Code vulnérable

Comme nous le savons déjà, la vulnérabilité se situe dans tls-openssl.c.

/*************************************************
*         Write bytes down TLS channel           *
*************************************************/

/*
Arguments:
  ct_ctx    client context pointer, or NULL for the one global server context
  buff      buffer of data
  len       number of bytes
  more	    further data expected soon

Returns:    the number of bytes after a successful write,
            -1 after a failed write

Used by both server-side and client-side TLS.
*/

int
tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more)
{
int outbytes, error, left;
SSL * ssl = ct_ctx ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl;
static gstring * corked = NULL;

DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
  buff, (unsigned long)len, more ? ", more" : "");

/* Lacking a CORK or MSG_MORE facility (such as GnuTLS has) we copy data when
"more" is notified.  This hack is only ok if small amounts are involved AND only
one stream does it, in one context (i.e. no store reset).  Currently it is used
for the responses to the received SMTP MAIL , RCPT, DATA sequence, only. */
/*XXX + if PIPE_COMMAND, banner & ehlo-resp for smmtp-on-connect. Suspect there's
a store reset there. */

if (!ct_ctx && (more || corked))
  {
#ifdef EXPERIMENTAL_PIPE_CONNECT
  int save_pool = store_pool;
  store_pool = POOL_PERM;
#endif

  corked = string_catn(corked, buff, len);

#ifdef EXPERIMENTAL_PIPE_CONNECT
  store_pool = save_pool;
#endif

  if (more)
    return len;
  buff = CUS corked->s;
  len = corked->ptr;
  corked = NULL;
  }

for (left = len; left > 0;)
  {
  DEBUG(D_tls) debug_printf("SSL_write(%p, %p, %d)\n", ssl, buff, left);
  outbytes = SSL_write(ssl, CS buff, left);
  error = SSL_get_error(ssl, outbytes);
  DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error);
  switch (error)
    {
    case SSL_ERROR_SSL:
      ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
      log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring);
      return -1;

    case SSL_ERROR_NONE:
      left -= outbytes;
      buff += outbytes;
      break;

    case SSL_ERROR_ZERO_RETURN:
      log_write(0, LOG_MAIN, "SSL channel closed on write");
      return -1;

    case SSL_ERROR_SYSCALL:
      log_write(0, LOG_MAIN, "SSL_write: (from %s) syscall: %s",
	sender_fullhost ? sender_fullhost : US"<unknown>",
	strerror(errno));
      return -1;

    default:
      log_write(0, LOG_MAIN, "SSL_write error %d", error);
      return -1;
    }
  }
return len;
}

smtp_setup_msg() est la fonction principale qui lit le message provenant du client.

Dans certaines situations, smtp_reset() est appelée, ce qui nettoie tous les tampons et valeurs.

Cela peut se produire dans des situations comme :

  • la réception de HELO/EHLO
  • la réception de STARTTLS
  • la réception de RSET
  • au démarrage de smtp_setup_msg()

À la fin de smtp_reset(), un appel à store_reset() est effectué.

store_reset est une macro qui encapsule la fonction store_reset_3().

Les fonctions store sont simplement des fonctions qui gèrent la mémoire dynamique.

Exim utilise un allocateur par pool sur des blocs obtenus via malloc.

Il existe également une fonctionnalité intéressante : une implémentation de chaîne extensible (growable string).

Structure gstring :

typedef struct gstring {
  int   size;           /* Current capacity of string memory */
  int   ptr;            /* Offset at which to append further chars */
  uschar * s;           /* The string memory */
} gstring;

Lorsqu'il a besoin de plus d'espace pour concaténer une nouvelle chaîne, il appelle gstring_grow().

Cette fonction essaie d'abord d'appeler store_extend_3(), qui tente d'étendre la mémoire dans le même bloc du pool.

Cela peut être utile lorsque la longueur de l'entrée n'est pas connue, mais si de la mémoire supplémentaire a été allouée après, nous ne pourrons pas l'étendre.

Ensuite, gstring_grow() appelle store_newblock_3(), qui retourne simplement une nouvelle zone mémoire et copie les octets déjà présents dans l'ancienne vers la nouvelle.

Ensuite, le pointeur g->s est restauré à partir de gstring_catn().

Dans la fonction tls_write(), on peut voir un BOOL appelé more.

Il indique s'il y a d'autres données à copier dans le tampon de chaîne avant de renvoyer les données à l'utilisateur.

Si c'est le cas, le pointeur n'est pas mis à NULL.

Sinon, les données contenues dans le tampon de chaîne sont renvoyées à l'utilisateur.

Cette fonctionnalité ouvre des pistes intéressantes pour déclencher un Use-After-Free.

Premièrement, le pointeur vers la structure gstring est stocké dans une variable statique ; cela signifie que lors de futurs appels à tls_write(), nous pourrons l'utiliser.

Comment pouvons-nous libérer le tampon puis être en mesure de l'utiliser ?

Nous devons faire en sorte que smtp_setup_msg() appelle smtp_reset() alors que l'un de nos tampons se trouve encore dans server_corked (non mis à NULL).

Après le reset, si nous appelons tls_write() d'une manière ou d'une autre, le pointeur sera toujours là, ce qui nous permet de l'utiliser après que la mémoire a été libérée.

smtp_reset() libère toute la mémoire de POOL_MAIN, dans laquelle notre tampon est contenu.

Déclenchement du Use-After-Free

Pour contrôler le Use-After-Free, nous devons d'abord initialiser une nouvelle connexion.

Comme nous voulons exploiter tls_write(), nous devons d'abord démarrer une nouvelle session TLS.

Nous envoyons donc d'abord une commande EHLO, suivie d'un STARTTLS pour démarrer la connexion TLS.

Ensuite, pour que more soit à 1, nous pipelinons une commande, et la dernière sera la moitié d'un NOOP.

Nous fermons la connexion TLS et envoyons le reste de la commande NOOP.

Nous envoyons maintenant à nouveau EHLO, ce qui déclenchera l'appel de smtp_reset et libérera notre tampon.

Nous devons maintenant démarrer une autre connexion TLS pour pouvoir utiliser à nouveau tls_write().

Nous envoyons STARTTLS.

Désormais, l'envoi de n'importe quelle commande au serveur aboutira à l'appel de tls_write() pour renvoyer une réponse.

Mais... server_corked contient toujours un pointeur vers une zone de la mémoire libérée.

Télécharger l’outil