qmail remote root patch

Anton Alin-Adrian aanton at reversedhell.net
Mon Jan 19 05:27:40 PST 2004


Anton Alin-Adrian wrote:

> Anton Alin-Adrian wrote:
>
>> Regarding latest qmail vulnerability, I coded this quickly patch. 
>> Please double-check me if I am wrong here. Forward this to 
>> freebsd-security please.
>>
>>
>> Regards,
>> Alin.
>>
>> ------------------------------------------------------------------------
>>
>> 320c320
>> <       ++pos;
>> ---
>>  
>>
>>>      if (pos>9) ++pos;
>>>   
>>> ------------------------------------------------------------------------ 
>>>
>>>
>>> _______________________________________________
>>> freebsd-hackers at freebsd.org mailing list
>>> http://lists.freebsd.org/mailman/listinfo/freebsd-hackers
>>> To unsubscribe, send any mail to 
>>> "freebsd-hackers-unsubscribe at freebsd.org"
>>>   
>>
> I forgot to mention about vuln:
>
> http://www.guninski.com/qmailcrash.html
>

Actually that was utterly wrong. I think this works:
bash-2.05b$ diff -a qmail-smtpd.c qmail-smtpd-patched.c
318a319
 >       ++pos;
320d320
<       ++pos;


The patched function will look like:
void blast(hops)
int *hops;
{
  char ch;
  int state;
  int flaginheader;
  int pos; /* number of bytes since most recent \n, if fih */
  int flagmaybex; /* 1 if this line might match RECEIVED, if fih */
  int flagmaybey; /* 1 if this line might match \r\n, if fih */
  int flagmaybez; /* 1 if this line might match DELIVERED, if fih */
 
  state = 1;
  *hops = 0;
  flaginheader = 1;
  pos = 0; flagmaybex = flagmaybey = flagmaybez = 1;
  for (;;) {
    substdio_get(&ssin,&ch,1);
    if (flaginheader) {
      if (pos < 9) {
        if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) 
flagmaybez = 0;
        if (flagmaybez) if (pos == 8) ++*hops;
        if (pos < 8)
          if (ch != "received"[pos]) if (ch != "RECEIVED"[pos]) 
flagmaybex = 0;
        if (flagmaybex) if (pos == 7) ++*hops;
        if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0;
        if (flagmaybey) if (pos == 1) flaginheader = 0;
    ++pos;
      }
      if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; }
    }
    switch(state) {
      case 0:
        if (ch == '\n') straynewline();
        if (ch == '\r') { state = 4; continue; }
        break;
      case 1: /* \r\n */
        if (ch == '\n') straynewline();
        if (ch == '.') { state = 2; continue; }
        if (ch == '\r') { state = 4; continue; }
        state = 0;
        break;
      case 2: /* \r\n + . */
        if (ch == '\n') straynewline();
        if (ch == '\r') { state = 3; continue; }
        state = 0;
        break;
      case 3: /* \r\n + .\r */
        if (ch == '\n') return;
        put(".");
        put("\r");
        if (ch == '\r') { state = 4; continue; }
        state = 0;
        break;
      case 4: /* + \r */
        if (ch == '\n') { state = 1; break; }
        if (ch != '\r') { put("\r"); state = 0; }
    }
    put(&ch);
  }
}


So what I did is move ++pos; into the if (pos < 9) block. Originally it 
is right after the } ending that block.

This works if pos gets incremented as 
pos=1,2,.....9,10,...,max,...,upper-overflow(negative).

This utterly fails if pos is not incremented like that.

Any ideas? I think it works, after a first look at the incrementation loop.

Sorry for all other mails, I am stressed . (need to calm down i know)

Alin.




More information about the freebsd-hackers mailing list