Bug 11939 - UAF in textIterate (mbox.c)
UAF in textIterate (mbox.c)
Status: RESOLVED FIXED
Product: ClamAV
Classification: ClamAV
Component: All
ALL
x86_64 GNU/Linux
: P1 security
: ---
Assigned To: Micah Snyder
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2017-10-26 18:39 EDT by Suleman Ali
Modified: 2021-09-23 16:36 EDT (History)
7 users (show)

See Also:
QA Contact:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Suleman Ali 2017-10-26 18:39:13 EDT
Created attachment 7304 [details]
poc, pass is virus

There is an Use-After-Free dealing with Bounce messages. The UAF happens at `text.c:446` inside the function `textIterate`:
```c
444         if(destroy)
445                 while(t_text) {
446                         (*cb)(t_text->t_line, arg);
447 
```
Backtrace:
```c
    #0 0x62ab94 in textIterate /home/default/clamav-devel/libclamav/text.c:446:18
    #1 0x62b07e in textToFileblob /home/default/clamav-devel/libclamav/text.c:380:7
    #2 0x605bed in parseEmailBody /home/default/clamav-devel/libclamav/mbox.c:2381:32
    #3 0x5fd13e in cli_parse_mbox /home/default/clamav-devel/libclamav/mbox.c:555:11
    #4 0x5fc091 in cli_mbox /home/default/clamav-devel/libclamav/mbox.c:353:9
    #5 0x5dd62c in cli_scanmail /home/default/clamav-devel/libclamav/scanners.c:2010:16
    #6 0x5c4d31 in magic_scandesc /home/default/clamav-devel/libclamav/scanners.c:3294:19
    #7 0x5cb434 in cli_map_scandesc /home/default/clamav-devel/libclamav/scanners.c:3779:15
    #8 0x5cc771 in scan_common /home/default/clamav-devel/libclamav/scanners.c:3896:16
    #9 0x5ccbf7 in cl_scanmap_callback /home/default/clamav-devel/libclamav/scanners.c:4042:12
```

Another backtrace, without using the cl_scanmap function:
```c
    #0 0x81b8b9e in textIterate /tmp/clamav-devel/libclamav/text.c:446:4
    #1 0x81b8b9e in textToFileblob /tmp/clamav-devel/libclamav/text.c:380
    #2 0x81988b9 in parseEmailBody /tmp/clamav-devel/libclamav/mbox.c:2385:32
    #3 0x8194e47 in cli_parse_mbox /tmp/clamav-devel/libclamav/mbox.c:555:11
    #4 0x8194e47 in cli_mbox /tmp/clamav-devel/libclamav/mbox.c:353
    #5 0x8182f73 in cli_scanmail /tmp/clamav-devel/libclamav/scanners.c:2010:16
    #6 0x816e5d2 in magic_scandesc /tmp/clamav-devel/libclamav/scanners.c:3291:19
    #7 0x8167904 in cli_base_scandesc /tmp/clamav-devel/libclamav/scanners.c:3616:11
    #8 0x8176391 in cli_magic_scandesc /tmp/clamav-devel/libclamav/scanners.c:3625:12
    #9 0x8176391 in scan_common /tmp/clamav-devel/libclamav/scanners.c:3891
    #10 0x8177162 in cl_scandesc_callback /tmp/clamav-devel/libclamav/scanners.c:4032:12
    #11 0x8177162 in cl_scanfile_callback /tmp/clamav-devel/libclamav/scanners.c:4099
    #12 0x8177162 in cl_scanfile /tmp/clamav-devel/libclamav/scanners.c:4082
```

The variable `t_text` is freed previously at `messageToFileblob`->`textDestroy`. Here is the call stack during the freeing:
```c
freed by thread T0 here:
    #0 0x4e1a52 in free (/home/default/clamav-devel/clamavfuzz_libFuzzer+0x4e1a52)
    #1 0x629a39 in textDestroy /home/default/clamav-devel/libclamav/text.c:129:3
    #2 0x623493 in messageToFileblob /home/default/clamav-devel/libclamav/message.c:1490:3
    #3 0x604590 in parseEmailBody /home/default/clamav-devel/libclamav/mbox.c:2163:10
    #4 0x5fd13e in cli_parse_mbox /home/default/clamav-devel/libclamav/mbox.c:555:11
    #5 0x5fc091 in cli_mbox /home/default/clamav-devel/libclamav/mbox.c:353:9
    #6 0x5dd62c in cli_scanmail /home/default/clamav-devel/libclamav/scanners.c:2010:16
    #7 0x5c4d31 in magic_scandesc /home/default/clamav-devel/libclamav/scanners.c:3294:19
    #8 0x5cb434 in cli_map_scandesc /home/default/clamav-devel/libclamav/scanners.c:3779:15
    #9 0x5cc771 in scan_common /home/default/clamav-devel/libclamav/scanners.c:3896:16
    #10 0x5ccbf7 in cl_scanmap_callback /home/default/clamav-devel/libclamav/scanners.c:4042:12
```

The text trying to be display belongs to the body of the email, this text is freed after the message has been converted to `fileblob`. But after this step ClamAV tries to look for viruses hidden inside the HTML:
```c
2340         /*
2341          * No attachments - scan the text portions, often files
2342          * are hidden in HTML code
2343          */
...
2364                         else if((t_line = encodingLine(mainMessage)) != NULL) {
2365                                 /*
2366                                  * Some bounces include the message
2367                                  * body without the headers.
2368                                  * FIXME: Unfortunately this generates a
2369                                  * lot of false positives that a bounce
2370                                  * has been found when it hasn't.
2371                                  */
2372                                 if((fb = fileblobCreate()) != NULL) {
2373                                         cli_dbgmsg("Found a bounce message with no header at '%s'\n",
2374                                                 lineGetData(t_line->t_line));
2375                                         fileblobSetFilename(fb, mctx->dir, "bounce");
2376                                         fileblobAddData(fb,
2377                                                 (const unsigned char *)"Received: by clamd (bounce)\n",
2378                                                 28);
2379 
2380                                         fileblobSetCTX(fb, mctx->ctx);
2381                                         if(fileblobScanAndDestroy(textToFileblob(t_line, fb, 1)) == CL_VIRUS)
2382                                                 rc = VIRUS;
2383                                         mctx->files++;
2384                                 }
2385                                 saveIt = FALSE;
```

The line `2364     else if((t_line = encodingLine(mainMessage)) != NULL) {` return the line where the encoding is defined. In the PoC the line where the encoding is defined is the line with the body that was freed previously.

This is not exploitable because the freed type `t_line *` is an structure that does not contain any function pointer that could be overwrite to control the execution. The program crashes after the UAF when the region is double freed `free(): invalid pointer: 0x000000000085d7f0`.

A solution is to add a condition in mbox.c to validate that body is not NULL before looking for hiden viruses:
Original:
```c
2364                         else if((t_line = encodingLine(mainMessage)) != NULL) {
```
Modification:
```c
2364                         else if(mainMessage->body_last != NULL &&  (t_line = encodingLine(mainMessage)) != NULL) {
```



This vulnerability has been found by Offensive Research at Salesforce.com:
Alberto Garcia (@algillera), Francisco Oca (@francisco_oca) & Suleman Ali (@Salbei_)
Comment 1 Joel Esler 2017-10-27 09:51:08 EDT
Thank you very much for reporting these issues.  If possible, we'd like to gather some initial first details from you on these.  

First, can you tell us what version of ClamAV you are reporting against?  I notice that you have our git-repository name in the reports, is this a fresh check out of git?
Comment 2 Micah Snyder 2017-10-29 17:39:10 EDT
Adding recommended fix along with a couple extra lines to set pointers to NULL after freeing them. 

Commit: 7cf2a701041b775dda9743d01665279facc9b326
Comment 4 Jhon Merced 2018-04-18 06:04:04 EDT
Thank you for response and fix. Seems to work now.

Jhon
https://amsterdamdiary.com/