Home / vulnerabilities mtr-overflow.txt
Posted on 20 May 2008
Source : packetstormsecurity.org Link
Name: Mtr - network diagnostic tool.
Author: Adam Zabrocki <pi3@itsec.pl> or <pi3ki31ny@gmail.com>
Date: February 28, 2008
Issue:
Mtr allows local and remote attackers to overflow buffer on stack.
Description:
Mtr combines the functionality of the traceroute and ping programs in a single
network diagnostic tool. For more detail please read manual page.
Details:
It is possible to overflow buffor on stack in suid program - mtr. Remote attack
is possible too. Bug is in function which print result of runing program with parametr
'split' (-p). Victim must use DNS which we can control or we can try exploit this
vulnerability by spoofing technique. In remote exploiting this vulnerability we must
know which IP user gave to program - or he must simply run program and argument
must be IP adres which we can controle in DNS server.
Look for this code:
"split.c"
#define MAX_LINE_SIZE 256
void split_redraw(void)
{
int max;
int at;
ip_t *addr;
char *name;
char newLine[MAX_LINE_SIZE];
int i;
...
for(at = 0; at < max; at++) {
addr = net_addr(at);
if( addrcmp( (void *) addr, (void *) &unspec_addr, af ) != 0 ) {
name = dns_lookup(addr); [1]
if(name != NULL) {
/* May be we should test name's length */ [!!]
sprintf(newLine, "%s %d %d %d %d %d %d", name, [2]
net_loss(at),
net_returned(at), net_xmit(at),
net_best(at) /1000, net_avg(at)/1000,
net_worst(at)/1000);
} else {
...
sprintf(newLine, "???");
}
...
...
}
}
As we can see in [2] there is unsecure call for function sprintf().
Argument 'name' is RevDNS for IP address. In details exploiting this
situaction will be later becouse normal we can't do that!
Now let's look what call this function:
"display.c"
void display_redraw(void)
{
switch(DisplayMode) {
...
case DisplaySplit: /* BL */
split_redraw();
break;
...
}
}
Call for function display_redraw() is here:
"select.c"
void select_loop(void) {
...
...
while(1) {
...
...
do {
if(anyset || paused) {
...
} else {
if(Interactive) display_redraw();
...
...
}
} while ((rv < 0) && (errno == EINTR));
...
...
}
return;
}
And call for this functio is here:
"display.c"
void display_loop(void)
{
switch(DisplayMode) {
case DisplayReport:
case DisplayTXT:
case DisplayXML:
case DisplayCSV:
case DisplaySplit: /* BL */
case DisplayCurses:
case DisplayRaw:
select_loop();
break;
case DisplayGTK:
gtk_loop();
break;
}
}
Call for function display_loop() is in main function:
"mtr.c"
int main(int argc, char **argv)
{
...
...
display_loop();
...
return 0;
}
Value for variable 'Interactive' is here:
"mtr.c"
int Interactive = 1;
void parse_arg (int argc, char **argv)
{
...
...
while(1) {
/* added f:m:o: byMin */
opt = getopt_long(argc, argv,
"vhrxtglpo:i:c:s:b:Q:na:f:m:46", long_options, NULL);
if(opt == -1)
break;
switch(opt) {
...
case 'p': /* BL */
DisplayMode = DisplaySplit;
break;
...
}
if (DisplayMode == DisplayReport ||
DisplayMode == DisplayTXT ||
DisplayMode == DisplayXML ||
DisplayMode == DisplayRaw ||
DisplayMode == DisplayCSV)
Interactive = 0;
...
}
So we must run program with option '-p' to be in function split_redraw().
Ok it is the first topic of this advisory. Let's look how to exploit it.
In place [1] we can saw this line:
name = dns_lookup(addr); [1]
so we must look how we can controle value of this variable. Look here:
"dns.c"
char *dns_lookup(ip_t * ip)
{
char *t;
if (!dns) return strlongip (ip);
t = dns_lookup2 (ip);
return (t&&use_dns)?t:strlongip(ip);
}
and function dns_lookup2() is here:
"dns.c"
char *dns_lookup2(ip_t * ip)
{
struct resolve *rp;
if ((rp = findip(ip))) {
...
<try to find this IP in local cache which mtr create>
...
}
rp = allocresolve();
rp->state = STATE_PTRREQ1;
rp->expiretime = sweeptime + ResRetryDelay1;
addrcpy( (void *) &(rp->ip), (void *) ip, af );
linkresolve(rp);
addrcpy( (void *) &(rp->ip), (void *) ip, af );
linkresolveip(rp);
sendrequest(rp,T_PTR);
return NULL;
}
funkction linkresolve/linkresolveip initialize data for new data (address/domain)
and once again try to find this in cache. So let's look for function sendrequest():
"dns.c"
void sendrequest(struct resolve *rp,int type)
{
do {
idseed = (((idseed + idseed) | (long)time(NULL)) + idseed - 0x54bad4a) ^ aseed;
aseed^= idseed;
rp->id = (word)idseed;
} while (findid(rp->id));
linkresolveid(rp);
resendrequest(rp,type);
}
linkresolveid() try to find this id in cache :) So look for resendrequest():
"dns.c"
void resendrequest(struct resolve *rp,int type)
{
if (type == T_A) {
...
...
} else if (type == T_PTR) {
switch ( af ) {
case AF_INET:
sprintf(tempstring,"%u.%u.%u.%u.in-addr.arpa",
((byte *)&rp->ip)[3],
((byte *)&rp->ip)[2],
((byte *)&rp->ip)[1],
((byte *)&rp->ip)[0]);
break;
#ifdef ENABLE_IPV6
case AF_INET6:
addr2ip6arpa( &(rp->ip), tempstring );
break;
#endif
}
dorequest(tempstring,type,rp->id);
...
...
}
}
and function dorequest() make and send quero PTR for DNS server:
"dns.c"
void dorequest(char *s,int type,word id)
{
packetheader *hp;
int r,i;
int buf[(MaxPacketsize/sizeof (int))+1];
r = res_mkquery(QUERY,s,C_IN,type,NULL,0,NULL,(unsigned char*)buf,MaxPacketsize);
if (r == -1) {
restell("Resolver error: Query too large.");
return;
}
hp = (packetheader *)buf;
hp->id = id; /* htons() deliberately left out (redundant) */
for (i = 0;i < _res.nscount;i++)
(void)sendto(resfd,buf,r,0,(struct sockaddr *)&_res.nsaddr_list[i],
sizeof(struct sockaddr));
}
... and reply from server is examined in select_loop() function:
"select.c"
void select_loop(void) {
...
...
while(1) {
...
if (dns) {
/* Handle any pending resolver events */
dnsinterval = WaitTime;
dns_events(&dnsinterval);
}
/* Have we finished a nameservice lookup? */
if(dns && FD_ISSET(dnsfd, &readfd)) {
dns_ack();
anyset = 1;
}
...
}
...
}
and function dns_ack() is here:
"dns.c"
void dns_ack(void)
{
int r,i;
r = recvfrom(resfd,(byte *)resrecvbuf,MaxPacketsize,0,
from, &fromlen);
if (r > 0) {
...
<some tests>
...
if (i == _res.nscount) {
...
} else
parserespacket((byte *)resrecvbuf,r);
} else {
...
}
}
So we must look for function parserespacket(). It's realy big
and advanced function so i paste only what we must see. Look:
"dns.c"
void parserespacket(byte *s, int l)
{
..,
packetheader *hp;
...
...
hp = (packetheader *)s;
...
<many tests for packet/header>
...
...
eob = s + l;
c = s + HFIXEDSZ;
switch (getheader_rcode(hp)) {
case NOERROR:
if (hp->ancount) {
...
<many fine test etc>
...
switch (qdatatype) {
case T_PTR:
if (!Is_PTR(rp))
if (debug) {
restell("Resolver warning: Ignoring response with unexpected query type "PTR".");
return;
}
break;
default:
sprintf(tempstring,"Resolver error: Received unimplemented query type: %u (%s)",
qdatatype,qdatatype < ResourcetypeCount ?
resourcetypes[qdatatype] : resourcetypes[ResourcetypeCount]);
restell(tempstring);
}
for (rr = hp->ancount + hp->nscount + hp->arcount;rr;rr--) {
...
<other test and code>
...
datatype = sucknetword(c);
...
if (datatype == qdatatype || datatype == T_CNAME) {
...
}
if (usefulanswer)
switch (datatype) {
...
...
case T_PTR:
case T_CNAME:
*namestring = '